Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 228 additions & 0 deletions src/Toolkit/kits/shadcn/field/EXAMPLES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
# Examples

## Input

```twig {"preview":true,"height":"500px"}
<div class="w-full max-w-md">
<twig:Field:Set>
<twig:Field:Group>
<twig:Field>
<twig:Field:Label for="username">Username</twig:Field:Label>
<twig:Input id="username" type="text" placeholder="Max Leiter" />
<twig:Field:Description>
Choose a unique username for your account.
</twig:Field:Description>
</twig:Field>
<twig:Field>
<twig:Field:Label for="password">Password</twig:Field:Label>
<twig:Field:Description>
Must be at least 8 characters long.
</twig:Field:Description>
<twig:Input id="password" type="password" placeholder="********" />
</twig:Field>
</twig:Field:Group>
</twig:Field:Set>
</div>
```

## Textarea

```twig {"preview":true,"height":"400px"}
<div class="w-full max-w-md">
<twig:Field:Set>
<twig:Field:Group>
<twig:Field>
<twig:Field:Label for="feedback">Feedback</twig:Field:Label>
<twig:Textarea
id="feedback"
placeholder="Your feedback helps us improve..."
rows="4"
/>
<twig:Field:Description>
Share your thoughts about our service.
</twig:Field:Description>
</twig:Field>
</twig:Field:Group>
</twig:Field:Set>
</div>
```

## Select

```twig {"preview":true,"height":"360px"}
<div class="w-full max-w-md">
<twig:Field>
<twig:Field:Label for="department">Department</twig:Field:Label>
<twig:Select id="department">
<option value="engineering">Engineering</option>
<option value="design">Design</option>
<option value="marketing">Marketing</option>
<option value="sales">Sales</option>
<option value="support">Customer Support</option>
<option value="hr">Human Resources</option>
<option value="finance">Finance</option>
<option value="operations">Operations</option>
</twig:Select>
<twig:Field:Description>
Select your department or area of work.
</twig:Field:Description>
</twig:Field>
</div>
```

## Field set

```twig {"preview":true,"height":"400px"}
<div class="w-full max-w-md space-y-6">
<twig:Field:Set>
<twig:Field:Legend>Address information</twig:Field:Legend>
<twig:Field:Description>
We need your address to deliver your order.
</twig:Field:Description>
<twig:Field:Group>
<twig:Field>
<twig:Field:Label for="street">Street address</twig:Field:Label>
<twig:Input id="street" type="text" placeholder="123 Main St" />
</twig:Field>
<div class="grid grid-cols-2 gap-4">
<twig:Field>
<twig:Field:Label for="city">City</twig:Field:Label>
<twig:Input id="city" type="text" placeholder="New York" />
</twig:Field>
<twig:Field>
<twig:Field:Label for="zip">Postal code</twig:Field:Label>
<twig:Input id="zip" type="text" placeholder="90502" />
</twig:Field>
</div>
</twig:Field:Group>
</twig:Field:Set>
</div>
```

## Checkbox

```twig {"preview":true,"height":"520px"}
<div class="w-full max-w-md">
<twig:Field:Group>
<twig:Field:Set>
<twig:Field:Legend variant="label">
Show these items on the desktop
</twig:Field:Legend>
<twig:Field:Description>
Select the items you want to show on the desktop.
</twig:Field:Description>
<twig:Field:Group class="gap-3">
<twig:Field orientation="horizontal">
<twig:Checkbox id="finder-pref-9k2-hard-disks-ljj" />
<twig:Field:Label
for="finder-pref-9k2-hard-disks-ljj"
class="font-normal"
checked
>
Hard disks
</twig:Field:Label>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:Checkbox id="finder-pref-9k2-external-disks-1yg" />
<twig:Field:Label
for="finder-pref-9k2-external-disks-1yg"
class="font-normal"
>
External disks
</twig:Field:Label>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:Checkbox id="finder-pref-9k2-cds-dvds-fzt" />
<twig:Field:Label
for="finder-pref-9k2-cds-dvds-fzt"
class="font-normal"
>
CDs, DVDs, and iPods
</twig:Field:Label>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:Checkbox id="finder-pref-9k2-connected-servers-6l2" />
<twig:Field:Label
for="finder-pref-9k2-connected-servers-6l2"
class="font-normal"
>
Connected servers
</twig:Field:Label>
</twig:Field>
</twig:Field:Group>
</twig:Field:Set>
<twig:Field:Separator />
<twig:Field orientation="horizontal">
<twig:Checkbox id="finder-pref-9k2-sync-folders-nep" checked />
<twig:Field:Content>
<twig:Field:Label for="finder-pref-9k2-sync-folders-nep">
Sync Desktop & Documents folders
</twig:Field:Label>
<twig:Field:Description>
Your Desktop & Documents folders are being synced with iCloud Drive. You can access them from other devices.
</twig:Field:Description>
</twig:Field:Content>
</twig:Field>
</twig:Field:Group>
</div>
```

## Switch

```twig {"preview":true,"height":"240px"}
<div class="w-full max-w-md">
<twig:Field orientation="horizontal">
<twig:Field:Content>
<twig:Field:Label for="2fa">Multi-factor authentication</twig:Field:Label>
<twig:Field:Description>
Enable multi-factor authentication. If you do not have a two-factor device, you can use a one-time code sent to your email.
</twig:Field:Description>
</twig:Field:Content>
<twig:Switch id="2fa" />
</twig:Field>
</div>
```

## Field group

