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,78 @@
|
|
|
1
|
+
export default FileUpload;
|
|
2
|
+
type FileUpload = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* FileUpload - A component for uploading files with drag and drop support.
|
|
8
|
+
* Provides consistent styling, accessibility features, and various display options.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <FileUpload
|
|
13
|
+
* onchange={handleFiles}
|
|
14
|
+
* accept="image/*"
|
|
15
|
+
* multiple
|
|
16
|
+
* />
|
|
17
|
+
*
|
|
18
|
+
* <FileUpload
|
|
19
|
+
* value={existingFiles}
|
|
20
|
+
* maxFiles={5}
|
|
21
|
+
* maxSize={5242880}
|
|
22
|
+
* onchange={handleFiles}
|
|
23
|
+
* onerror={handleError}
|
|
24
|
+
* >
|
|
25
|
+
* <div slot="dropzone">
|
|
26
|
+
* <p>Drag files here or click to browse</p>
|
|
27
|
+
* </div>
|
|
28
|
+
* </FileUpload>
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare const FileUpload: import("svelte").Component<{
|
|
32
|
+
class?: string;
|
|
33
|
+
id?: any;
|
|
34
|
+
name: any;
|
|
35
|
+
value?: any[];
|
|
36
|
+
accept: any;
|
|
37
|
+
multiple?: boolean;
|
|
38
|
+
disabled?: boolean;
|
|
39
|
+
maxFiles: any;
|
|
40
|
+
maxSize: any;
|
|
41
|
+
showPreviews?: boolean;
|
|
42
|
+
autoUpload?: boolean;
|
|
43
|
+
uploadUrl: any;
|
|
44
|
+
uploadHeaders: any;
|
|
45
|
+
browseLabel?: string;
|
|
46
|
+
dropzoneLabel?: string;
|
|
47
|
+
ariaLabel?: string;
|
|
48
|
+
onchange: any;
|
|
49
|
+
onerror: any;
|
|
50
|
+
onprogress: any;
|
|
51
|
+
onsuccess: any;
|
|
52
|
+
dropzone: any;
|
|
53
|
+
previews: any;
|
|
54
|
+
}, {}, "">;
|
|
55
|
+
type $$ComponentProps = {
|
|
56
|
+
class?: string;
|
|
57
|
+
id?: any;
|
|
58
|
+
name: any;
|
|
59
|
+
value?: any[];
|
|
60
|
+
accept: any;
|
|
61
|
+
multiple?: boolean;
|
|
62
|
+
disabled?: boolean;
|
|
63
|
+
maxFiles: any;
|
|
64
|
+
maxSize: any;
|
|
65
|
+
showPreviews?: boolean;
|
|
66
|
+
autoUpload?: boolean;
|
|
67
|
+
uploadUrl: any;
|
|
68
|
+
uploadHeaders: any;
|
|
69
|
+
browseLabel?: string;
|
|
70
|
+
dropzoneLabel?: string;
|
|
71
|
+
ariaLabel?: string;
|
|
72
|
+
onchange: any;
|
|
73
|
+
onerror: any;
|
|
74
|
+
onprogress: any;
|
|
75
|
+
onsuccess: any;
|
|
76
|
+
dropzone: any;
|
|
77
|
+
previews: any;
|
|
78
|
+
};
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
FloatLabel - A component for creating form inputs with animated floating labels.
|
|
4
|
+
Provides a modern, space-efficient input with improved user experience.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<FloatLabel label="Username">
|
|
9
|
+
<TextInput name="username" />
|
|
10
|
+
</FloatLabel>
|
|
11
|
+
|
|
12
|
+
<FloatLabel label="Email" required>
|
|
13
|
+
<TextInput type="email" name="email" />
|
|
14
|
+
</FloatLabel>
|
|
15
|
+
|
|
16
|
+
<FloatLabel label="Message">
|
|
17
|
+
<Textarea name="message" rows={3} />
|
|
18
|
+
</FloatLabel>
|
|
19
|
+
```
|
|
20
|
+
-->
|
|
21
|
+
<script>
|
|
22
|
+
import { onMount } from "svelte"
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
/** @type {string} - Additional CSS classes */
|
|
26
|
+
class: className = "",
|
|
27
|
+
|
|
28
|
+
/** @type {string} - HTML id for accessibility */
|
|
29
|
+
id = crypto.randomUUID(),
|
|
30
|
+
|
|
31
|
+
/** @type {string} - Label text */
|
|
32
|
+
label,
|
|
33
|
+
|
|
34
|
+
/** @type {boolean} - Whether the input is required */
|
|
35
|
+
required = false,
|
|
36
|
+
|
|
37
|
+
/** @type {boolean} - Whether the input is disabled */
|
|
38
|
+
disabled = false,
|
|
39
|
+
|
|
40
|
+
/** @type {string} - Error message to display */
|
|
41
|
+
error,
|
|
42
|
+
|
|
43
|
+
/** @type {string} - Help text to display below the input */
|
|
44
|
+
helpText,
|
|
45
|
+
|
|
46
|
+
children,
|
|
47
|
+
} = $props()
|
|
48
|
+
|
|
49
|
+
// Component state
|
|
50
|
+
let inputElement
|
|
51
|
+
let isFocused = $state(false)
|
|
52
|
+
let hasValue = $state(false)
|
|
53
|
+
let isFloating = $state(false)
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Checks if the input has a value
|
|
57
|
+
*/
|
|
58
|
+
function checkValue() {
|
|
59
|
+
if (!inputElement) return
|
|
60
|
+
|
|
61
|
+
// Find the actual input element (could be nested)
|
|
62
|
+
const input = findInputElement(inputElement)
|
|
63
|
+
|
|
64
|
+
if (input) {
|
|
65
|
+
hasValue = !!input.value
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Update floating state
|
|
69
|
+
isFloating = isFocused || hasValue
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Recursively finds an input element
|
|
74
|
+
* @param {HTMLElement} element - Element to search in
|
|
75
|
+
* @returns {HTMLElement|null} - Found input element or null
|
|
76
|
+
*/
|
|
77
|
+
function findInputElement(element) {
|
|
78
|
+
// Check if this is an input, textarea, or select
|
|
79
|
+
if (
|
|
80
|
+
element.tagName === "INPUT" ||
|
|
81
|
+
element.tagName === "TEXTAREA" ||
|
|
82
|
+
element.tagName === "SELECT"
|
|
83
|
+
) {
|
|
84
|
+
return element
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check children
|
|
88
|
+
const inputs = element.querySelectorAll("input, textarea, select")
|
|
89
|
+
if (inputs.length > 0) {
|
|
90
|
+
return inputs[0]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Handles focus event
|
|
98
|
+
*/
|
|
99
|
+
function handleFocus() {
|
|
100
|
+
isFocused = true
|
|
101
|
+
isFloating = true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Handles blur event
|
|
106
|
+
*/
|
|
107
|
+
function handleBlur() {
|
|
108
|
+
isFocused = false
|
|
109
|
+
checkValue()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Handles input event
|
|
114
|
+
*/
|
|
115
|
+
function handleInput() {
|
|
116
|
+
checkValue()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Initialize component
|
|
120
|
+
onMount(() => {
|
|
121
|
+
if (inputElement) {
|
|
122
|
+
// Find the actual input element
|
|
123
|
+
const input = findInputElement(inputElement)
|
|
124
|
+
|
|
125
|
+
if (input) {
|
|
126
|
+
// Add event listeners
|
|
127
|
+
input.addEventListener("focus", handleFocus)
|
|
128
|
+
input.addEventListener("blur", handleBlur)
|
|
129
|
+
input.addEventListener("input", handleInput)
|
|
130
|
+
|
|
131
|
+
// Initial check
|
|
132
|
+
checkValue()
|
|
133
|
+
|
|
134
|
+
// Clean up
|
|
135
|
+
return () => {
|
|
136
|
+
input.removeEventListener("focus", handleFocus)
|
|
137
|
+
input.removeEventListener("blur", handleBlur)
|
|
138
|
+
input.removeEventListener("input", handleInput)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
</script>
|
|
144
|
+
|
|
145
|
+
<div
|
|
146
|
+
class="
|
|
147
|
+
float-label-wrapper
|
|
148
|
+
{isFocused ? 'is-focused' : ''}
|
|
149
|
+
{isFloating ? 'is-floating' : ''}
|
|
150
|
+
{hasValue ? 'has-value' : ''}
|
|
151
|
+
{error ? 'has-error' : ''}
|
|
152
|
+
{disabled ? 'is-disabled' : ''}
|
|
153
|
+
{className}
|
|
154
|
+
"
|
|
155
|
+
>
|
|
156
|
+
<div class="float-label-container">
|
|
157
|
+
{#if label}
|
|
158
|
+
<label
|
|
159
|
+
for={id}
|
|
160
|
+
class="float-label"
|
|
161
|
+
>
|
|
162
|
+
{label}
|
|
163
|
+
{#if required}
|
|
164
|
+
<span class="required-indicator" aria-hidden="true">*</span>
|
|
165
|
+
<span class="sr-only">required</span>
|
|
166
|
+
{/if}
|
|
167
|
+
</label>
|
|
168
|
+
{/if}
|
|
169
|
+
|
|
170
|
+
<div class="float-label-input" bind:this={inputElement}>
|
|
171
|
+
{@render children?.()}
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
{#if error}
|
|
176
|
+
<div class="float-label-error" role="alert">
|
|
177
|
+
{error}
|
|
178
|
+
</div>
|
|
179
|
+
{/if}
|
|
180
|
+
|
|
181
|
+
{#if helpText && !error}
|
|
182
|
+
<div class="float-label-help">
|
|
183
|
+
{helpText}
|
|
184
|
+
</div>
|
|
185
|
+
{/if}
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<style>
|
|
189
|
+
@reference "../../twintrinsic.css";
|
|
190
|
+
|
|
191
|
+
.float-label-wrapper {
|
|
192
|
+
@apply w-full;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.float-label-container {
|
|
196
|
+
@apply relative;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.float-label {
|
|
200
|
+
@apply absolute left-3 top-1/2 -translate-y-1/2;
|
|
201
|
+
@apply text-muted dark:text-muted;
|
|
202
|
+
@apply pointer-events-none transition-all duration-200;
|
|
203
|
+
@apply text-base;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.is-floating .float-label {
|
|
207
|
+
@apply text-xs top-0 -translate-y-1/2;
|
|
208
|
+
@apply bg-background dark:bg-background px-1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.is-focused .float-label {
|
|
212
|
+
@apply text-primary-500 dark:text-primary-400;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.has-error .float-label {
|
|
216
|
+
@apply text-error-500 dark:text-error-400;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.float-label-input {
|
|
220
|
+
@apply w-full;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* Style child inputs */
|
|
224
|
+
.float-label-input :global(input),
|
|
225
|
+
.float-label-input :global(textarea),
|
|
226
|
+
.float-label-input :global(select) {
|
|
227
|
+
@apply pt-4 pb-2;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.required-indicator {
|
|
231
|
+
@apply ml-1 text-error-500 dark:text-error-400;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.float-label-error {
|
|
235
|
+
@apply mt-1 text-xs text-error-600 dark:text-error-400;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.float-label-help {
|
|
239
|
+
@apply mt-1 text-xs text-muted dark:text-muted;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.is-disabled {
|
|
243
|
+
@apply opacity-60 pointer-events-none;
|
|
244
|
+
}
|
|
245
|
+
</style>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export default FloatLabel;
|
|
2
|
+
type FloatLabel = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* FloatLabel - A component for creating form inputs with animated floating labels.
|
|
8
|
+
* Provides a modern, space-efficient input with improved user experience.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <FloatLabel label="Username">
|
|
13
|
+
* <TextInput name="username" />
|
|
14
|
+
* </FloatLabel>
|
|
15
|
+
*
|
|
16
|
+
* <FloatLabel label="Email" required>
|
|
17
|
+
* <TextInput type="email" name="email" />
|
|
18
|
+
* </FloatLabel>
|
|
19
|
+
*
|
|
20
|
+
* <FloatLabel label="Message">
|
|
21
|
+
* <Textarea name="message" rows={3} />
|
|
22
|
+
* </FloatLabel>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare const FloatLabel: import("svelte").Component<{
|
|
26
|
+
class?: string;
|
|
27
|
+
id?: any;
|
|
28
|
+
label: any;
|
|
29
|
+
required?: boolean;
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
error: any;
|
|
32
|
+
helpText: any;
|
|
33
|
+
children: any;
|
|
34
|
+
}, {}, "">;
|
|
35
|
+
type $$ComponentProps = {
|
|
36
|
+
class?: string;
|
|
37
|
+
id?: any;
|
|
38
|
+
label: any;
|
|
39
|
+
required?: boolean;
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
error: any;
|
|
42
|
+
helpText: any;
|
|
43
|
+
children: any;
|
|
44
|
+
};
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Form - A component for creating accessible, styled forms with validation support.
|
|
4
|
+
Handles form submission, validation, and provides context for form controls.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<Form onsubmit={handleSubmit}>
|
|
9
|
+
<FormField label="Username" name="username" required>
|
|
10
|
+
<TextInput />
|
|
11
|
+
</FormField>
|
|
12
|
+
|
|
13
|
+
<FormField label="Email" name="email" required type="email">
|
|
14
|
+
<TextInput type="email" />
|
|
15
|
+
</FormField>
|
|
16
|
+
|
|
17
|
+
<Button type="submit">Submit</Button>
|
|
18
|
+
</Form>
|
|
19
|
+
```
|
|
20
|
+
-->
|
|
21
|
+
<script>
|
|
22
|
+
import { setContext } from "svelte"
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
/** @type {string} - Additional CSS classes */
|
|
26
|
+
class: className = "",
|
|
27
|
+
|
|
28
|
+
/** @type {string} - HTML id for accessibility */
|
|
29
|
+
id = crypto.randomUUID(),
|
|
30
|
+
|
|
31
|
+
/** @type {string} - Form method (get or post) */
|
|
32
|
+
method = "post",
|
|
33
|
+
|
|
34
|
+
/** @type {string} - Form action URL */
|
|
35
|
+
action = "",
|
|
36
|
+
|
|
37
|
+
/** @type {boolean} - Whether to validate the form on submission */
|
|
38
|
+
validate = true,
|
|
39
|
+
|
|
40
|
+
/** @type {boolean} - Whether to use the browser's built-in validation UI */
|
|
41
|
+
useNativeValidation = true,
|
|
42
|
+
|
|
43
|
+
/** @type {string} - Layout direction (vertical or horizontal) */
|
|
44
|
+
layout = "vertical",
|
|
45
|
+
|
|
46
|
+
/** @type {boolean} - Whether to disable all form controls */
|
|
47
|
+
disabled = false,
|
|
48
|
+
|
|
49
|
+
/** @type {boolean} - Whether the form is in a loading state */
|
|
50
|
+
loading = false,
|
|
51
|
+
|
|
52
|
+
/** @type {(event: CustomEvent) => void} - Submit event handler */
|
|
53
|
+
onsubmit,
|
|
54
|
+
/** @type {(event: CustomEvent) => void} - Change event handler */
|
|
55
|
+
onchange,
|
|
56
|
+
/** @type {(event: CustomEvent) => void} - Error event handler */
|
|
57
|
+
onerror,
|
|
58
|
+
|
|
59
|
+
children,
|
|
60
|
+
} = $props()
|
|
61
|
+
|
|
62
|
+
// Form state
|
|
63
|
+
let formElement
|
|
64
|
+
let formData = $state({})
|
|
65
|
+
let errors = $state({})
|
|
66
|
+
let touched = $state({})
|
|
67
|
+
let isSubmitting = $state(false)
|
|
68
|
+
let isValid = $state(true)
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Updates a field's value in the form data
|
|
72
|
+
* @param {string} name - Field name
|
|
73
|
+
* @param {any} value - Field value
|
|
74
|
+
*/
|
|
75
|
+
function updateField(name, value) {
|
|
76
|
+
formData[name] = value
|
|
77
|
+
touched[name] = true
|
|
78
|
+
|
|
79
|
+
// Validate the field if needed
|
|
80
|
+
if (validate) {
|
|
81
|
+
validateField(name, value)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validates a single field
|
|
87
|
+
* @param {string} name - Field name
|
|
88
|
+
* @param {any} value - Field value
|
|
89
|
+
* @returns {boolean} - Whether the field is valid
|
|
90
|
+
*/
|
|
91
|
+
function validateField(name, value) {
|
|
92
|
+
const field = formElement?.elements[name]
|
|
93
|
+
|
|
94
|
+
if (!field) return true
|
|
95
|
+
|
|
96
|
+
// Clear existing errors for this field
|
|
97
|
+
delete errors[name]
|
|
98
|
+
|
|
99
|
+
// Use HTML5 validation API
|
|
100
|
+
if (field.validity) {
|
|
101
|
+
if (!field.validity.valid) {
|
|
102
|
+
errors[name] = field.validationMessage
|
|
103
|
+
return false
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return true
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Validates the entire form
|
|
112
|
+
* @returns {boolean} - Whether the form is valid
|
|
113
|
+
*/
|
|
114
|
+
function validateForm() {
|
|
115
|
+
// Reset errors
|
|
116
|
+
errors = {}
|
|
117
|
+
|
|
118
|
+
// Check all fields
|
|
119
|
+
let valid = true
|
|
120
|
+
|
|
121
|
+
if (formElement) {
|
|
122
|
+
const formControls = Array.from(formElement.elements)
|
|
123
|
+
|
|
124
|
+
formControls.forEach((field) => {
|
|
125
|
+
if (field.name) {
|
|
126
|
+
const fieldValid = validateField(field.name, field.value)
|
|
127
|
+
if (!fieldValid) {
|
|
128
|
+
valid = false
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
isValid = valid
|
|
135
|
+
return valid
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Handles form submission
|
|
140
|
+
* @param {Event} event - Submit event
|
|
141
|
+
*/
|
|
142
|
+
function handleSubmit(event) {
|
|
143
|
+
// Prevent default form submission
|
|
144
|
+
event.preventDefault()
|
|
145
|
+
|
|
146
|
+
// Mark all fields as touched
|
|
147
|
+
if (formElement) {
|
|
148
|
+
const formControls = Array.from(formElement.elements)
|
|
149
|
+
formControls.forEach((field) => {
|
|
150
|
+
if (field.name) {
|
|
151
|
+
touched[field.name] = true
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Validate if needed
|
|
157
|
+
if (validate) {
|
|
158
|
+
if (!validateForm()) {
|
|
159
|
+
// If validation fails, don't submit
|
|
160
|
+
onerror?.(new CustomEvent("invalid", { detail: { errors } }))
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Set submitting state
|
|
166
|
+
isSubmitting = true
|
|
167
|
+
|
|
168
|
+
// Get form data
|
|
169
|
+
const formDataObj = new FormData(formElement)
|
|
170
|
+
const dataObj = {}
|
|
171
|
+
|
|
172
|
+
for (const [key, value] of formDataObj.entries()) {
|
|
173
|
+
dataObj[key] = value
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Dispatch submit event with form data
|
|
177
|
+
onsubmit?.(new CustomEvent("submit", { detail: {
|
|
178
|
+
data: dataObj,
|
|
179
|
+
formData: formDataObj,
|
|
180
|
+
form: formElement,
|
|
181
|
+
} }))
|
|
182
|
+
|
|
183
|
+
// Reset submitting state after a short delay
|
|
184
|
+
setTimeout(() => {
|
|
185
|
+
isSubmitting = false
|
|
186
|
+
}, 100)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Resets the form to its initial state
|
|
191
|
+
*/
|
|
192
|
+
function resetForm() {
|
|
193
|
+
if (formElement) {
|
|
194
|
+
formElement.reset()
|
|
195
|
+
formData = {}
|
|
196
|
+
errors = {}
|
|
197
|
+
touched = {}
|
|
198
|
+
isValid = true
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Provide form context to child components
|
|
203
|
+
$effect(() => {
|
|
204
|
+
setContext("form", {
|
|
205
|
+
registerField: (name, initialValue) => {
|
|
206
|
+
if (initialValue !== undefined && formData[name] === undefined) {
|
|
207
|
+
formData[name] = initialValue
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
getValue: () => formData[name],
|
|
212
|
+
setValue: (value) => updateField(name, value),
|
|
213
|
+
getError: () => errors[name],
|
|
214
|
+
isTouched: () => !!touched[name],
|
|
215
|
+
isDisabled: () => disabled || loading || isSubmitting,
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
layout,
|
|
219
|
+
disabled: () => disabled || loading || isSubmitting,
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
// Expose form API to parent components
|
|
224
|
+
const formApi = {
|
|
225
|
+
reset: resetForm,
|
|
226
|
+
validate: validateForm,
|
|
227
|
+
setValues: (values) => {
|
|
228
|
+
formData = { ...formData, ...values }
|
|
229
|
+
},
|
|
230
|
+
getValues: () => ({ ...formData }),
|
|
231
|
+
setErrors: (newErrors) => {
|
|
232
|
+
errors = { ...errors, ...newErrors }
|
|
233
|
+
isValid = Object.keys(errors).length === 0
|
|
234
|
+
},
|
|
235
|
+
clearErrors: () => {
|
|
236
|
+
errors = {}
|
|
237
|
+
isValid = true
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Expose form API to parent
|
|
242
|
+
dispatch("ready", { form: formApi })
|
|
243
|
+
</script>
|
|
244
|
+
|
|
245
|
+
<form
|
|
246
|
+
{id}
|
|
247
|
+
class="form {layout === 'horizontal' ? 'form-horizontal' : 'form-vertical'} {className}"
|
|
248
|
+
class:disabled
|
|
249
|
+
class:loading={loading || isSubmitting}
|
|
250
|
+
{method}
|
|
251
|
+
{action}
|
|
252
|
+
novalidate={!useNativeValidation}
|
|
253
|
+
onsubmit={handleSubmit}
|
|
254
|
+
bind:this={formElement}
|
|
255
|
+
>
|
|
256
|
+
{@render children?.()}
|
|
257
|
+
</form>
|
|
258
|
+
|
|
259
|
+
<style>
|
|
260
|
+
@reference "../../twintrinsic.css";
|
|
261
|
+
|
|
262
|
+
.form {
|
|
263
|
+
@apply w-full;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.form-vertical {
|
|
267
|
+
@apply space-y-4;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.form-horizontal {
|
|
271
|
+
@apply grid gap-4;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.form.disabled {
|
|
275
|
+
@apply opacity-70 pointer-events-none;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.form.loading {
|
|
279
|
+
@apply opacity-70;
|
|
280
|
+
}
|
|
281
|
+
</style>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export default Form;
|
|
2
|
+
type Form = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Form - A component for creating accessible, styled forms with validation support.
|
|
8
|
+
* Handles form submission, validation, and provides context for form controls.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <Form onsubmit={handleSubmit}>
|
|
13
|
+
* <FormField label="Username" name="username" required>
|
|
14
|
+
* <TextInput />
|
|
15
|
+
* </FormField>
|
|
16
|
+
*
|
|
17
|
+
* <FormField label="Email" name="email" required type="email">
|
|
18
|
+
* <TextInput type="email" />
|
|
19
|
+
* </FormField>
|
|
20
|
+
*
|
|
21
|
+
* <Button type="submit">Submit</Button>
|
|
22
|
+
* </Form>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare const Form: import("svelte").Component<{
|
|
26
|
+
class?: string;
|
|
27
|
+
id?: any;
|
|
28
|
+
method?: string;
|
|
29
|
+
action?: string;
|
|
30
|
+
validate?: boolean;
|
|
31
|
+
useNativeValidation?: boolean;
|
|
32
|
+
layout?: string;
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
loading?: boolean;
|
|
35
|
+
onsubmit: any;
|
|
36
|
+
onchange: any;
|
|
37
|
+
onerror: any;
|
|
38
|
+
children: any;
|
|
39
|
+
}, {}, "">;
|
|
40
|
+
type $$ComponentProps = {
|
|
41
|
+
class?: string;
|
|
42
|
+
id?: any;
|
|
43
|
+
method?: string;
|
|
44
|
+
action?: string;
|
|
45
|
+
validate?: boolean;
|
|
46
|
+
useNativeValidation?: boolean;
|
|
47
|
+
layout?: string;
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
loading?: boolean;
|
|
50
|
+
onsubmit: any;
|
|
51
|
+
onchange: any;
|
|
52
|
+
onerror: any;
|
|
53
|
+
children: any;
|
|
54
|
+
};
|