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 ----
(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 )