```twig {"preview":true,"height":"520px"}
<div class="w-full max-w-md">
<twig:Field:Group>
<twig:Field:Set>
<twig:Field:Label>Responses</twig:Field:Label>
<twig:Field:Description>
Get notified when ChatGPT responds to requests that take time, like research or image generation.
</twig:Field:Description>
<twig:Field:Group data-slot="checkbox-group">
<twig:Field orientation="horizontal">
<twig:Checkbox id="push" checked disabled />
<twig:Field:Label for="push" class="font-normal">
Push notifications
</twig:Field:Label>
</twig:Field>
</twig:Field:Group>
</twig:Field:Set>
<twig:Field:Separator />
<twig:Field:Set>
<twig:Field:Label>Tasks</twig:Field:Label>
<twig:Field:Description>
Get notified when tasks you've created have updates. <a href="#">Manage tasks</a>
</twig:Field:Description>
<twig:Field:Group data-slot="checkbox-group">
<twig:Field orientation="horizontal">
<twig:Checkbox id="push-tasks" />
<twig:Field:Label for="push-tasks" class="font-normal">
Push notifications
</twig:Field:Label>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:Checkbox id="email-tasks" />
<twig:Field:Label for="email-tasks" class="font-normal">
Email notifications
</twig:Field:Label>
</twig:Field>
</twig:Field:Group>
</twig:Field:Set>
</twig:Field:Group>
</div>
```
13 changes: 13 additions & 0 deletions src/Toolkit/kits/shadcn/field/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "../../../schema-kit-recipe-v1.json",
"type": "component",
"name": "Field",
"description": "Layout helpers for form fields with labels, descriptions, errors, grouping, and separators.",
"copy-files": {
"templates/": "templates/"
},
"dependencies": {
"composer": ["twig/extra-bundle", "twig/html-extra:^3.12.0", "tales-from-a-dev/twig-tailwind-extra:^1.0.0"],
"recipe": ["label", "separator"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{# @prop orientation 'vertical'|'horizontal'|'responsive' The orientation of the field, default to `vertical` #}
{# @block content The default block #}
{%- props orientation = 'vertical' -%}
{%- set style = html_cva(
base: 'group/field flex w-full gap-3 data-[invalid=true]:text-destructive',
variants: {
orientation: {
vertical: 'flex-col [&>*]:w-full [&>.sr-only]:w-auto',
horizontal: 'flex-row items-center [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
responsive: 'flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
},
},
) -%}

<div
role="group"
data-slot="field"
data-orientation="{{ orientation }}"
class="{{ style.apply({orientation: orientation}, attributes.render('class'))|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{# @block content The default block #}
<div
data-slot="field-content"
class="{{ 'group/field-content flex flex-1 flex-col gap-1.5 leading-snug ' ~ attributes.render('class')|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{# @block content The default block #}
<p
data-slot="field-description"
class="{{ 'text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5 [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4 ' ~ attributes.render('class')|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{# @prop errors array A list of errors (string or objects with a `message` field), defaults to `[]` #}
{# @block content The default block #}
{%- props errors = [] -%}
{%- set slot_content -%}{%- block content %}{% endblock -%}{%- endset -%}
{%- set slot_content = slot_content|trim -%}

{%- if slot_content == '' -%}
{%- set messages = [] -%}
{%- for error in errors|default([]) -%}
{%- set message = error.message ?? error -%}
{%- if message is not same as(false) and message is not null and message != '' and not (message in messages) -%}
{%- set messages = messages|merge([message]) -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}

{%- if slot_content != '' or (messages ?? [])|length > 0 -%}
<div
role="alert"
data-slot="field-error"
class="{{ 'text-destructive text-sm font-normal ' ~ attributes.render('class')|tailwind_merge }}"
{{ attributes }}
>
{%- if slot_content != '' -%}
{{ slot_content }}
{%- elseif (messages ?? [])|length == 1 -%}
{{ messages[0] }}
{%- else -%}
<ul class="ml-4 flex list-disc flex-col gap-1">
{%- for message in messages|default([]) -%}
<li>{{ message }}</li>
{%- endfor -%}
</ul>
{%- endif -%}
</div>
{%- endif -%}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{# @block content The default block #}
<div
data-slot="field-group"
class="{{ 'group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4 ' ~ attributes.render('class')|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{# @block content The default block #}
<twig:Label
data-slot="field-label"
class="{{ 'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4 has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10 ' ~ attributes.render('class')|tailwind_merge }}"
{{ ...attributes }}
>
{{- block(outerBlocks.content) -}}
</twig:Label>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{# @prop variant 'legend'|'label' The variant, default to `legend` #}
{# @block content The default block #}
{%- props variant = 'legend' -%}
{%- set style = html_cva(
base: 'mb-3 font-medium',
variants: {
variant: {
legend: 'text-base',
label: 'text-sm',
},
},
) %}
<legend
data-slot="field-legend"
data-variant="{{ variant }}"
class="{{ style.apply({variant: variant}, attributes.render('class'))|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</legend>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{# @block content The default block #}
{%- set content -%}{%- block content %}{% endblock -%}{%- endset -%}
{%- set has_content = content|trim != '' -%}
<div
data-slot="field-separator"
data-content="{{ has_content ? 'true' : 'false' }}"
class="{{ 'relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2 ' ~ attributes.render('class')|tailwind_merge }}"
{{ attributes }}
>
<twig:Separator class="absolute inset-0 top-1/2" />
{%- if has_content -%}
<span
class="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
data-slot="field-separator-content"
>
{{ content }}
</span>
{%- endif -%}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{# @block content The default block #}
<fieldset
data-slot="field-set"
class="{{ 'flex flex-col gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 ' ~ attributes.render('class')|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</fieldset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{# @block content The default block #}
<div
data-slot="field-label"
class="{{ 'flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50 ' ~ attributes.render('class')|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</div>
Loading
Loading