twintrinsic 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +150 -0
- package/dist/App/App.svelte +54 -0
- package/dist/App/App.svelte.d.ts +65 -0
- package/dist/Section.svelte +25 -0
- package/dist/Section.svelte.d.ts +34 -0
- package/dist/actions/clickOutside.d.ts +9 -0
- package/dist/actions/clickOutside.js +19 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/components/Accordion/Accordion.svelte +75 -0
- package/dist/components/Accordion/Accordion.svelte.d.ts +39 -0
- package/dist/components/Accordion/AccordionItem.svelte +150 -0
- package/dist/components/Accordion/AccordionItem.svelte.d.ts +30 -0
- package/dist/components/App/App.story.md +8 -0
- package/dist/components/App/App.story.svelte +170 -0
- package/dist/components/App/App.story.svelte.d.ts +22 -0
- package/dist/components/App/App.svelte +77 -0
- package/dist/components/App/App.svelte.d.ts +66 -0
- package/dist/components/App/Split.svelte +346 -0
- package/dist/components/App/Split.svelte.d.ts +54 -0
- package/dist/components/App/index.d.ts +2 -0
- package/dist/components/App/index.js +3 -0
- package/dist/components/AppHeader/AppHeader.svelte +439 -0
- package/dist/components/AppHeader/AppHeader.svelte.d.ts +24 -0
- package/dist/components/Avatar/Avatar.svelte +300 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +48 -0
- package/dist/components/Avatar/AvatarGroup.svelte +185 -0
- package/dist/components/Avatar/AvatarGroup.svelte.d.ts +46 -0
- package/dist/components/Badge/Badge.svelte +186 -0
- package/dist/components/Badge/Badge.svelte.d.ts +51 -0
- package/dist/components/BottomBar/BottomBar.svelte +146 -0
- package/dist/components/BottomBar/BottomBar.svelte.d.ts +38 -0
- package/dist/components/Breadcrumb/Breadcrumb.svelte +77 -0
- package/dist/components/Breadcrumb/Breadcrumb.svelte.d.ts +42 -0
- package/dist/components/Breadcrumb/BreadcrumbItem.svelte +171 -0
- package/dist/components/Breadcrumb/BreadcrumbItem.svelte.d.ts +38 -0
- package/dist/components/Button/Button.svelte +252 -0
- package/dist/components/Button/Button.svelte.d.ts +80 -0
- package/dist/components/Button/ButtonGroup.svelte +127 -0
- package/dist/components/Button/ButtonGroup.svelte.d.ts +44 -0
- package/dist/components/Card/Card.svelte +152 -0
- package/dist/components/Card/Card.svelte.d.ts +55 -0
- package/dist/components/Carousel/Carousel.svelte +461 -0
- package/dist/components/Carousel/Carousel.svelte.d.ts +79 -0
- package/dist/components/Carousel/CarouselItem.svelte +149 -0
- package/dist/components/Carousel/CarouselItem.svelte.d.ts +35 -0
- package/dist/components/Chip/Chip.svelte +288 -0
- package/dist/components/Chip/Chip.svelte.d.ts +71 -0
- package/dist/components/Chip/ChipGroup.svelte +190 -0
- package/dist/components/Chip/ChipGroup.svelte.d.ts +71 -0
- package/dist/components/CodeBlock/CodeBlock.svelte +356 -0
- package/dist/components/CodeBlock/CodeBlock.svelte.d.ts +44 -0
- package/dist/components/CodeBlock/index.d.ts +1 -0
- package/dist/components/CodeBlock/index.js +1 -0
- package/dist/components/CodeBlockSpeed/CodeBlockSpeed.svelte +145 -0
- package/dist/components/CodeBlockSpeed/CodeBlockSpeed.svelte.d.ts +44 -0
- package/dist/components/CodeEditor/CodeEditor.svelte +229 -0
- package/dist/components/CodeEditor/CodeEditor.svelte.d.ts +23 -0
- package/dist/components/Combobox/Combobox.svelte +279 -0
- package/dist/components/Combobox/Combobox.svelte.d.ts +34 -0
- package/dist/components/Container/Container.svelte +45 -0
- package/dist/components/Container/Container.svelte.d.ts +36 -0
- package/dist/components/DataTable/DataTable.svelte +879 -0
- package/dist/components/DataTable/DataTable.svelte.d.ts +102 -0
- package/dist/components/Form/AutoComplete.svelte +357 -0
- package/dist/components/Form/AutoComplete.svelte.d.ts +73 -0
- package/dist/components/Form/Calendar.svelte +429 -0
- package/dist/components/Form/Calendar.svelte.d.ts +53 -0
- package/dist/components/Form/Checkbox.svelte +196 -0
- package/dist/components/Form/Checkbox.svelte.d.ts +50 -0
- package/dist/components/Form/ColorPicker.svelte +396 -0
- package/dist/components/Form/ColorPicker.svelte.d.ts +43 -0
- package/dist/components/Form/Combobox.svelte +645 -0
- package/dist/components/Form/Combobox.svelte.d.ts +93 -0
- package/dist/components/Form/Dropdown.svelte +773 -0
- package/dist/components/Form/Dropdown.svelte.d.ts +81 -0
- package/dist/components/Form/FileUpload.svelte +796 -0
- package/dist/components/Form/FileUpload.svelte.d.ts +78 -0
- package/dist/components/Form/FloatLabel.svelte +245 -0
- package/dist/components/Form/FloatLabel.svelte.d.ts +44 -0
- package/dist/components/Form/Form.svelte +281 -0
- package/dist/components/Form/Form.svelte.d.ts +54 -0
- package/dist/components/Form/FormField.svelte +218 -0
- package/dist/components/Form/FormField.svelte.d.ts +47 -0
- package/dist/components/Form/Input.svelte +340 -0
- package/dist/components/Form/Input.svelte.d.ts +79 -0
- package/dist/components/Form/InputSwitch.svelte +189 -0
- package/dist/components/Form/InputSwitch.svelte.d.ts +46 -0
- package/dist/components/Form/InvalidState.svelte +97 -0
- package/dist/components/Form/InvalidState.svelte.d.ts +37 -0
- package/dist/components/Form/Knob.svelte +537 -0
- package/dist/components/Form/Knob.svelte.d.ts +78 -0
- package/dist/components/Form/ListInput.svelte +469 -0
- package/dist/components/Form/ListInput.svelte.d.ts +70 -0
- package/dist/components/Form/Listbox.svelte +513 -0
- package/dist/components/Form/Listbox.svelte.d.ts +74 -0
- package/dist/components/Form/NumberInput.svelte +452 -0
- package/dist/components/Form/NumberInput.svelte.d.ts +82 -0
- package/dist/components/Form/Radio.svelte +192 -0
- package/dist/components/Form/Radio.svelte.d.ts +53 -0
- package/dist/components/Form/RadioGroup.svelte +155 -0
- package/dist/components/Form/RadioGroup.svelte.d.ts +48 -0
- package/dist/components/Form/Rating.svelte +380 -0
- package/dist/components/Form/Rating.svelte.d.ts +64 -0
- package/dist/components/Form/Select.svelte +436 -0
- package/dist/components/Form/Select.svelte.d.ts +49 -0
- package/dist/components/Form/SelectGroup.svelte +34 -0
- package/dist/components/Form/SelectGroup.svelte.d.ts +33 -0
- package/dist/components/Form/Slider.svelte +622 -0
- package/dist/components/Form/Slider.svelte.d.ts +73 -0
- package/dist/components/Form/Switch.svelte +192 -0
- package/dist/components/Form/Switch.svelte.d.ts +46 -0
- package/dist/components/Form/TextInput.svelte +274 -0
- package/dist/components/Form/TextInput.svelte.d.ts +74 -0
- package/dist/components/Form/Textarea.svelte +207 -0
- package/dist/components/Form/Textarea.svelte.d.ts +62 -0
- package/dist/components/Icon/Icon.svelte +140 -0
- package/dist/components/Icon/Icon.svelte.d.ts +25 -0
- package/dist/components/Icon/index.d.ts +1 -0
- package/dist/components/Icon/index.js +1 -0
- package/dist/components/Lazy/Lazy.svelte +158 -0
- package/dist/components/Lazy/Lazy.svelte.d.ts +42 -0
- package/dist/components/Masonry/Masonry.svelte +299 -0
- package/dist/components/Masonry/Masonry.svelte.d.ts +55 -0
- package/dist/components/Menu/Menu/Menu.svelte +65 -0
- package/dist/components/Menu/Menu/Menu.svelte.d.ts +17 -0
- package/dist/components/Menu/Menu/MenuItem.svelte +90 -0
- package/dist/components/Menu/Menu/MenuItem.svelte.d.ts +27 -0
- package/dist/components/Modal/Modal.svelte +334 -0
- package/dist/components/Modal/Modal.svelte.d.ts +55 -0
- package/dist/components/Panel/Card.svelte +141 -0
- package/dist/components/Panel/Card.svelte.d.ts +52 -0
- package/dist/components/Panel/Hero/Hero.story.md +9 -0
- package/dist/components/Panel/Hero/Hero.story.svelte +49 -0
- package/dist/components/Panel/Hero/Hero.story.svelte.d.ts +21 -0
- package/dist/components/Panel/Hero/Hero.svelte +24 -0
- package/dist/components/Panel/Hero/Hero.svelte.d.ts +32 -0
- package/dist/components/Panel/LazyPanel.svelte +110 -0
- package/dist/components/Panel/LazyPanel.svelte.d.ts +46 -0
- package/dist/components/Panel/Panel.svelte +205 -0
- package/dist/components/Panel/Panel.svelte.d.ts +23 -0
- package/dist/components/Progress/Progress.svelte +220 -0
- package/dist/components/Progress/Progress.svelte.d.ts +61 -0
- package/dist/components/Separator/Separator.svelte +109 -0
- package/dist/components/Separator/Separator.svelte.d.ts +35 -0
- package/dist/components/Sidebar/Sidebar.svelte +213 -0
- package/dist/components/Sidebar/Sidebar.svelte.d.ts +60 -0
- package/dist/components/Skeleton/Skeleton.svelte +170 -0
- package/dist/components/Skeleton/Skeleton.svelte.d.ts +48 -0
- package/dist/components/Stepper/Stepper.svelte +111 -0
- package/dist/components/Stepper/Stepper.svelte.d.ts +54 -0
- package/dist/components/Stepper/StepperStep.svelte +369 -0
- package/dist/components/Stepper/StepperStep.svelte.d.ts +63 -0
- package/dist/components/Table/Table.svelte +167 -0
- package/dist/components/Table/Table.svelte.d.ts +56 -0
- package/dist/components/Table/TableBody.svelte +41 -0
- package/dist/components/Table/TableBody.svelte.d.ts +33 -0
- package/dist/components/Table/TableCell.svelte +76 -0
- package/dist/components/Table/TableCell.svelte.d.ts +36 -0
- package/dist/components/Table/TableHead.svelte +41 -0
- package/dist/components/Table/TableHead.svelte.d.ts +32 -0
- package/dist/components/Table/TableHeader.svelte +148 -0
- package/dist/components/Table/TableHeader.svelte.d.ts +42 -0
- package/dist/components/Table/TableRow.svelte +99 -0
- package/dist/components/Table/TableRow.svelte.d.ts +40 -0
- package/dist/components/Tabs/Tab.svelte +145 -0
- package/dist/components/Tabs/Tab.svelte.d.ts +36 -0
- package/dist/components/Tabs/TabList.svelte +60 -0
- package/dist/components/Tabs/TabList.svelte.d.ts +32 -0
- package/dist/components/Tabs/TabPanel.svelte +118 -0
- package/dist/components/Tabs/TabPanel.svelte.d.ts +38 -0
- package/dist/components/Tabs/Tabs.svelte +287 -0
- package/dist/components/Tabs/Tabs.svelte.d.ts +50 -0
- package/dist/components/Tag/Tag.svelte +260 -0
- package/dist/components/Tag/Tag.svelte.d.ts +54 -0
- package/dist/components/Tag/TagGroup.svelte +147 -0
- package/dist/components/Tag/TagGroup.svelte.d.ts +62 -0
- package/dist/components/ThemeToggle/ThemeToggle.svelte +93 -0
- package/dist/components/ThemeToggle/ThemeToggle.svelte.d.ts +12 -0
- package/dist/components/Timeline/Timeline.svelte +144 -0
- package/dist/components/Timeline/Timeline.svelte.d.ts +48 -0
- package/dist/components/Timeline/TimelineItem.svelte +391 -0
- package/dist/components/Timeline/TimelineItem.svelte.d.ts +63 -0
- package/dist/components/Toast/Toast.svelte +313 -0
- package/dist/components/Toast/Toast.svelte.d.ts +44 -0
- package/dist/components/Toast/toastStore.d.ts +40 -0
- package/dist/components/Toast/toastStore.js +293 -0
- package/dist/components/Tooltip/Tooltip.svelte +282 -0
- package/dist/components/Tooltip/Tooltip.svelte.d.ts +55 -0
- package/dist/components/Tree/Tree.svelte +129 -0
- package/dist/components/Tree/Tree.svelte.d.ts +61 -0
- package/dist/components/Tree/TreeNode.svelte +332 -0
- package/dist/components/Tree/TreeNode.svelte.d.ts +55 -0
- package/dist/components/icons/TwintrinsicLogo.svelte +73 -0
- package/dist/components/icons/TwintrinsicLogo.svelte.d.ts +17 -0
- package/dist/components/icons/twintrinsic-source.svg +73 -0
- package/dist/components/icons/twintrinsic.svg +38 -0
- package/dist/docs/EventsTable.svelte +86 -0
- package/dist/docs/EventsTable.svelte.d.ts +27 -0
- package/dist/docs/PropsTable.svelte +103 -0
- package/dist/docs/PropsTable.svelte.d.ts +28 -0
- package/dist/docs/index.d.ts +2 -0
- package/dist/docs/index.js +2 -0
- package/dist/helpers/detectLanguage.d.ts +6 -0
- package/dist/helpers/detectLanguage.js +60 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +94 -0
- package/dist/twintrinsic.css +347 -0
- package/package.json +98 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
FormField - A component for creating form field containers with labels, validation, and help text.
|
|
4
|
+
Provides consistent styling and accessibility features for form inputs.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<FormField
|
|
9
|
+
label="Username"
|
|
10
|
+
name="username"
|
|
11
|
+
required
|
|
12
|
+
helpText="Enter your username"
|
|
13
|
+
>
|
|
14
|
+
<TextInput />
|
|
15
|
+
</FormField>
|
|
16
|
+
```
|
|
17
|
+
-->
|
|
18
|
+
<script>
|
|
19
|
+
import { getContext } from "svelte"
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
/** @type {string} - Additional CSS classes */
|
|
23
|
+
class: className = "",
|
|
24
|
+
|
|
25
|
+
/** @type {string} - HTML id for accessibility */
|
|
26
|
+
id = crypto.randomUUID(),
|
|
27
|
+
|
|
28
|
+
/** @type {string} - Field name (used for form data) */
|
|
29
|
+
name,
|
|
30
|
+
|
|
31
|
+
/** @type {string} - Field label */
|
|
32
|
+
label,
|
|
33
|
+
|
|
34
|
+
/** @type {string} - Help text displayed below the field */
|
|
35
|
+
helpText,
|
|
36
|
+
|
|
37
|
+
/** @type {string} - Error message to display */
|
|
38
|
+
error,
|
|
39
|
+
|
|
40
|
+
/** @type {boolean} - Whether the field is required */
|
|
41
|
+
required = false,
|
|
42
|
+
|
|
43
|
+
/** @type {boolean} - Whether the field is disabled */
|
|
44
|
+
disabled = false,
|
|
45
|
+
|
|
46
|
+
/** @type {boolean} - Whether to hide the label visually (still accessible to screen readers) */
|
|
47
|
+
hideLabel = false,
|
|
48
|
+
|
|
49
|
+
/** @type {string} - Layout direction (vertical or horizontal) */
|
|
50
|
+
layout,
|
|
51
|
+
|
|
52
|
+
children,
|
|
53
|
+
} = $props()
|
|
54
|
+
|
|
55
|
+
// Get form context if available
|
|
56
|
+
const formContext = getContext("form")
|
|
57
|
+
|
|
58
|
+
// Use layout from form context if not specified
|
|
59
|
+
const fieldLayout = $derived(layout || (formContext ? formContext.layout : "vertical"))
|
|
60
|
+
|
|
61
|
+
// Generate unique ID for the field
|
|
62
|
+
const fieldId = $derived(`${id}-field`)
|
|
63
|
+
const errorId = $derived(`${id}-error`)
|
|
64
|
+
const helpId = $derived(`${id}-help`)
|
|
65
|
+
|
|
66
|
+
// Track field state
|
|
67
|
+
let fieldError = $state("")
|
|
68
|
+
let touched = $state(false)
|
|
69
|
+
let fieldDisabled = $state(false)
|
|
70
|
+
let fieldApi = $state()
|
|
71
|
+
|
|
72
|
+
// Register with form if available
|
|
73
|
+
$effect(() => {
|
|
74
|
+
if (formContext && name) {
|
|
75
|
+
fieldApi = formContext.registerField(name)
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Update field error when form validation runs
|
|
80
|
+
$effect(() => {
|
|
81
|
+
if (fieldApi) {
|
|
82
|
+
const formError = fieldApi.getError()
|
|
83
|
+
if (formError) {
|
|
84
|
+
fieldError = formError
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
fieldError = error || ""
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Update touched state
|
|
92
|
+
$effect(() => {
|
|
93
|
+
if (fieldApi) {
|
|
94
|
+
touched = fieldApi.isTouched()
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// Update disabled state
|
|
99
|
+
$effect(() => {
|
|
100
|
+
if (fieldApi) {
|
|
101
|
+
const formDisabled = formContext.disabled()
|
|
102
|
+
fieldDisabled = disabled || formDisabled
|
|
103
|
+
} else {
|
|
104
|
+
fieldDisabled = disabled
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
// Determine if we should show an error
|
|
109
|
+
const showError = $derived(!!fieldError && touched)
|
|
110
|
+
|
|
111
|
+
// Determine the aria-describedby attribute value
|
|
112
|
+
const describedBy = $derived(
|
|
113
|
+
[helpText ? helpId : null, showError ? errorId : null].filter(Boolean).join(" ")
|
|
114
|
+
)
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
<div
|
|
118
|
+
class="
|
|
119
|
+
form-field
|
|
120
|
+
{fieldLayout === 'horizontal' ? 'form-field-horizontal' : 'form-field-vertical'}
|
|
121
|
+
{showError ? 'has-error' : ''}
|
|
122
|
+
{className}
|
|
123
|
+
"
|
|
124
|
+
>
|
|
125
|
+
{#if label}
|
|
126
|
+
<label
|
|
127
|
+
for={fieldId}
|
|
128
|
+
class="form-label {hideLabel ? 'sr-only' : ''}"
|
|
129
|
+
>
|
|
130
|
+
{label}
|
|
131
|
+
{#if required}
|
|
132
|
+
<span class="required-indicator" aria-hidden="true">*</span>
|
|
133
|
+
<span class="sr-only">required</span>
|
|
134
|
+
{/if}
|
|
135
|
+
</label>
|
|
136
|
+
{/if}
|
|
137
|
+
|
|
138
|
+
<div class="form-control-container">
|
|
139
|
+
<div class="form-control">
|
|
140
|
+
<!-- Slot for the actual form control -->
|
|
141
|
+
{@render children({
|
|
142
|
+
name: fieldName,
|
|
143
|
+
id: fieldId,
|
|
144
|
+
disabled: fieldDisabled,
|
|
145
|
+
'aria-invalid': showError ? 'true' : undefined,
|
|
146
|
+
'aria-describedby': describedBy || undefined,
|
|
147
|
+
'aria-required': required ? 'true' : undefined,
|
|
148
|
+
required
|
|
149
|
+
})}
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
{#if helpText && !showError}
|
|
153
|
+
<div class="form-help-text" id={helpId}>
|
|
154
|
+
{helpText}
|
|
155
|
+
</div>
|
|
156
|
+
{/if}
|
|
157
|
+
|
|
158
|
+
{#if showError}
|
|
159
|
+
<div class="form-error" id={errorId} role="alert">
|
|
160
|
+
{fieldError}
|
|
161
|
+
</div>
|
|
162
|
+
{/if}
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<style>
|
|
167
|
+
@reference "../../twintrinsic.css";
|
|
168
|
+
|
|
169
|
+
.form-field {
|
|
170
|
+
@apply w-full;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.form-field-vertical {
|
|
174
|
+
@apply flex flex-col gap-1.5;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.form-field-horizontal {
|
|
178
|
+
@apply grid grid-cols-12 gap-4 items-start;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.form-field-horizontal .form-label {
|
|
182
|
+
@apply col-span-3 pt-2 text-right;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.form-field-horizontal .form-control-container {
|
|
186
|
+
@apply col-span-9;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.form-label {
|
|
190
|
+
@apply block text-sm font-medium text-text dark:text-text;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.required-indicator {
|
|
194
|
+
@apply ml-1 text-error-500 dark:text-error-400;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.form-control-container {
|
|
198
|
+
@apply w-full;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.form-control {
|
|
202
|
+
@apply w-full;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.form-help-text {
|
|
206
|
+
@apply mt-1 text-xs text-muted dark:text-muted;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.form-error {
|
|
210
|
+
@apply mt-1 text-xs text-error-600 dark:text-error-400;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.has-error :global(input),
|
|
214
|
+
.has-error :global(select),
|
|
215
|
+
.has-error :global(textarea) {
|
|
216
|
+
@apply border-error-500 dark:border-error-400 focus:ring-error-500 dark:focus:ring-error-400 focus:border-error-500 dark:focus:border-error-400;
|
|
217
|
+
}
|
|
218
|
+
</style>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export default FormField;
|
|
2
|
+
type FormField = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* FormField - A component for creating form field containers with labels, validation, and help text.
|
|
8
|
+
* Provides consistent styling and accessibility features for form inputs.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <FormField
|
|
13
|
+
* label="Username"
|
|
14
|
+
* name="username"
|
|
15
|
+
* required
|
|
16
|
+
* helpText="Enter your username"
|
|
17
|
+
* >
|
|
18
|
+
* <TextInput />
|
|
19
|
+
* </FormField>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare const FormField: import("svelte").Component<{
|
|
23
|
+
class?: string;
|
|
24
|
+
id?: any;
|
|
25
|
+
name: any;
|
|
26
|
+
label: any;
|
|
27
|
+
helpText: any;
|
|
28
|
+
error: any;
|
|
29
|
+
required?: boolean;
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
hideLabel?: boolean;
|
|
32
|
+
layout: any;
|
|
33
|
+
children: any;
|
|
34
|
+
}, {}, "">;
|
|
35
|
+
type $$ComponentProps = {
|
|
36
|
+
class?: string;
|
|
37
|
+
id?: any;
|
|
38
|
+
name: any;
|
|
39
|
+
label: any;
|
|
40
|
+
helpText: any;
|
|
41
|
+
error: any;
|
|
42
|
+
required?: boolean;
|
|
43
|
+
disabled?: boolean;
|
|
44
|
+
hideLabel?: boolean;
|
|
45
|
+
layout: any;
|
|
46
|
+
children: any;
|
|
47
|
+
};
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Input - Base component for form inputs with validation, floating labels, and input groups.
|
|
4
|
+
Built with accessibility and customization in mind.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<Input label="Username" />
|
|
9
|
+
|
|
10
|
+
<Input
|
|
11
|
+
label="Email"
|
|
12
|
+
type="email"
|
|
13
|
+
value="user@example.com"
|
|
14
|
+
floating={true}
|
|
15
|
+
required
|
|
16
|
+
error="Please enter a valid email"
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<Input
|
|
20
|
+
label="Password"
|
|
21
|
+
type="password"
|
|
22
|
+
leftIcon="lock"
|
|
23
|
+
rightIcon="eye"
|
|
24
|
+
onrightIconClick={togglePassword}
|
|
25
|
+
/>
|
|
26
|
+
```
|
|
27
|
+
-->
|
|
28
|
+
<script>
|
|
29
|
+
import { slide } from "svelte/transition"
|
|
30
|
+
import Icon from "../Icon/Icon.svelte"
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
/** @type {string} - Input label text */
|
|
34
|
+
label,
|
|
35
|
+
/** @type {string} - Input type (text, email, password, etc.) */
|
|
36
|
+
type = "text",
|
|
37
|
+
/** @type {string} - Input value */
|
|
38
|
+
value = "",
|
|
39
|
+
/** @type {string} - Placeholder text */
|
|
40
|
+
placeholder = "",
|
|
41
|
+
/** @type {string} - Name attribute */
|
|
42
|
+
name = "",
|
|
43
|
+
/** @type {string} - Id attribute */
|
|
44
|
+
id = crypto.randomUUID(),
|
|
45
|
+
/** @type {boolean} - Whether the input is disabled */
|
|
46
|
+
disabled = false,
|
|
47
|
+
/** @type {boolean} - Whether the input is required */
|
|
48
|
+
required = false,
|
|
49
|
+
/** @type {boolean} - Whether to use floating labels */
|
|
50
|
+
floating = false,
|
|
51
|
+
/** @type {boolean} - Whether the input is readonly */
|
|
52
|
+
readonly = false,
|
|
53
|
+
/** @type {string} - Error message to display */
|
|
54
|
+
error = "",
|
|
55
|
+
/** @type {string} - Help text to display below input */
|
|
56
|
+
helpText = "",
|
|
57
|
+
/** @type {string} - Left icon name */
|
|
58
|
+
leftIcon = "",
|
|
59
|
+
/** @type {string} - Right icon name */
|
|
60
|
+
rightIcon = "",
|
|
61
|
+
/** @type {string} - Additional CSS classes */
|
|
62
|
+
class: className = "",
|
|
63
|
+
/** @type {string} - Input mask pattern */
|
|
64
|
+
mask = "",
|
|
65
|
+
/** @type {string} - ARIA description */
|
|
66
|
+
ariaDescription = "",
|
|
67
|
+
/** @type {(event: Event) => void} - Focus event handler */
|
|
68
|
+
onfocus,
|
|
69
|
+
/** @type {(event: Event) => void} - Blur event handler */
|
|
70
|
+
onblur,
|
|
71
|
+
/** @type {(event: CustomEvent) => void} - Input event handler */
|
|
72
|
+
oninput,
|
|
73
|
+
/** @type {() => void} - Left icon click handler */
|
|
74
|
+
onleftIconClick,
|
|
75
|
+
/** @type {() => void} - Right icon click handler */
|
|
76
|
+
onrightIconClick,
|
|
77
|
+
} = $props()
|
|
78
|
+
|
|
79
|
+
let inputValue = $state(value)
|
|
80
|
+
let focused = $state(false)
|
|
81
|
+
let touched = $state(false)
|
|
82
|
+
|
|
83
|
+
// Sync input value when prop changes
|
|
84
|
+
$effect(() => {
|
|
85
|
+
inputValue = value
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// Handle input focus
|
|
89
|
+
function handleFocus(event) {
|
|
90
|
+
focused = true
|
|
91
|
+
onfocus?.(event)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Handle input blur
|
|
95
|
+
function handleBlur(event) {
|
|
96
|
+
focused = false
|
|
97
|
+
touched = true
|
|
98
|
+
onblur?.(event)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Handle input change
|
|
102
|
+
function handleInput(event) {
|
|
103
|
+
const newValue = event.target.value
|
|
104
|
+
|
|
105
|
+
// Apply mask if provided
|
|
106
|
+
if (mask) {
|
|
107
|
+
inputValue = applyMask(newValue, mask)
|
|
108
|
+
} else {
|
|
109
|
+
inputValue = newValue
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
oninput?.(new CustomEvent("input", { detail: { value: inputValue } }))
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Handle icon clicks
|
|
116
|
+
function handleLeftIconClick() {
|
|
117
|
+
onleftIconClick?.()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function handleRightIconClick() {
|
|
121
|
+
onrightIconClick?.()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Apply input mask
|
|
125
|
+
function applyMask(value, pattern) {
|
|
126
|
+
let result = ""
|
|
127
|
+
let valueIndex = 0
|
|
128
|
+
|
|
129
|
+
for (let i = 0; i < pattern.length && valueIndex < value.length; i++) {
|
|
130
|
+
const maskChar = pattern[i]
|
|
131
|
+
const valueChar = value[valueIndex]
|
|
132
|
+
|
|
133
|
+
if (maskChar === "#") {
|
|
134
|
+
if (/\d/.test(valueChar)) {
|
|
135
|
+
result += valueChar
|
|
136
|
+
valueIndex++
|
|
137
|
+
}
|
|
138
|
+
} else if (maskChar === "A") {
|
|
139
|
+
if (/[a-zA-Z]/.test(valueChar)) {
|
|
140
|
+
result += valueChar
|
|
141
|
+
valueIndex++
|
|
142
|
+
}
|
|
143
|
+
} else if (maskChar === "*") {
|
|
144
|
+
result += valueChar
|
|
145
|
+
valueIndex++
|
|
146
|
+
} else {
|
|
147
|
+
result += maskChar
|
|
148
|
+
if (valueChar === maskChar) {
|
|
149
|
+
valueIndex++
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return result
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Compute classes
|
|
158
|
+
const containerClasses = $derived(`
|
|
159
|
+
form-input-container
|
|
160
|
+
${floating ? "form-input-floating" : ""}
|
|
161
|
+
${error ? "form-input-error" : ""}
|
|
162
|
+
${disabled ? "form-input-disabled" : ""}
|
|
163
|
+
${className}
|
|
164
|
+
`)
|
|
165
|
+
|
|
166
|
+
const labelClasses = $derived(`
|
|
167
|
+
form-input-label
|
|
168
|
+
${floating && (focused || inputValue) ? "form-input-label-float" : ""}
|
|
169
|
+
${error ? "form-input-label-error" : ""}
|
|
170
|
+
${disabled ? "form-input-label-disabled" : ""}
|
|
171
|
+
`)
|
|
172
|
+
|
|
173
|
+
const inputClasses = $derived(`
|
|
174
|
+
form-input
|
|
175
|
+
${leftIcon ? "form-input-left-icon" : ""}
|
|
176
|
+
${rightIcon ? "form-input-right-icon" : ""}
|
|
177
|
+
${error ? "form-input-field-error" : ""}
|
|
178
|
+
`)
|
|
179
|
+
</script>
|
|
180
|
+
|
|
181
|
+
<div class={containerClasses}>
|
|
182
|
+
{#if label}
|
|
183
|
+
<label for={id} class={labelClasses}>
|
|
184
|
+
{label}
|
|
185
|
+
{#if required}
|
|
186
|
+
<span class="form-input-required">*</span>
|
|
187
|
+
{/if}
|
|
188
|
+
</label>
|
|
189
|
+
{/if}
|
|
190
|
+
|
|
191
|
+
<div class="form-input-wrapper">
|
|
192
|
+
{#if leftIcon}
|
|
193
|
+
<button
|
|
194
|
+
type="button"
|
|
195
|
+
class="form-input-icon form-input-icon-left"
|
|
196
|
+
onclick={handleLeftIconClick}
|
|
197
|
+
tabindex="-1"
|
|
198
|
+
aria-hidden="true"
|
|
199
|
+
>
|
|
200
|
+
<Icon name={leftIcon} />
|
|
201
|
+
</button>
|
|
202
|
+
{/if}
|
|
203
|
+
|
|
204
|
+
<input
|
|
205
|
+
{type}
|
|
206
|
+
{id}
|
|
207
|
+
{name}
|
|
208
|
+
class={inputClasses}
|
|
209
|
+
value={inputValue}
|
|
210
|
+
placeholder={floating ? ' ' : placeholder}
|
|
211
|
+
{disabled}
|
|
212
|
+
{readonly}
|
|
213
|
+
{required}
|
|
214
|
+
aria-invalid={!!error}
|
|
215
|
+
aria-describedby={error || helpText ? `${id}-description` : undefined}
|
|
216
|
+
onfocus={handleFocus}
|
|
217
|
+
onblur={handleBlur}
|
|
218
|
+
oninput={handleInput}
|
|
219
|
+
/>
|
|
220
|
+
|
|
221
|
+
{#if rightIcon}
|
|
222
|
+
<button
|
|
223
|
+
type="button"
|
|
224
|
+
class="form-input-icon form-input-icon-right"
|
|
225
|
+
onclick={handleRightIconClick}
|
|
226
|
+
tabindex="-1"
|
|
227
|
+
aria-hidden="true"
|
|
228
|
+
>
|
|
229
|
+
<Icon name={rightIcon} />
|
|
230
|
+
</button>
|
|
231
|
+
{/if}
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
{#if (error || helpText) && touched}
|
|
235
|
+
<div
|
|
236
|
+
id="{id}-description"
|
|
237
|
+
class="form-input-description"
|
|
238
|
+
class:form-input-error-text={!!error}
|
|
239
|
+
transition:slide={{ duration: 150 }}
|
|
240
|
+
>
|
|
241
|
+
{error || helpText}
|
|
242
|
+
</div>
|
|
243
|
+
{/if}
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
<style>
|
|
247
|
+
@reference "../../twintrinsic.css";
|
|
248
|
+
|
|
249
|
+
/* Container */
|
|
250
|
+
.form-input-container {
|
|
251
|
+
@apply relative space-y-1.5;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* Label */
|
|
255
|
+
.form-input-label {
|
|
256
|
+
@apply block text-sm font-medium text-muted transition-all;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.form-input-label-error {
|
|
260
|
+
@apply text-error-bold;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.form-input-label-disabled {
|
|
264
|
+
@apply opacity-50 cursor-not-allowed;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.form-input-required {
|
|
268
|
+
@apply text-error-bold ml-1;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* Input wrapper */
|
|
272
|
+
.form-input-wrapper {
|
|
273
|
+
@apply relative;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* Base input */
|
|
277
|
+
.form-input {
|
|
278
|
+
@apply block w-full px-3 py-2 bg-surface;
|
|
279
|
+
@apply border border-border rounded-md shadow-sm;
|
|
280
|
+
@apply text-base placeholder-muted;
|
|
281
|
+
@apply transition-colors duration-150;
|
|
282
|
+
@apply focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.form-input-disabled {
|
|
286
|
+
@apply opacity-50 cursor-not-allowed;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.form-input-field-error {
|
|
290
|
+
@apply border-error-bold;
|
|
291
|
+
@apply focus:ring-error-bold focus:border-error-bold;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* Icons */
|
|
295
|
+
.form-input-left-icon {
|
|
296
|
+
@apply pl-10;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.form-input-right-icon {
|
|
300
|
+
@apply pr-10;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.form-input-icon {
|
|
304
|
+
@apply absolute inset-y-0 flex items-center;
|
|
305
|
+
@apply p-2 text-muted transition-colors;
|
|
306
|
+
@apply hover:text-primary-text focus:outline-none;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.form-input-icon-left {
|
|
310
|
+
@apply left-0;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.form-input-icon-right {
|
|
314
|
+
@apply right-0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* Floating label */
|
|
318
|
+
.form-input-floating .form-input-label {
|
|
319
|
+
@apply absolute text-sm text-muted;
|
|
320
|
+
transform-origin: 0 0;
|
|
321
|
+
transition: transform 0.15s ease-in-out;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.form-input-floating .form-input {
|
|
325
|
+
@apply pt-4 pb-1;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.form-input-label-float {
|
|
329
|
+
@apply transform -translate-y-3 scale-75 text-primary-500;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/* Description/Error text */
|
|
333
|
+
.form-input-description {
|
|
334
|
+
@apply mt-1 text-sm;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.form-input-error-text {
|
|
338
|
+
@apply text-error-bold;
|
|
339
|
+
}
|
|
340
|
+
</style>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export default Input;
|
|
2
|
+
type Input = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Input - Base component for form inputs with validation, floating labels, and input groups.
|
|
8
|
+
* Built with accessibility and customization in mind.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <Input label="Username" />
|
|
13
|
+
*
|
|
14
|
+
* <Input
|
|
15
|
+
* label="Email"
|
|
16
|
+
* type="email"
|
|
17
|
+
* value="user@example.com"
|
|
18
|
+
* floating={true}
|
|
19
|
+
* required
|
|
20
|
+
* error="Please enter a valid email"
|
|
21
|
+
* />
|
|
22
|
+
*
|
|
23
|
+
* <Input
|
|
24
|
+
* label="Password"
|
|
25
|
+
* type="password"
|
|
26
|
+
* leftIcon="lock"
|
|
27
|
+
* rightIcon="eye"
|
|
28
|
+
* onrightIconClick={togglePassword}
|
|
29
|
+
* />
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare const Input: import("svelte").Component<{
|
|
33
|
+
label: any;
|
|
34
|
+
type?: string;
|
|
35
|
+
value?: string;
|
|
36
|
+
placeholder?: string;
|
|
37
|
+
name?: string;
|
|
38
|
+
id?: any;
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
required?: boolean;
|
|
41
|
+
floating?: boolean;
|
|
42
|
+
readonly?: boolean;
|
|
43
|
+
error?: string;
|
|
44
|
+
helpText?: string;
|
|
45
|
+
leftIcon?: string;
|
|
46
|
+
rightIcon?: string;
|
|
47
|
+
class?: string;
|
|
48
|
+
mask?: string;
|
|
49
|
+
ariaDescription?: string;
|
|
50
|
+
onfocus: any;
|
|
51
|
+
onblur: any;
|
|
52
|
+
oninput: any;
|
|
53
|
+
onleftIconClick: any;
|
|
54
|
+
onrightIconClick: any;
|
|
55
|
+
}, {}, "">;
|
|
56
|
+
type $$ComponentProps = {
|
|
57
|
+
label: any;
|
|
58
|
+
type?: string;
|
|
59
|
+
value?: string;
|
|
60
|
+
placeholder?: string;
|
|
61
|
+
name?: string;
|
|
62
|
+
id?: any;
|
|
63
|
+
disabled?: boolean;
|
|
64
|
+
required?: boolean;
|
|
65
|
+
floating?: boolean;
|
|
66
|
+
readonly?: boolean;
|
|
67
|
+
error?: string;
|
|
68
|
+
helpText?: string;
|
|
69
|
+
leftIcon?: string;
|
|
70
|
+
rightIcon?: string;
|
|
71
|
+
class?: string;
|
|
72
|
+
mask?: string;
|
|
73
|
+
ariaDescription?: string;
|
|
74
|
+
onfocus: any;
|
|
75
|
+
onblur: any;
|
|
76
|
+
oninput: any;
|
|
77
|
+
onleftIconClick: any;
|
|
78
|
+
onrightIconClick: any;
|
|
79
|
+
};
|