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,334 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Modal - A dialog component for displaying content that requires user attention.
|
|
4
|
+
Provides accessible focus management, keyboard navigation, and backdrop interactions.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<Modal open={showModal} onclose={() => showModal = false}>
|
|
9
|
+
<svelte:fragment slot="header">Modal Title</svelte:fragment>
|
|
10
|
+
<p>Modal content goes here</p>
|
|
11
|
+
<svelte:fragment slot="footer">
|
|
12
|
+
<Button onclick={() => showModal = false}>Close</Button>
|
|
13
|
+
<Button variant="primary">Save</Button>
|
|
14
|
+
</svelte:fragment>
|
|
15
|
+
</Modal>
|
|
16
|
+
```
|
|
17
|
+
-->
|
|
18
|
+
<script>
|
|
19
|
+
import { onMount } from "svelte"
|
|
20
|
+
import { fade, scale } from "svelte/transition"
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
/** @type {string} - Additional CSS classes */
|
|
24
|
+
class: className = "",
|
|
25
|
+
|
|
26
|
+
/** @type {string} - HTML id for accessibility */
|
|
27
|
+
id = crypto.randomUUID(),
|
|
28
|
+
|
|
29
|
+
/** @type {boolean} - Whether the modal is open */
|
|
30
|
+
open = false,
|
|
31
|
+
|
|
32
|
+
/** @type {boolean} - Whether to close when clicking outside */
|
|
33
|
+
closeOnOutsideClick = true,
|
|
34
|
+
|
|
35
|
+
/** @type {boolean} - Whether to close when pressing Escape */
|
|
36
|
+
closeOnEscape = true,
|
|
37
|
+
|
|
38
|
+
/** @type {string} - Size of the modal (sm, md, lg, xl, full) */
|
|
39
|
+
size = "md",
|
|
40
|
+
|
|
41
|
+
/** @type {boolean} - Whether to center the modal vertically */
|
|
42
|
+
centered = true,
|
|
43
|
+
|
|
44
|
+
/** @type {boolean} - Whether to show a close button in the header */
|
|
45
|
+
showCloseButton = true,
|
|
46
|
+
|
|
47
|
+
/** @type {string} - ARIA label for the close button */
|
|
48
|
+
closeButtonLabel = "Close modal",
|
|
49
|
+
|
|
50
|
+
/** @type {string} - ARIA label for the modal */
|
|
51
|
+
ariaLabel,
|
|
52
|
+
|
|
53
|
+
/** @type {string} - ARIA description for the modal */
|
|
54
|
+
ariaDescription,
|
|
55
|
+
|
|
56
|
+
/** @type {(event: CustomEvent) => void} - Close event handler */
|
|
57
|
+
onclose,
|
|
58
|
+
|
|
59
|
+
children,
|
|
60
|
+
header,
|
|
61
|
+
footer,
|
|
62
|
+
} = $props()
|
|
63
|
+
|
|
64
|
+
// Modal state
|
|
65
|
+
let isOpen = $state(open)
|
|
66
|
+
let modalElement = $state()
|
|
67
|
+
let previouslyFocusedElement = $state()
|
|
68
|
+
|
|
69
|
+
// Sync open state when prop changes
|
|
70
|
+
$effect(() => {
|
|
71
|
+
isOpen = open
|
|
72
|
+
|
|
73
|
+
if (isOpen) {
|
|
74
|
+
openModal()
|
|
75
|
+
} else {
|
|
76
|
+
closeModal()
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Opens the modal
|
|
82
|
+
*/
|
|
83
|
+
function openModal() {
|
|
84
|
+
// Save the currently focused element to restore later
|
|
85
|
+
previouslyFocusedElement = document.activeElement
|
|
86
|
+
|
|
87
|
+
// Add body class to prevent scrolling
|
|
88
|
+
document.body.classList.add("modal-open")
|
|
89
|
+
|
|
90
|
+
// Focus the modal after it's visible
|
|
91
|
+
setTimeout(() => {
|
|
92
|
+
focusFirstElement()
|
|
93
|
+
}, 50)
|
|
94
|
+
|
|
95
|
+
dispatch("open")
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Closes the modal
|
|
100
|
+
*/
|
|
101
|
+
function closeModal() {
|
|
102
|
+
// Remove body class
|
|
103
|
+
document.body.classList.remove("modal-open")
|
|
104
|
+
|
|
105
|
+
// Restore focus to the previously focused element
|
|
106
|
+
if (previouslyFocusedElement) {
|
|
107
|
+
previouslyFocusedElement.focus()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
onclose?.(new CustomEvent("close"))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Handles backdrop clicks
|
|
115
|
+
* @param {MouseEvent} event - Click event
|
|
116
|
+
*/
|
|
117
|
+
function handleBackdropClick(event) {
|
|
118
|
+
// Only close if clicking directly on the backdrop, not on the modal content
|
|
119
|
+
if (closeOnOutsideClick && event.target === event.currentTarget) {
|
|
120
|
+
isOpen = false
|
|
121
|
+
closeModal()
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Handles keydown events
|
|
127
|
+
* @param {KeyboardEvent} event - Keydown event
|
|
128
|
+
*/
|
|
129
|
+
function handleKeydown(event) {
|
|
130
|
+
if (!isOpen) return
|
|
131
|
+
|
|
132
|
+
// Close on Escape key
|
|
133
|
+
if (closeOnEscape && event.key === "Escape") {
|
|
134
|
+
event.preventDefault()
|
|
135
|
+
isOpen = false
|
|
136
|
+
closeModal()
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Trap focus within modal
|
|
141
|
+
if (event.key === "Tab") {
|
|
142
|
+
trapFocus(event)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Traps focus within the modal
|
|
148
|
+
* @param {KeyboardEvent} event - Keydown event
|
|
149
|
+
*/
|
|
150
|
+
function trapFocus(event) {
|
|
151
|
+
if (!modalElement) return
|
|
152
|
+
|
|
153
|
+
// Get all focusable elements
|
|
154
|
+
const focusableElements = modalElement.querySelectorAll(
|
|
155
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
if (focusableElements.length === 0) return
|
|
159
|
+
|
|
160
|
+
const firstElement = focusableElements[0]
|
|
161
|
+
const lastElement = focusableElements[focusableElements.length - 1]
|
|
162
|
+
|
|
163
|
+
// If shift+tab on first element, move to last element
|
|
164
|
+
if (event.shiftKey && document.activeElement === firstElement) {
|
|
165
|
+
event.preventDefault()
|
|
166
|
+
lastElement.focus()
|
|
167
|
+
}
|
|
168
|
+
// If tab on last element, move to first element
|
|
169
|
+
else if (!event.shiftKey && document.activeElement === lastElement) {
|
|
170
|
+
event.preventDefault()
|
|
171
|
+
firstElement.focus()
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Focuses the first focusable element in the modal
|
|
177
|
+
*/
|
|
178
|
+
function focusFirstElement() {
|
|
179
|
+
if (!modalElement) return
|
|
180
|
+
|
|
181
|
+
// Try to focus the first focusable element
|
|
182
|
+
const focusableElement = modalElement.querySelector(
|
|
183
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if (focusableElement) {
|
|
187
|
+
focusableElement.focus()
|
|
188
|
+
} else {
|
|
189
|
+
// If no focusable element, focus the modal itself
|
|
190
|
+
modalElement.focus()
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Clean up when component is destroyed
|
|
195
|
+
onMount(() => {
|
|
196
|
+
if (isOpen) {
|
|
197
|
+
openModal()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return () => {
|
|
201
|
+
if (isOpen) {
|
|
202
|
+
document.body.classList.remove("modal-open")
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
// Determine size classes
|
|
208
|
+
const sizeClasses = $derived(
|
|
209
|
+
{
|
|
210
|
+
sm: "max-w-sm",
|
|
211
|
+
md: "max-w-md",
|
|
212
|
+
lg: "max-w-lg",
|
|
213
|
+
xl: "max-w-xl",
|
|
214
|
+
"2xl": "max-w-2xl",
|
|
215
|
+
"3xl": "max-w-3xl",
|
|
216
|
+
"4xl": "max-w-4xl",
|
|
217
|
+
"5xl": "max-w-5xl",
|
|
218
|
+
"6xl": "max-w-6xl",
|
|
219
|
+
"7xl": "max-w-7xl",
|
|
220
|
+
full: "max-w-full",
|
|
221
|
+
}[size] || "max-w-md"
|
|
222
|
+
)
|
|
223
|
+
</script>
|
|
224
|
+
|
|
225
|
+
<svelte:window onkeydown={handleKeydown} />
|
|
226
|
+
|
|
227
|
+
{#if isOpen}
|
|
228
|
+
<div
|
|
229
|
+
class="modal-backdrop"
|
|
230
|
+
onclick={handleBackdropClick}
|
|
231
|
+
transition:fade={{ duration: 200 }}
|
|
232
|
+
>
|
|
233
|
+
<div
|
|
234
|
+
{id}
|
|
235
|
+
class="
|
|
236
|
+
modal
|
|
237
|
+
{sizeClasses}
|
|
238
|
+
{centered ? 'modal-centered' : ''}
|
|
239
|
+
{className}
|
|
240
|
+
"
|
|
241
|
+
role="dialog"
|
|
242
|
+
aria-modal="true"
|
|
243
|
+
aria-label={ariaLabel}
|
|
244
|
+
aria-describedby={ariaDescription ? `${id}-description` : undefined}
|
|
245
|
+
tabindex="-1"
|
|
246
|
+
bind:this={modalElement}
|
|
247
|
+
transition:scale={{ duration: 200, start: 0.95 }}
|
|
248
|
+
>
|
|
249
|
+
{#if header}
|
|
250
|
+
<div class="modal-header">
|
|
251
|
+
<div class="modal-title">
|
|
252
|
+
{@render header()}
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
{#if showCloseButton}
|
|
256
|
+
<button
|
|
257
|
+
type="button"
|
|
258
|
+
class="modal-close-button"
|
|
259
|
+
aria-label={closeButtonLabel}
|
|
260
|
+
onclick={() => {
|
|
261
|
+
isOpen = false;
|
|
262
|
+
closeModal();
|
|
263
|
+
}}
|
|
264
|
+
>
|
|
265
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
266
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
267
|
+
</svg>
|
|
268
|
+
</button>
|
|
269
|
+
{/if}
|
|
270
|
+
</div>
|
|
271
|
+
{/if}
|
|
272
|
+
|
|
273
|
+
<div class="modal-body" id={ariaDescription ? `${id}-description` : undefined}>
|
|
274
|
+
{@render children?.()}
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
{#if footer}
|
|
278
|
+
<div class="modal-footer">
|
|
279
|
+
{@render footer()}
|
|
280
|
+
</div>
|
|
281
|
+
{/if}
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
{/if}
|
|
285
|
+
|
|
286
|
+
<style>
|
|
287
|
+
@reference "../../twintrinsic.css";
|
|
288
|
+
|
|
289
|
+
/* Add global style to prevent body scrolling when modal is open */
|
|
290
|
+
:global(body.modal-open) {
|
|
291
|
+
@apply overflow-hidden;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.modal-backdrop {
|
|
295
|
+
@apply fixed inset-0 z-50;
|
|
296
|
+
@apply bg-black/50 dark:bg-black/60 backdrop-blur-sm;
|
|
297
|
+
@apply flex items-center justify-center p-4;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.modal {
|
|
301
|
+
@apply w-full relative;
|
|
302
|
+
@apply bg-background dark:bg-background text-text dark:text-text;
|
|
303
|
+
@apply rounded-lg shadow-lg overflow-hidden;
|
|
304
|
+
@apply flex flex-col max-h-[calc(100vh-2rem)];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.modal-centered {
|
|
308
|
+
@apply my-auto;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.modal-header {
|
|
312
|
+
@apply flex items-center justify-between p-4 sm:p-6;
|
|
313
|
+
@apply border-b border-border dark:border-border;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.modal-title {
|
|
317
|
+
@apply text-lg font-medium;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.modal-close-button {
|
|
321
|
+
@apply p-1 rounded-md text-muted dark:text-muted;
|
|
322
|
+
@apply hover:bg-hover dark:hover:bg-hover hover:text-text dark:hover:text-text;
|
|
323
|
+
@apply focus:outline-none focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.modal-body {
|
|
327
|
+
@apply p-4 sm:p-6 overflow-y-auto;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.modal-footer {
|
|
331
|
+
@apply flex items-center justify-end gap-3 p-4 sm:p-6;
|
|
332
|
+
@apply border-t border-border dark:border-border;
|
|
333
|
+
}
|
|
334
|
+
</style>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export default Modal;
|
|
2
|
+
type Modal = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Modal - A dialog component for displaying content that requires user attention.
|
|
8
|
+
* Provides accessible focus management, keyboard navigation, and backdrop interactions.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <Modal open={showModal} onclose={() => showModal = false}>
|
|
13
|
+
* <svelte:fragment slot="header">Modal Title</svelte:fragment>
|
|
14
|
+
* <p>Modal content goes here</p>
|
|
15
|
+
* <svelte:fragment slot="footer">
|
|
16
|
+
* <Button onclick={() => showModal = false}>Close</Button>
|
|
17
|
+
* <Button variant="primary">Save</Button>
|
|
18
|
+
* </svelte:fragment>
|
|
19
|
+
* </Modal>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare const Modal: import("svelte").Component<{
|
|
23
|
+
class?: string;
|
|
24
|
+
id?: any;
|
|
25
|
+
open?: boolean;
|
|
26
|
+
closeOnOutsideClick?: boolean;
|
|
27
|
+
closeOnEscape?: boolean;
|
|
28
|
+
size?: string;
|
|
29
|
+
centered?: boolean;
|
|
30
|
+
showCloseButton?: boolean;
|
|
31
|
+
closeButtonLabel?: string;
|
|
32
|
+
ariaLabel: any;
|
|
33
|
+
ariaDescription: any;
|
|
34
|
+
onclose: any;
|
|
35
|
+
children: any;
|
|
36
|
+
header: any;
|
|
37
|
+
footer: any;
|
|
38
|
+
}, {}, "">;
|
|
39
|
+
type $$ComponentProps = {
|
|
40
|
+
class?: string;
|
|
41
|
+
id?: any;
|
|
42
|
+
open?: boolean;
|
|
43
|
+
closeOnOutsideClick?: boolean;
|
|
44
|
+
closeOnEscape?: boolean;
|
|
45
|
+
size?: string;
|
|
46
|
+
centered?: boolean;
|
|
47
|
+
showCloseButton?: boolean;
|
|
48
|
+
closeButtonLabel?: string;
|
|
49
|
+
ariaLabel: any;
|
|
50
|
+
ariaDescription: any;
|
|
51
|
+
onclose: any;
|
|
52
|
+
children: any;
|
|
53
|
+
header: any;
|
|
54
|
+
footer: any;
|
|
55
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Card - A styled container for content with optional header and footer.
|
|
4
|
+
Built on top of the Panel component with additional card-specific styling.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<Card>
|
|
9
|
+
<svelte:fragment slot="header">Card Title</svelte:fragment>
|
|
10
|
+
<p>Card content</p>
|
|
11
|
+
<svelte:fragment slot="footer">Card footer</svelte:fragment>
|
|
12
|
+
</Card>
|
|
13
|
+
|
|
14
|
+
<Card hoverable>
|
|
15
|
+
<svelte:fragment slot="media">
|
|
16
|
+
<img src="image.jpg" alt="Card image" />
|
|
17
|
+
</svelte:fragment>
|
|
18
|
+
<h3>Title</h3>
|
|
19
|
+
<p>Content with hover effect</p>
|
|
20
|
+
</Card>
|
|
21
|
+
```
|
|
22
|
+
-->
|
|
23
|
+
<script>
|
|
24
|
+
import Panel from "./Panel.svelte"
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
/** @type {string} - Additional CSS classes */
|
|
28
|
+
class: className = "",
|
|
29
|
+
|
|
30
|
+
/** @type {string} - HTML id for accessibility */
|
|
31
|
+
id = crypto.randomUUID(),
|
|
32
|
+
|
|
33
|
+
/** @type {string} - ARIA label */
|
|
34
|
+
ariaLabel,
|
|
35
|
+
|
|
36
|
+
/** @type {boolean} - Whether to add hover effects */
|
|
37
|
+
hoverable = false,
|
|
38
|
+
|
|
39
|
+
/** @type {boolean} - Whether to add shadow */
|
|
40
|
+
shadow = true,
|
|
41
|
+
|
|
42
|
+
/** @type {"none" | "sm" | "md" | "lg" | "xl"} - Shadow size when shadow is true */
|
|
43
|
+
shadowSize = "md",
|
|
44
|
+
|
|
45
|
+
/** @type {boolean} - Whether the card is clickable */
|
|
46
|
+
clickable = false,
|
|
47
|
+
|
|
48
|
+
children,
|
|
49
|
+
header,
|
|
50
|
+
footer,
|
|
51
|
+
media,
|
|
52
|
+
} = $props()
|
|
53
|
+
|
|
54
|
+
// Handle click events if card is clickable
|
|
55
|
+
function handleClick(event) {
|
|
56
|
+
if (clickable) {
|
|
57
|
+
const customEvent = new CustomEvent("click", {
|
|
58
|
+
detail: { originalEvent: event },
|
|
59
|
+
bubbles: true,
|
|
60
|
+
})
|
|
61
|
+
this?.dispatchEvent(customEvent)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<div
|
|
67
|
+
class="
|
|
68
|
+
card
|
|
69
|
+
{shadow ? `shadow-${shadowSize}` : ''}
|
|
70
|
+
{hoverable ? 'card-hoverable' : ''}
|
|
71
|
+
{clickable ? 'card-clickable' : ''}
|
|
72
|
+
{className}
|
|
73
|
+
"
|
|
74
|
+
{id}
|
|
75
|
+
role={clickable ? 'button' : 'article'}
|
|
76
|
+
aria-label={ariaLabel}
|
|
77
|
+
onclick={handleClick}
|
|
78
|
+
onkeydown={event => {
|
|
79
|
+
if (clickable && (event.key === 'Enter' || event.key === ' ')) {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
handleClick(event);
|
|
82
|
+
}
|
|
83
|
+
}}
|
|
84
|
+
tabindex={clickable ? '0' : undefined}
|
|
85
|
+
>
|
|
86
|
+
{#if media}
|
|
87
|
+
<div class="card-media">
|
|
88
|
+
{@render media?.()}
|
|
89
|
+
</div>
|
|
90
|
+
{/if}
|
|
91
|
+
|
|
92
|
+
<Panel
|
|
93
|
+
expanded={true}
|
|
94
|
+
showIcon={false}
|
|
95
|
+
bordered={false}
|
|
96
|
+
>
|
|
97
|
+
<svelte:fragment slot="header">
|
|
98
|
+
{@render header?.()}
|
|
99
|
+
</svelte:fragment>
|
|
100
|
+
|
|
101
|
+
{@render children?.()}
|
|
102
|
+
</Panel>
|
|
103
|
+
|
|
104
|
+
{#if footer}
|
|
105
|
+
<div class="card-footer">
|
|
106
|
+
{@render footer?.()}
|
|
107
|
+
</div>
|
|
108
|
+
{/if}
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<style>
|
|
112
|
+
@reference "../../twintrinsic.css";
|
|
113
|
+
|
|
114
|
+
.card {
|
|
115
|
+
@apply bg-surface dark:bg-surface rounded-lg overflow-hidden;
|
|
116
|
+
@apply border border-border dark:border-border;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.card-hoverable {
|
|
120
|
+
@apply transition-all duration-200;
|
|
121
|
+
@apply hover:shadow-lg hover:-translate-y-1;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.card-clickable {
|
|
125
|
+
@apply cursor-pointer;
|
|
126
|
+
@apply focus:outline-none focus:ring-2 focus:ring-focus-ring;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.card-media {
|
|
130
|
+
@apply w-full overflow-hidden;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.card-media :global(img) {
|
|
134
|
+
@apply w-full h-full object-cover;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.card-footer {
|
|
138
|
+
@apply px-4 py-3 bg-muted/10 dark:bg-muted/10;
|
|
139
|
+
@apply border-t border-border dark:border-border;
|
|
140
|
+
}
|
|
141
|
+
</style>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export default Card;
|
|
2
|
+
type Card = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Card - A styled container for content with optional header and footer.
|
|
8
|
+
* Built on top of the Panel component with additional card-specific styling.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <Card>
|
|
13
|
+
* <svelte:fragment slot="header">Card Title</svelte:fragment>
|
|
14
|
+
* <p>Card content</p>
|
|
15
|
+
* <svelte:fragment slot="footer">Card footer</svelte:fragment>
|
|
16
|
+
* </Card>
|
|
17
|
+
*
|
|
18
|
+
* <Card hoverable>
|
|
19
|
+
* <svelte:fragment slot="media">
|
|
20
|
+
* <img src="image.jpg" alt="Card image" />
|
|
21
|
+
* </svelte:fragment>
|
|
22
|
+
* <h3>Title</h3>
|
|
23
|
+
* <p>Content with hover effect</p>
|
|
24
|
+
* </Card>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
declare const Card: import("svelte").Component<{
|
|
28
|
+
class?: string;
|
|
29
|
+
id?: any;
|
|
30
|
+
ariaLabel: any;
|
|
31
|
+
hoverable?: boolean;
|
|
32
|
+
shadow?: boolean;
|
|
33
|
+
shadowSize?: string;
|
|
34
|
+
clickable?: boolean;
|
|
35
|
+
children: any;
|
|
36
|
+
header: any;
|
|
37
|
+
footer: any;
|
|
38
|
+
media: any;
|
|
39
|
+
}, {}, "">;
|
|
40
|
+
type $$ComponentProps = {
|
|
41
|
+
class?: string;
|
|
42
|
+
id?: any;
|
|
43
|
+
ariaLabel: any;
|
|
44
|
+
hoverable?: boolean;
|
|
45
|
+
shadow?: boolean;
|
|
46
|
+
shadowSize?: string;
|
|
47
|
+
clickable?: boolean;
|
|
48
|
+
children: any;
|
|
49
|
+
header: any;
|
|
50
|
+
footer: any;
|
|
51
|
+
media: any;
|
|
52
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// import type { HstSvelte as Histoire } from '@histoire/plugin-svelte';
|
|
3
|
+
export let Hst
|
|
4
|
+
import Hero from "./Hero.svelte"
|
|
5
|
+
|
|
6
|
+
let heading = "This is a HERO!"
|
|
7
|
+
let slotContent =
|
|
8
|
+
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vel est nemo fugiat delectus perferendis adipisci maiores optio cupiditate hic ipsam dignissimos in quasi ea, minima, ut, reprehenderit quos tempora inventore."
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<Hst.Story title="Panels/Hero" layout={{ type: 'grid', width: '100%' }} icon="tabler:section">
|
|
12
|
+
<svelte:fragment slot="controls">
|
|
13
|
+
<Hst.Text bind:value={heading} title="Heading" />
|
|
14
|
+
<Hst.Text bind:value={slotContent} title="slotContent" />
|
|
15
|
+
</svelte:fragment>
|
|
16
|
+
|
|
17
|
+
<Hst.Variant title="Default Slot" source="<Hero>{slotContent}</Hero>">
|
|
18
|
+
<Hero>{slotContent}</Hero>
|
|
19
|
+
</Hst.Variant>
|
|
20
|
+
|
|
21
|
+
<Hst.Variant
|
|
22
|
+
title="With Heading"
|
|
23
|
+
source="<Hero heading='This is a HERO'>{slotContent}</Hero>"
|
|
24
|
+
>
|
|
25
|
+
<Hero {heading}>
|
|
26
|
+
{slotContent}
|
|
27
|
+
</Hero>
|
|
28
|
+
</Hst.Variant>
|
|
29
|
+
|
|
30
|
+
<Hst.Variant title="Success" source="<Hero type='success'>{slotContent}</Hero>">
|
|
31
|
+
<Hero type="success">{slotContent}</Hero>
|
|
32
|
+
</Hst.Variant>
|
|
33
|
+
|
|
34
|
+
<Hst.Variant title="Info" source="<Hero type='info'>{slotContent}</Hero>">
|
|
35
|
+
<Hero type="info">{slotContent}</Hero>
|
|
36
|
+
</Hst.Variant>
|
|
37
|
+
|
|
38
|
+
<Hst.Variant title="Warning" source="<Hero type='warning'>{slotContent}</Hero>">
|
|
39
|
+
<Hero type="warning">{slotContent}</Hero>
|
|
40
|
+
</Hst.Variant>
|
|
41
|
+
|
|
42
|
+
<Hst.Variant title="Failure" source="<Hero type='failure'>{slotContent}</Hero>">
|
|
43
|
+
<Hero type="failure">{slotContent}</Hero>
|
|
44
|
+
</Hst.Variant>
|
|
45
|
+
|
|
46
|
+
<Hst.Variant title="Danger" source="<Hero type='danger'>{slotContent}</Hero>">
|
|
47
|
+
<Hero type="danger">{slotContent}</Hero>
|
|
48
|
+
</Hst.Variant>
|
|
49
|
+
</Hst.Story>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Hero from "./Hero.svelte";
|
|
2
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
+
$$bindings?: Bindings;
|
|
5
|
+
} & Exports;
|
|
6
|
+
(internal: unknown, props: Props & {
|
|
7
|
+
$$events?: Events;
|
|
8
|
+
$$slots?: Slots;
|
|
9
|
+
}): Exports & {
|
|
10
|
+
$set?: any;
|
|
11
|
+
$on?: any;
|
|
12
|
+
};
|
|
13
|
+
z_$$bindings?: Bindings;
|
|
14
|
+
}
|
|
15
|
+
declare const Hero: $$__sveltets_2_IsomorphicComponent<{
|
|
16
|
+
Hst: any;
|
|
17
|
+
}, {
|
|
18
|
+
[evt: string]: CustomEvent<any>;
|
|
19
|
+
}, {}, {}, string>;
|
|
20
|
+
type Hero = InstanceType<typeof Hero>;
|
|
21
|
+
export default Hero;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Container from "../../Container/Container.svelte"
|
|
3
|
+
let propClasses = ""
|
|
4
|
+
export let heading: string | null = ""
|
|
5
|
+
export let type: TypeLevel = null
|
|
6
|
+
let typeClass = ""
|
|
7
|
+
$: typeClass = type ? `bg-${type}` : "bg-light"
|
|
8
|
+
export { propClasses as class }
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<Container as="section" {...$$restProps} class="twin-hero {typeClass} {propClasses} ">
|
|
12
|
+
<h1 slot="heading">{heading}</h1>
|
|
13
|
+
<slot></slot>
|
|
14
|
+
</Container>
|
|
15
|
+
|
|
16
|
+
<style>
|
|
17
|
+
@reference "../../../twintrinsic.css";
|
|
18
|
+
:global(.twin-hero) {
|
|
19
|
+
@apply p-4;
|
|
20
|
+
}
|
|
21
|
+
:global(.twin-hero h1) {
|
|
22
|
+
@apply text-4xl mb-3;
|
|
23
|
+
}
|
|
24
|
+
</style>
|