Chandra

 view release on metacpan or  search on metacpan

examples/form_example.pl  view on Meta::CPAN

  input[type="range"] { width: 100%; }
  .chandra-field-checkbox, .chandra-radio-option {
    display: flex; align-items: center; gap: 8px;
    font-size: 14px;
  }
  .chandra-radio-option { margin: 4px 0; }
  fieldset.chandra-group {
    border: 1px solid #444; border-radius: 6px;
    padding: 12px 14px; margin: 16px 0;
  }
  fieldset.chandra-group legend {
    font-size: 14px; font-weight: 600; padding: 0 6px;
  }
  .chandra-submit {
    padding: 10px 28px; font-size: 14px; font-weight: 600;
    background: #0078d4; color: #fff; border: none;
    border-radius: 4px; cursor: pointer; margin-top: 8px;
  }
  .chandra-submit:hover { background: #106ebe; }
  .chandra-error {
    display: block; font-size: 12px; color: #f44;

lib/Chandra/Form.pm  view on Meta::CPAN


Set the submit button label. Defaults to C<"Submit">.

=head2 group

    $form->group('Appearance' => sub {
        $form->select('theme', { ... });
        $form->number('font_size', { ... });
    });

Wrap fields in a C<< <fieldset> >> with a C<< <legend> >>.

=head2 render

    my $html = $form->render;

Render the form to an HTML string. Each field is wrapped in a
C<< <div class="chandra-field"> >> with a label and error placeholder.

=head2 bind_js

lib/Chandra/Theme.pm  view on Meta::CPAN

    background: var(--chandra-primary);
    color: #fff;
    border: none;
    border-radius: var(--chandra-radius);
    cursor: pointer;
    font-weight: 500;
}
.chandra-submit:hover { opacity: 0.9; }
.chandra-error { color: var(--chandra-danger); font-size: 0.85em; }
.chandra-group { border: 1px solid var(--chandra-border); border-radius: var(--chandra-radius); padding: 12px; }
.chandra-group legend { font-weight: 600; padding: 0 6px; }

/* ── Table (Chandra::Table) ───────────────────────────── */

.chandra-table-wrap { font-family: inherit; }
.chandra-table { width: 100%; border-collapse: collapse; }
.chandra-table th, .chandra-table td {
    padding: 8px 12px;
    text-align: left;
    border-bottom: 1px solid var(--chandra-border);
}

t/59_form.t  view on Meta::CPAN

    $f->group('Appearance' => sub {
        $f->select('theme', {
            label => 'Theme',
            options => [{ value => 'dark', label => 'Dark' }],
        });
        $f->number('font', { label => 'Font Size' });
    });
    $f->text('other', { label => 'Other' });
    my $html = $f->render;
    like($html, qr/<fieldset class="chandra-group">/, 'fieldset rendered');
    like($html, qr/<legend>Appearance<\/legend>/, 'legend rendered');
    like($html, qr/<\/fieldset>/, 'fieldset closed');
}

# ---- bind_js() ----
{
    my $f = Chandra::Form->new(id => 'js-test');
    my $js = $f->bind_js;
    like($js, qr/js-test/, 'JS contains form id');
    like($js, qr/addEventListener/, 'JS has event listener');
    like($js, qr/submit/, 'JS handles submit');

t/60_form_edge.t  view on Meta::CPAN

    my $f = Chandra::Form->new(id => 'mg');
    $f->group('Group A' => sub {
        $f->text('a1', {});
    });
    $f->group('Group B' => sub {
        $f->text('b1', {});
    });
    my $html = $f->render;
    my @fieldsets = ($html =~ /<fieldset/g);
    is(scalar @fieldsets, 2, 'two fieldsets');
    my @legends = ($html =~ /<legend>(.*?)<\/legend>/g);
    is_deeply(\@legends, ['Group A', 'Group B'], 'legend text');
}

# ---- group() rejects non-coderef ----
{
    my $f = Chandra::Form->new(id => 'eg');
    eval { $f->group('Bad', 'not a sub') };
    like($@, qr/coderef/, 'group rejects non-coderef');
}

# ---- on_change() bad args ----

xs/form.xs  view on Meta::CPAN

                              (int)form_id_len, form_id,
                              (int)name_len, name);

        /* Handle group transitions */
        grp_svp = hv_fetchs(fhv, "group", 0);
        if (grp_svp && SvOK(*grp_svp)) {
            const char *grp = SvPV_nolen(*grp_svp);
            if (!cur_group || !strEQ(cur_group, grp)) {
                if (in_fieldset)
                    sv_catpvs(html, "</fieldset>");
                sv_catpvs(html, "<fieldset class=\"chandra-group\"><legend>");
                {
                    STRLEN glen;
                    const char *g = SvPV(*grp_svp, glen);
                    SV *esc = _elem_escape_html(aTHX_ g, glen);
                    sv_catsv(html, esc);
                    SvREFCNT_dec(esc);
                }
                sv_catpvs(html, "</legend>");
                cur_group = SvPV_nolen(*grp_svp);
                in_fieldset = 1;
            }
        } else {
            if (in_fieldset) {
                sv_catpvs(html, "</fieldset>");
                in_fieldset = 0;
                cur_group = NULL;
            }
        }



( run in 2.085 seconds using v1.01-cache-2.11-cpan-2398b32b56e )