@stacksjs/components 0.2.3
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/README.md +477 -0
- package/dist/Accordion-x2gjedap.stx +108 -0
- package/dist/Audio-0fk6p9tn.stx +50 -0
- package/dist/Avatar-5m0v0s4x.stx +88 -0
- package/dist/Badge-ynes82vv.stx +70 -0
- package/dist/Breadcrumb-h9n427sk.stx +79 -0
- package/dist/Button-7xtrzn14.stx +89 -0
- package/dist/Calendar-egvx1pma.stx +232 -0
- package/dist/Card-r9w7vy1a.stx +74 -0
- package/dist/Checkbox-m8v46pmx.stx +92 -0
- package/dist/Checkout-vcnnr176.stx +39 -0
- package/dist/CodeBlock-mwex82cm.stx +57 -0
- package/dist/Combobox-npb08vkp.stx +13 -0
- package/dist/ComboboxButton-vcdyq4zw.stx +13 -0
- package/dist/ComboboxInput-c9d2p02m.stx +25 -0
- package/dist/ComboboxOption-7d0h9ash.stx +52 -0
- package/dist/ComboboxOptions-4dtqc0qa.stx +12 -0
- package/dist/CommandPalette-ah00bf8v.stx +39 -0
- package/dist/CommandPaletteItem-2yx0gvtj.stx +32 -0
- package/dist/DefaultPaymentMethod-emw5fg8s.stx +62 -0
- package/dist/Dialog-1w4rz7x0.stx +19 -0
- package/dist/DialogBackdrop-qbm3y5ab.stx +9 -0
- package/dist/DialogDescription-01w5j74b.stx +11 -0
- package/dist/DialogPanel-brsszrrs.stx +16 -0
- package/dist/DialogTitle-3rxhwg21.stx +11 -0
- package/dist/Drawer-yfaz8q7f.stx +137 -0
- package/dist/Dropdown-ng7nx556.stx +24 -0
- package/dist/DropdownButton-vc4zpyrb.stx +23 -0
- package/dist/DropdownItem-z491y23c.stx +43 -0
- package/dist/DropdownItems-7ykf29sj.stx +18 -0
- package/dist/EmailInput-bfshpj92.stx +41 -0
- package/dist/Footer-xdaf02nr.stx +66 -0
- package/dist/Form-z4f7w6r1.stx +209 -0
- package/dist/Hero-9vvp7pxf.stx +25 -0
- package/dist/Image-ktk0451c.stx +197 -0
- package/dist/Installation-33bj4rm7.stx +39 -0
- package/dist/Listbox-xcejcmyc.stx +14 -0
- package/dist/ListboxButton-nx5kmw19.stx +17 -0
- package/dist/ListboxLabel-aky8a73n.stx +11 -0
- package/dist/ListboxOption-6h4hsdz5.stx +57 -0
- package/dist/ListboxOptions-2sacdtm5.stx +16 -0
- package/dist/Login-s2jkvd9y.stx +163 -0
- package/dist/Navigator-tqqdgdkw.stx +138 -0
- package/dist/Notification-sry8egea.stx +71 -0
- package/dist/NumberInput-zm93ddfw.stx +138 -0
- package/dist/Pagination-yza9v4qw.stx +183 -0
- package/dist/PasswordInput-4xnndp5y.stx +155 -0
- package/dist/PaymentMethods-jxba6kek.stx +113 -0
- package/dist/Popover-scbqatjq.stx +24 -0
- package/dist/PopoverButton-rbxhzj4k.stx +23 -0
- package/dist/PopoverPanel-9ng1rm6e.stx +23 -0
- package/dist/Portal-j111yvqp.stx +74 -0
- package/dist/Progress-gbmw3akr.stx +142 -0
- package/dist/Radio-1n9qamv1.stx +81 -0
- package/dist/RadioGroup-12dw2vhk.stx +13 -0
- package/dist/RadioGroupDescription-bjm3mdht.stx +11 -0
- package/dist/RadioGroupLabel-aky8a73n.stx +11 -0
- package/dist/RadioGroupOption-9pcxgwmd.stx +51 -0
- package/dist/SearchInput-g240e2nq.stx +63 -0
- package/dist/Select-p9f94b20.stx +89 -0
- package/dist/Sidebar-gwxw8q38.stx +288 -0
- package/dist/SidebarFooter-887kxret.stx +95 -0
- package/dist/SidebarHeader-qjeyx0hv.stx +100 -0
- package/dist/SidebarItem-4dpwtmb4.stx +118 -0
- package/dist/SidebarSection-kh92fk4m.stx +108 -0
- package/dist/Signup-3nxq5zag.stx +161 -0
- package/dist/Skeleton-g0nx1njm.stx +48 -0
- package/dist/Spinner-w4zj58xp.stx +109 -0
- package/dist/Stepper-rwc5actp.stx +17 -0
- package/dist/StepperStep-h0k657nm.stx +53 -0
- package/dist/SubscriptionCheckout-4cbxdg7w.stx +353 -0
- package/dist/Switch-kjq8qd2p.stx +74 -0
- package/dist/Table-59j9qwp0.stx +14 -0
- package/dist/TableBody-reka2gwv.stx +10 -0
- package/dist/TableCell-j7nh4sgr.stx +10 -0
- package/dist/TableHead-ecbrwpqm.stx +10 -0
- package/dist/TableHeader-jn2shwsv.stx +13 -0
- package/dist/TableRow-xz8rrxhc.stx +11 -0
- package/dist/Tabs-kb6ex6m7.stx +145 -0
- package/dist/Teleport-819cg9zm.stx +101 -0
- package/dist/TextInput-zx9kgr79.stx +146 -0
- package/dist/Textarea-5xvmrb0z.stx +119 -0
- package/dist/Tooltip-5ehqsy49.stx +76 -0
- package/dist/Transition-2j39s044.stx +174 -0
- package/dist/TwoFactorChallenge-2q4gg89m.stx +125 -0
- package/dist/Video-6aq4shh4.stx +45 -0
- package/dist/VirtualList-weh5qtm1.stx +83 -0
- package/dist/VirtualTable-59a1eskc.stx +113 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/composables/index.d.ts +4 -0
- package/dist/composables/index.js +3 -0
- package/dist/composables/useCopyCode.d.ts +19 -0
- package/dist/composables/useDarkMode.d.ts +25 -0
- package/dist/composables/useSEO.d.ts +36 -0
- package/dist/composables/utilities/index.d.ts +5 -0
- package/dist/composables/utilities/useClickOutside.d.ts +28 -0
- package/dist/composables/utilities/useFocusTrap.d.ts +34 -0
- package/dist/composables/utilities/useKeyboard.d.ts +55 -0
- package/dist/composables/utilities/useLocalStorage.d.ts +33 -0
- package/dist/composables/utilities/useMediaQuery.d.ts +64 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +19 -0
- package/dist/ui/accordion/index.d.ts +12 -0
- package/dist/ui/audio/index.d.ts +12 -0
- package/dist/ui/auth/index.d.ts +29 -0
- package/dist/ui/avatar/index.d.ts +11 -0
- package/dist/ui/badge/index.d.ts +9 -0
- package/dist/ui/breadcrumb/index.d.ts +13 -0
- package/dist/ui/button/index.d.ts +45 -0
- package/dist/ui/calendar/index.d.ts +13 -0
- package/dist/ui/card/index.d.ts +12 -0
- package/dist/ui/checkbox/index.d.ts +15 -0
- package/dist/ui/combobox/index.d.ts +35 -0
- package/dist/ui/command-palette/index.d.ts +13 -0
- package/dist/ui/dialog/index.d.ts +27 -0
- package/dist/ui/drawer/index.d.ts +8 -0
- package/dist/ui/dropdown/index.d.ts +25 -0
- package/dist/ui/form/index.d.ts +48 -0
- package/dist/ui/image/index.d.ts +21 -0
- package/dist/ui/input/index.d.ts +73 -0
- package/dist/ui/listbox/index.d.ts +34 -0
- package/dist/ui/navigator/index.d.ts +18 -0
- package/dist/ui/notification/index.d.ts +11 -0
- package/dist/ui/pagination/index.d.ts +10 -0
- package/dist/ui/payment/index.d.ts +53 -0
- package/dist/ui/popover/index.d.ts +19 -0
- package/dist/ui/portal/index.d.ts +5 -0
- package/dist/ui/progress/index.d.ts +11 -0
- package/dist/ui/radio/index.d.ts +14 -0
- package/dist/ui/radio-group/index.d.ts +26 -0
- package/dist/ui/select/index.d.ts +19 -0
- package/dist/ui/sidebar/index.d.ts +129 -0
- package/dist/ui/skeleton/index.d.ts +10 -0
- package/dist/ui/spinner/index.d.ts +8 -0
- package/dist/ui/stepper/index.d.ts +17 -0
- package/dist/ui/storage/index.d.ts +40 -0
- package/dist/ui/switch/index.d.ts +9 -0
- package/dist/ui/table/index.d.ts +29 -0
- package/dist/ui/tabs/index.d.ts +14 -0
- package/dist/ui/teleport/index.d.ts +6 -0
- package/dist/ui/textarea/index.d.ts +19 -0
- package/dist/ui/tooltip/index.d.ts +9 -0
- package/dist/ui/transition/index.d.ts +24 -0
- package/dist/ui/video/index.d.ts +15 -0
- package/dist/ui/virtual-list/index.d.ts +9 -0
- package/dist/ui/virtual-table/index.d.ts +19 -0
- package/dist/utils/accessibility.d.ts +285 -0
- package/dist/utils/animation/easing.d.ts +112 -0
- package/dist/utils/animation/helpers.d.ts +119 -0
- package/dist/utils/animation/index.d.ts +3 -0
- package/dist/utils/animation/keyframes.d.ts +334 -0
- package/dist/utils/error-handling.d.ts +182 -0
- package/dist/utils/highlighter.d.ts +27 -0
- package/dist/utils/highlighter.js +2 -0
- package/dist/utils/i18n.d.ts +245 -0
- package/dist/utils/keep-alive.d.ts +171 -0
- package/dist/utils/lazy-loading.d.ts +174 -0
- package/dist/utils/prop-validation.d.ts +106 -0
- package/dist/utils/theme.d.ts +234 -0
- package/package.json +69 -0
- package/src/components/CodeBlock.stx +57 -0
- package/src/components/Footer.stx +66 -0
- package/src/components/Hero.stx +25 -0
- package/src/components/Installation.stx +39 -0
- package/src/components/index.ts +4 -0
- package/src/composables/index.ts +4 -0
- package/src/composables/useCopyCode.ts +73 -0
- package/src/composables/useDarkMode.ts +100 -0
- package/src/composables/useSEO.ts +229 -0
- package/src/composables/utilities/index.ts +7 -0
- package/src/composables/utilities/useClickOutside.ts +72 -0
- package/src/composables/utilities/useFocusTrap.ts +159 -0
- package/src/composables/utilities/useKeyboard.ts +124 -0
- package/src/composables/utilities/useLocalStorage.ts +132 -0
- package/src/composables/utilities/useMediaQuery.ts +118 -0
- package/src/index.ts +99 -0
- package/src/stx.d.ts +4 -0
- package/src/ui/accordion/Accordion.stx +108 -0
- package/src/ui/accordion/index.ts +14 -0
- package/src/ui/audio/Audio.stx +50 -0
- package/src/ui/audio/README.md +42 -0
- package/src/ui/audio/index.ts +13 -0
- package/src/ui/auth/Login.stx +163 -0
- package/src/ui/auth/README.md +145 -0
- package/src/ui/auth/Signup.stx +161 -0
- package/src/ui/auth/TwoFactorChallenge.stx +125 -0
- package/src/ui/auth/index.ts +32 -0
- package/src/ui/avatar/Avatar.stx +88 -0
- package/src/ui/avatar/index.ts +12 -0
- package/src/ui/badge/Badge.stx +70 -0
- package/src/ui/badge/index.ts +10 -0
- package/src/ui/breadcrumb/Breadcrumb.stx +79 -0
- package/src/ui/breadcrumb/index.ts +15 -0
- package/src/ui/button/Button.stx +89 -0
- package/src/ui/button/README.md +100 -0
- package/src/ui/button/index.ts +85 -0
- package/src/ui/calendar/Calendar.stx +232 -0
- package/src/ui/calendar/README.md +254 -0
- package/src/ui/calendar/index.ts +14 -0
- package/src/ui/card/Card.stx +74 -0
- package/src/ui/card/index.ts +13 -0
- package/src/ui/checkbox/Checkbox.stx +92 -0
- package/src/ui/checkbox/index.ts +16 -0
- package/src/ui/combobox/Combobox.stx +13 -0
- package/src/ui/combobox/ComboboxButton.stx +13 -0
- package/src/ui/combobox/ComboboxInput.stx +25 -0
- package/src/ui/combobox/ComboboxOption.stx +52 -0
- package/src/ui/combobox/ComboboxOptions.stx +12 -0
- package/src/ui/combobox/README.md +154 -0
- package/src/ui/combobox/index.ts +40 -0
- package/src/ui/command-palette/CommandPalette.stx +39 -0
- package/src/ui/command-palette/CommandPaletteItem.stx +32 -0
- package/src/ui/command-palette/README.md +42 -0
- package/src/ui/command-palette/index.ts +15 -0
- package/src/ui/dialog/Dialog.stx +19 -0
- package/src/ui/dialog/DialogBackdrop.stx +9 -0
- package/src/ui/dialog/DialogDescription.stx +11 -0
- package/src/ui/dialog/DialogPanel.stx +16 -0
- package/src/ui/dialog/DialogTitle.stx +11 -0
- package/src/ui/dialog/README.md +133 -0
- package/src/ui/dialog/index.ts +32 -0
- package/src/ui/drawer/Drawer.stx +137 -0
- package/src/ui/drawer/README.md +110 -0
- package/src/ui/drawer/index.ts +9 -0
- package/src/ui/dropdown/Dropdown.stx +24 -0
- package/src/ui/dropdown/DropdownButton.stx +23 -0
- package/src/ui/dropdown/DropdownItem.stx +43 -0
- package/src/ui/dropdown/DropdownItems.stx +18 -0
- package/src/ui/dropdown/README.md +142 -0
- package/src/ui/dropdown/index.ts +29 -0
- package/src/ui/form/Form.stx +209 -0
- package/src/ui/form/index.ts +67 -0
- package/src/ui/heatmap/Heatmap.stx +315 -0
- package/src/ui/image/Image.stx +197 -0
- package/src/ui/image/README.md +171 -0
- package/src/ui/image/index.ts +22 -0
- package/src/ui/input/EmailInput.stx +41 -0
- package/src/ui/input/NumberInput.stx +138 -0
- package/src/ui/input/PasswordInput.stx +155 -0
- package/src/ui/input/SearchInput.stx +63 -0
- package/src/ui/input/TextInput.stx +146 -0
- package/src/ui/input/index.ts +180 -0
- package/src/ui/listbox/Listbox.stx +14 -0
- package/src/ui/listbox/ListboxButton.stx +17 -0
- package/src/ui/listbox/ListboxLabel.stx +11 -0
- package/src/ui/listbox/ListboxOption.stx +57 -0
- package/src/ui/listbox/ListboxOptions.stx +16 -0
- package/src/ui/listbox/README.md +143 -0
- package/src/ui/listbox/index.ts +39 -0
- package/src/ui/navigator/Navigator.stx +138 -0
- package/src/ui/navigator/README.md +352 -0
- package/src/ui/navigator/index.ts +20 -0
- package/src/ui/notification/Notification.stx +71 -0
- package/src/ui/notification/README.md +118 -0
- package/src/ui/notification/index.ts +12 -0
- package/src/ui/pagination/Pagination.stx +183 -0
- package/src/ui/pagination/index.ts +11 -0
- package/src/ui/payment/Checkout.stx +39 -0
- package/src/ui/payment/DefaultPaymentMethod.stx +62 -0
- package/src/ui/payment/PaymentMethods.stx +113 -0
- package/src/ui/payment/README.md +427 -0
- package/src/ui/payment/SubscriptionCheckout.stx +353 -0
- package/src/ui/payment/index.ts +59 -0
- package/src/ui/popover/Popover.stx +24 -0
- package/src/ui/popover/PopoverButton.stx +23 -0
- package/src/ui/popover/PopoverPanel.stx +23 -0
- package/src/ui/popover/README.md +95 -0
- package/src/ui/popover/index.ts +22 -0
- package/src/ui/portal/Portal.stx +74 -0
- package/src/ui/portal/index.ts +6 -0
- package/src/ui/progress/Progress.stx +142 -0
- package/src/ui/progress/index.ts +12 -0
- package/src/ui/radio/Radio.stx +81 -0
- package/src/ui/radio/index.ts +15 -0
- package/src/ui/radio-group/README.md +145 -0
- package/src/ui/radio-group/RadioGroup.stx +13 -0
- package/src/ui/radio-group/RadioGroupDescription.stx +11 -0
- package/src/ui/radio-group/RadioGroupLabel.stx +11 -0
- package/src/ui/radio-group/RadioGroupOption.stx +51 -0
- package/src/ui/radio-group/index.ts +30 -0
- package/src/ui/select/README.md +204 -0
- package/src/ui/select/Select.stx +89 -0
- package/src/ui/select/index.ts +21 -0
- package/src/ui/sidebar/Sidebar.stx +288 -0
- package/src/ui/sidebar/SidebarFooter.stx +95 -0
- package/src/ui/sidebar/SidebarHeader.stx +100 -0
- package/src/ui/sidebar/SidebarItem.stx +118 -0
- package/src/ui/sidebar/SidebarSection.stx +108 -0
- package/src/ui/sidebar/index.ts +175 -0
- package/src/ui/skeleton/Skeleton.stx +48 -0
- package/src/ui/skeleton/index.ts +11 -0
- package/src/ui/spinner/Spinner.stx +109 -0
- package/src/ui/spinner/index.ts +9 -0
- package/src/ui/stepper/README.md +36 -0
- package/src/ui/stepper/Stepper.stx +17 -0
- package/src/ui/stepper/StepperStep.stx +53 -0
- package/src/ui/stepper/index.ts +19 -0
- package/src/ui/storage/README.md +193 -0
- package/src/ui/storage/index.ts +347 -0
- package/src/ui/switch/README.md +133 -0
- package/src/ui/switch/Switch.stx +74 -0
- package/src/ui/switch/index.ts +10 -0
- package/src/ui/table/README.md +39 -0
- package/src/ui/table/Table.stx +14 -0
- package/src/ui/table/TableBody.stx +10 -0
- package/src/ui/table/TableCell.stx +10 -0
- package/src/ui/table/TableHead.stx +10 -0
- package/src/ui/table/TableHeader.stx +13 -0
- package/src/ui/table/TableRow.stx +11 -0
- package/src/ui/table/index.ts +35 -0
- package/src/ui/tabs/Tabs.stx +145 -0
- package/src/ui/tabs/index.ts +16 -0
- package/src/ui/teleport/Teleport.stx +101 -0
- package/src/ui/teleport/index.ts +7 -0
- package/src/ui/textarea/Textarea.stx +119 -0
- package/src/ui/textarea/index.ts +20 -0
- package/src/ui/tooltip/Tooltip.stx +76 -0
- package/src/ui/tooltip/index.ts +10 -0
- package/src/ui/transition/README.md +30 -0
- package/src/ui/transition/Transition.stx +174 -0
- package/src/ui/transition/index.ts +25 -0
- package/src/ui/video/README.md +48 -0
- package/src/ui/video/Video.stx +45 -0
- package/src/ui/video/index.ts +16 -0
- package/src/ui/virtual-list/VirtualList.stx +83 -0
- package/src/ui/virtual-list/index.ts +10 -0
- package/src/ui/virtual-table/VirtualTable.stx +113 -0
- package/src/ui/virtual-table/index.ts +21 -0
- package/src/utils/PROP_VALIDATION.md +458 -0
- package/src/utils/THEME_SYSTEM.md +702 -0
- package/src/utils/accessibility.ts +841 -0
- package/src/utils/animation/easing.ts +368 -0
- package/src/utils/animation/helpers.ts +223 -0
- package/src/utils/animation/index.ts +5 -0
- package/src/utils/animation/keyframes.ts +230 -0
- package/src/utils/error-handling.ts +473 -0
- package/src/utils/highlighter.ts +93 -0
- package/src/utils/i18n.ts +634 -0
- package/src/utils/keep-alive.ts +818 -0
- package/src/utils/lazy-loading.ts +530 -0
- package/src/utils/prop-validation.ts +478 -0
- package/src/utils/theme.ts +626 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const value = $props.value || ''
|
|
3
|
+
export const onChange = $props.onChange || (() => {})
|
|
4
|
+
export const placeholder = $props.placeholder || ''
|
|
5
|
+
export const disabled = $props.disabled || false
|
|
6
|
+
export const readonly = $props.readonly || false
|
|
7
|
+
export const rows = $props.rows || 4
|
|
8
|
+
export const autoResize = $props.autoResize || false
|
|
9
|
+
export const maxRows = $props.maxRows || 10
|
|
10
|
+
export const error = $props.error || false
|
|
11
|
+
export const label = $props.label || ''
|
|
12
|
+
export const helperText = $props.helperText || ''
|
|
13
|
+
export const required = $props.required || false
|
|
14
|
+
export const maxLength = $props.maxLength
|
|
15
|
+
export const showCount = $props.showCount || false
|
|
16
|
+
export const resize = $props.resize || 'vertical' // 'none' | 'vertical' | 'horizontal' | 'both'
|
|
17
|
+
export const className = $props.className || ''
|
|
18
|
+
|
|
19
|
+
// Resize classes
|
|
20
|
+
const resizeClasses = {
|
|
21
|
+
none: 'resize-none',
|
|
22
|
+
vertical: 'resize-y',
|
|
23
|
+
horizontal: 'resize-x',
|
|
24
|
+
both: 'resize',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Base and state classes
|
|
28
|
+
const baseClasses = 'block w-full border-0 rounded-md shadow-sm ring-1 ring-inset transition-colors duration-200 focus:ring-2 focus:ring-inset px-4 py-2 text-base dark:bg-blue-gray-700 dark:text-gray-100'
|
|
29
|
+
|
|
30
|
+
const stateClasses = error
|
|
31
|
+
? 'ring-red-300 dark:ring-red-600 text-red-900 dark:text-red-300 placeholder-red-300 dark:placeholder-red-500 focus:ring-red-500 dark:focus:ring-red-400'
|
|
32
|
+
: 'ring-gray-300 dark:ring-gray-600 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:ring-indigo-600 dark:focus:ring-indigo-400'
|
|
33
|
+
|
|
34
|
+
const disabledClasses = disabled
|
|
35
|
+
? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
|
|
36
|
+
: readonly
|
|
37
|
+
? 'bg-gray-50 dark:bg-gray-800 cursor-default'
|
|
38
|
+
: 'bg-white dark:bg-blue-gray-700'
|
|
39
|
+
|
|
40
|
+
export const textareaClasses = `${baseClasses} ${stateClasses} ${disabledClasses} ${resizeClasses[resize]} ${className}`.trim()
|
|
41
|
+
export const labelClasses = `block text-sm font-medium leading-6 mb-2 ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-gray-100'}`.trim()
|
|
42
|
+
export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<div class="w-full">
|
|
46
|
+
@if(label)
|
|
47
|
+
<label for="textarea-input" class="{{ labelClasses }}">
|
|
48
|
+
{{ label }}
|
|
49
|
+
@if(required)
|
|
50
|
+
<span class="text-red-500 ml-1">*</span>
|
|
51
|
+
@endif
|
|
52
|
+
</label>
|
|
53
|
+
@endif
|
|
54
|
+
|
|
55
|
+
<textarea
|
|
56
|
+
id="textarea-input"
|
|
57
|
+
class="{{ textareaClasses }}"
|
|
58
|
+
rows="{{ rows }}"
|
|
59
|
+
placeholder="{{ placeholder }}"
|
|
60
|
+
disabled="{{ disabled }}"
|
|
61
|
+
readonly="{{ readonly }}"
|
|
62
|
+
required="{{ required }}"
|
|
63
|
+
@if(maxLength)maxlength="{{ maxLength }}"@endif
|
|
64
|
+
oninput="handleInput(event)"
|
|
65
|
+
aria-invalid="{{ error ? 'true' : 'false' }}"
|
|
66
|
+
@if(helperText)aria-describedby="helper-text"@endif
|
|
67
|
+
>{{ value }}</textarea>
|
|
68
|
+
|
|
69
|
+
@if(helperText || (showCount && maxLength))
|
|
70
|
+
<div class="flex items-center justify-between mt-2">
|
|
71
|
+
@if(helperText)
|
|
72
|
+
<p id="helper-text" class="{{ helperClasses }}">
|
|
73
|
+
{{ helperText }}
|
|
74
|
+
</p>
|
|
75
|
+
@else
|
|
76
|
+
<span></span>
|
|
77
|
+
@endif
|
|
78
|
+
|
|
79
|
+
@if(showCount && maxLength)
|
|
80
|
+
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
81
|
+
{{ value.length }} / {{ maxLength }}
|
|
82
|
+
</p>
|
|
83
|
+
@endif
|
|
84
|
+
</div>
|
|
85
|
+
@endif
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<script>
|
|
89
|
+
function handleInput(event) {
|
|
90
|
+
const textarea = event.target
|
|
91
|
+
const newValue = textarea.value
|
|
92
|
+
|
|
93
|
+
if (autoResize && typeof window !== 'undefined') {
|
|
94
|
+
// Auto-resize functionality
|
|
95
|
+
textarea.style.height = 'auto'
|
|
96
|
+
const scrollHeight = textarea.scrollHeight
|
|
97
|
+
const lineHeight = parseInt(window.getComputedStyle(textarea).lineHeight)
|
|
98
|
+
const maxHeight = lineHeight * maxRows
|
|
99
|
+
|
|
100
|
+
textarea.style.height = Math.min(scrollHeight, maxHeight) + 'px'
|
|
101
|
+
textarea.style.overflowY = scrollHeight > maxHeight ? 'auto' : 'hidden'
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (onChange) {
|
|
105
|
+
onChange(newValue)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Initialize auto-resize on mount
|
|
110
|
+
if (autoResize && typeof window !== 'undefined') {
|
|
111
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
112
|
+
const textarea = document.getElementById('textarea-input')
|
|
113
|
+
if (textarea && value) {
|
|
114
|
+
// Trigger initial resize
|
|
115
|
+
textarea.dispatchEvent(new Event('input'))
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
</script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { default as Textarea } from './Textarea.stx'
|
|
2
|
+
|
|
3
|
+
export interface TextareaProps {
|
|
4
|
+
value?: string
|
|
5
|
+
onChange?: (value: string) => void
|
|
6
|
+
placeholder?: string
|
|
7
|
+
disabled?: boolean
|
|
8
|
+
readonly?: boolean
|
|
9
|
+
rows?: number
|
|
10
|
+
autoResize?: boolean
|
|
11
|
+
maxRows?: number
|
|
12
|
+
error?: boolean
|
|
13
|
+
label?: string
|
|
14
|
+
helperText?: string
|
|
15
|
+
required?: boolean
|
|
16
|
+
maxLength?: number
|
|
17
|
+
showCount?: boolean
|
|
18
|
+
resize?: 'none' | 'vertical' | 'horizontal' | 'both'
|
|
19
|
+
className?: string
|
|
20
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const content = $props.content || ''
|
|
3
|
+
export const position = $props.position || 'top'
|
|
4
|
+
export const delay = $props.delay || 0
|
|
5
|
+
export const className = $props.className || ''
|
|
6
|
+
export const show = $props.show || false
|
|
7
|
+
export const disabled = $props.disabled || false
|
|
8
|
+
|
|
9
|
+
// Position classes
|
|
10
|
+
const positionClasses = {
|
|
11
|
+
top: 'bottom-full left-1/2 -translate-x-1/2 mb-2',
|
|
12
|
+
bottom: 'top-full left-1/2 -translate-x-1/2 mt-2',
|
|
13
|
+
left: 'right-full top-1/2 -translate-y-1/2 mr-2',
|
|
14
|
+
right: 'left-full top-1/2 -translate-y-1/2 ml-2',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Arrow classes
|
|
18
|
+
const arrowClasses = {
|
|
19
|
+
top: 'top-full left-1/2 -translate-x-1/2 border-t-gray-900 dark:border-t-gray-700',
|
|
20
|
+
bottom: 'bottom-full left-1/2 -translate-x-1/2 border-b-gray-900 dark:border-b-gray-700',
|
|
21
|
+
left: 'left-full top-1/2 -translate-y-1/2 border-l-gray-900 dark:border-l-gray-700',
|
|
22
|
+
right: 'right-full top-1/2 -translate-y-1/2 border-r-gray-900 dark:border-r-gray-700',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const tooltipClasses = `absolute z-50 px-3 py-2 text-sm font-medium text-white bg-gray-900 dark:bg-gray-700 rounded-lg shadow-lg whitespace-nowrap pointer-events-none opacity-0 transition-opacity duration-200 ${positionClasses[position]} ${className}`.trim()
|
|
26
|
+
export const arrowClass = `absolute w-0 h-0 border-4 border-transparent ${arrowClasses[position]}`.trim()
|
|
27
|
+
|
|
28
|
+
let isVisible = show
|
|
29
|
+
let timeoutId = null
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<div class="relative inline-block {{ disabled ? 'cursor-not-allowed' : '' }}">
|
|
33
|
+
<!-- Trigger element (slot) -->
|
|
34
|
+
<div
|
|
35
|
+
onmouseenter="handleMouseEnter()"
|
|
36
|
+
onmouseleave="handleMouseLeave()"
|
|
37
|
+
onfocus="handleMouseEnter()"
|
|
38
|
+
onblur="handleMouseLeave()"
|
|
39
|
+
>
|
|
40
|
+
<slot />
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<!-- Tooltip -->
|
|
44
|
+
@if(isVisible && !disabled)
|
|
45
|
+
<div
|
|
46
|
+
class="{{ tooltipClasses }}"
|
|
47
|
+
role="tooltip"
|
|
48
|
+
style="opacity: 1"
|
|
49
|
+
>
|
|
50
|
+
{{ content }}
|
|
51
|
+
<div class="{{ arrowClass }}"></div>
|
|
52
|
+
</div>
|
|
53
|
+
@endif
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<script>
|
|
57
|
+
function handleMouseEnter() {
|
|
58
|
+
if (disabled) return
|
|
59
|
+
|
|
60
|
+
if (delay > 0) {
|
|
61
|
+
timeoutId = setTimeout(() => {
|
|
62
|
+
isVisible = true
|
|
63
|
+
}, delay)
|
|
64
|
+
} else {
|
|
65
|
+
isVisible = true
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function handleMouseLeave() {
|
|
70
|
+
if (timeoutId) {
|
|
71
|
+
clearTimeout(timeoutId)
|
|
72
|
+
timeoutId = null
|
|
73
|
+
}
|
|
74
|
+
isVisible = false
|
|
75
|
+
}
|
|
76
|
+
</script>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Transition Component
|
|
2
|
+
|
|
3
|
+
Smooth enter/leave transitions for showing and hiding content.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @stacksjs/components
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```stx
|
|
14
|
+
<script>
|
|
15
|
+
export let isOpen = true
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
@component('Transition', { show: isOpen })
|
|
19
|
+
<div class="p-4 bg-white rounded-lg shadow-lg">
|
|
20
|
+
Content with fade transition
|
|
21
|
+
</div>
|
|
22
|
+
@endcomponent
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- Enter/leave animations
|
|
28
|
+
- Customizable transition classes
|
|
29
|
+
- Dark mode support
|
|
30
|
+
- Headwind utility classes
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const show = $props.show !== false
|
|
3
|
+
export const enter = $props.enter || 'transition-opacity duration-300'
|
|
4
|
+
export const enterFrom = $props.enterFrom || 'opacity-0'
|
|
5
|
+
export const enterTo = $props.enterTo || 'opacity-100'
|
|
6
|
+
export const leave = $props.leave || 'transition-opacity duration-200'
|
|
7
|
+
export const leaveFrom = $props.leaveFrom || 'opacity-100'
|
|
8
|
+
export const leaveTo = $props.leaveTo || 'opacity-0'
|
|
9
|
+
export const appear = $props.appear || false
|
|
10
|
+
export const appearFrom = $props.appearFrom || enterFrom
|
|
11
|
+
export const appearTo = $props.appearTo || enterTo
|
|
12
|
+
export const mode = $props.mode || 'in-out' // 'in-out' | 'out-in' | 'default'
|
|
13
|
+
export const duration = $props.duration || 300
|
|
14
|
+
export const delay = $props.delay || 0
|
|
15
|
+
export const preset = $props.preset || ''
|
|
16
|
+
export const onBeforeEnter = $props.onBeforeEnter
|
|
17
|
+
export const onEnter = $props.onEnter
|
|
18
|
+
export const onAfterEnter = $props.onAfterEnter
|
|
19
|
+
export const onBeforeLeave = $props.onBeforeLeave
|
|
20
|
+
export const onLeave = $props.onLeave
|
|
21
|
+
export const onAfterLeave = $props.onAfterLeave
|
|
22
|
+
export const className = $props.className || ''
|
|
23
|
+
|
|
24
|
+
// Preset transitions
|
|
25
|
+
const presets = {
|
|
26
|
+
fade: {
|
|
27
|
+
enter: 'transition-opacity',
|
|
28
|
+
enterFrom: 'opacity-0',
|
|
29
|
+
enterTo: 'opacity-100',
|
|
30
|
+
leave: 'transition-opacity',
|
|
31
|
+
leaveFrom: 'opacity-100',
|
|
32
|
+
leaveTo: 'opacity-0',
|
|
33
|
+
},
|
|
34
|
+
slide: {
|
|
35
|
+
enter: 'transition-transform',
|
|
36
|
+
enterFrom: 'translate-y-2',
|
|
37
|
+
enterTo: 'translate-y-0',
|
|
38
|
+
leave: 'transition-transform',
|
|
39
|
+
leaveFrom: 'translate-y-0',
|
|
40
|
+
leaveTo: 'translate-y-2',
|
|
41
|
+
},
|
|
42
|
+
slideLeft: {
|
|
43
|
+
enter: 'transition-transform',
|
|
44
|
+
enterFrom: 'translate-x-full',
|
|
45
|
+
enterTo: 'translate-x-0',
|
|
46
|
+
leave: 'transition-transform',
|
|
47
|
+
leaveFrom: 'translate-x-0',
|
|
48
|
+
leaveTo: '-translate-x-full',
|
|
49
|
+
},
|
|
50
|
+
slideRight: {
|
|
51
|
+
enter: 'transition-transform',
|
|
52
|
+
enterFrom: '-translate-x-full',
|
|
53
|
+
enterTo: 'translate-x-0',
|
|
54
|
+
leave: 'transition-transform',
|
|
55
|
+
leaveFrom: 'translate-x-0',
|
|
56
|
+
leaveTo: 'translate-x-full',
|
|
57
|
+
},
|
|
58
|
+
scale: {
|
|
59
|
+
enter: 'transition-transform',
|
|
60
|
+
enterFrom: 'scale-95',
|
|
61
|
+
enterTo: 'scale-100',
|
|
62
|
+
leave: 'transition-transform',
|
|
63
|
+
leaveFrom: 'scale-100',
|
|
64
|
+
leaveTo: 'scale-95',
|
|
65
|
+
},
|
|
66
|
+
rotate: {
|
|
67
|
+
enter: 'transition-transform',
|
|
68
|
+
enterFrom: 'rotate-12 scale-95',
|
|
69
|
+
enterTo: 'rotate-0 scale-100',
|
|
70
|
+
leave: 'transition-transform',
|
|
71
|
+
leaveFrom: 'rotate-0 scale-100',
|
|
72
|
+
leaveTo: '-rotate-12 scale-95',
|
|
73
|
+
},
|
|
74
|
+
zoom: {
|
|
75
|
+
enter: 'transition-all',
|
|
76
|
+
enterFrom: 'scale-0 opacity-0',
|
|
77
|
+
enterTo: 'scale-100 opacity-100',
|
|
78
|
+
leave: 'transition-all',
|
|
79
|
+
leaveFrom: 'scale-100 opacity-100',
|
|
80
|
+
leaveTo: 'scale-0 opacity-0',
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Apply preset if specified
|
|
85
|
+
const presetTransition = preset && presets[preset] ? presets[preset] : {}
|
|
86
|
+
const finalEnter = presetTransition.enter || enter
|
|
87
|
+
const finalEnterFrom = presetTransition.enterFrom || enterFrom
|
|
88
|
+
const finalEnterTo = presetTransition.enterTo || enterTo
|
|
89
|
+
const finalLeave = presetTransition.leave || leave
|
|
90
|
+
const finalLeaveFrom = presetTransition.leaveFrom || leaveFrom
|
|
91
|
+
const finalLeaveTo = presetTransition.leaveTo || leaveTo
|
|
92
|
+
|
|
93
|
+
export let isEntering = false
|
|
94
|
+
export let isLeaving = false
|
|
95
|
+
export let shouldRender = show
|
|
96
|
+
export let hasAppeared = false
|
|
97
|
+
|
|
98
|
+
const transitionClasses = show
|
|
99
|
+
? isEntering
|
|
100
|
+
? `${finalEnter} ${finalEnterFrom}`
|
|
101
|
+
: `${finalEnterTo}`
|
|
102
|
+
: isLeaving
|
|
103
|
+
? `${finalLeave} ${finalLeaveFrom}`
|
|
104
|
+
: `${finalLeaveTo}`
|
|
105
|
+
|
|
106
|
+
const componentClasses = `${transitionClasses} ${className}`.trim()
|
|
107
|
+
const durationStyle = `transition-duration: ${duration}ms; transition-delay: ${delay}ms;`
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
@if(shouldRender)
|
|
111
|
+
<div class="{{ componentClasses }}" style="{{ durationStyle }}">
|
|
112
|
+
<slot />
|
|
113
|
+
</div>
|
|
114
|
+
@endif
|
|
115
|
+
|
|
116
|
+
<script>
|
|
117
|
+
// Handle transitions with lifecycle callbacks
|
|
118
|
+
if (typeof window !== 'undefined') {
|
|
119
|
+
let previousShow = show
|
|
120
|
+
|
|
121
|
+
// Watch for show changes
|
|
122
|
+
function checkTransition() {
|
|
123
|
+
if (show !== previousShow) {
|
|
124
|
+
if (show && !shouldRender) {
|
|
125
|
+
// Entering
|
|
126
|
+
if (onBeforeEnter) onBeforeEnter()
|
|
127
|
+
|
|
128
|
+
shouldRender = true
|
|
129
|
+
isEntering = true
|
|
130
|
+
|
|
131
|
+
setTimeout(() => {
|
|
132
|
+
if (onEnter) onEnter()
|
|
133
|
+
}, delay)
|
|
134
|
+
|
|
135
|
+
setTimeout(() => {
|
|
136
|
+
isEntering = false
|
|
137
|
+
if (onAfterEnter) onAfterEnter()
|
|
138
|
+
}, duration + delay)
|
|
139
|
+
|
|
140
|
+
} else if (!show && shouldRender) {
|
|
141
|
+
// Leaving
|
|
142
|
+
if (onBeforeLeave) onBeforeLeave()
|
|
143
|
+
|
|
144
|
+
isLeaving = true
|
|
145
|
+
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
if (onLeave) onLeave()
|
|
148
|
+
}, delay)
|
|
149
|
+
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
isLeaving = false
|
|
152
|
+
shouldRender = false
|
|
153
|
+
if (onAfterLeave) onAfterLeave()
|
|
154
|
+
}, duration + delay)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
previousShow = show
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
requestAnimationFrame(checkTransition)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
requestAnimationFrame(checkTransition)
|
|
164
|
+
|
|
165
|
+
// Handle initial appear
|
|
166
|
+
if (appear && show && !hasAppeared) {
|
|
167
|
+
hasAppeared = true
|
|
168
|
+
isEntering = true
|
|
169
|
+
setTimeout(() => {
|
|
170
|
+
isEntering = false
|
|
171
|
+
}, duration + delay)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export { default as Transition } from './Transition.stx'
|
|
2
|
+
|
|
3
|
+
export interface TransitionProps {
|
|
4
|
+
show?: boolean
|
|
5
|
+
enter?: string
|
|
6
|
+
enterFrom?: string
|
|
7
|
+
enterTo?: string
|
|
8
|
+
leave?: string
|
|
9
|
+
leaveFrom?: string
|
|
10
|
+
leaveTo?: string
|
|
11
|
+
appear?: boolean
|
|
12
|
+
appearFrom?: string
|
|
13
|
+
appearTo?: string
|
|
14
|
+
mode?: 'in-out' | 'out-in' | 'default'
|
|
15
|
+
duration?: number
|
|
16
|
+
delay?: number
|
|
17
|
+
preset?: 'fade' | 'slide' | 'slideLeft' | 'slideRight' | 'scale' | 'rotate' | 'zoom' | ''
|
|
18
|
+
onBeforeEnter?: () => void
|
|
19
|
+
onEnter?: () => void
|
|
20
|
+
onAfterEnter?: () => void
|
|
21
|
+
onBeforeLeave?: () => void
|
|
22
|
+
onLeave?: () => void
|
|
23
|
+
onAfterLeave?: () => void
|
|
24
|
+
className?: string
|
|
25
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Video Component
|
|
2
|
+
|
|
3
|
+
A responsive video player with aspect ratio support and poster images.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @stacksjs/components
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```stx
|
|
14
|
+
@component('Video', {
|
|
15
|
+
src: '/videos/demo.mp4',
|
|
16
|
+
poster: '/images/video-thumbnail.jpg',
|
|
17
|
+
title: 'Product Demo',
|
|
18
|
+
aspectRatio: '16/9',
|
|
19
|
+
controls: true
|
|
20
|
+
})
|
|
21
|
+
@endcomponent
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Props
|
|
25
|
+
|
|
26
|
+
| Prop | Type | Default | Description |
|
|
27
|
+
|------|------|---------|-------------|
|
|
28
|
+
| `src` | `string` | - | Video file URL (required) |
|
|
29
|
+
| `poster` | `string` | `''` | Poster image URL |
|
|
30
|
+
| `autoplay` | `boolean` | `false` | Auto-play on load |
|
|
31
|
+
| `loop` | `boolean` | `false` | Loop playback |
|
|
32
|
+
| `muted` | `boolean` | `false` | Start muted |
|
|
33
|
+
| `controls` | `boolean` | `true` | Show native controls |
|
|
34
|
+
| `preload` | `'none' \| 'metadata' \| 'auto'` | `'metadata'` | Preload strategy |
|
|
35
|
+
| `width` | `number \| string` | - | Video width |
|
|
36
|
+
| `height` | `number \| string` | - | Video height |
|
|
37
|
+
| `aspectRatio` | `'16/9' \| '4/3' \| '1/1' \| string` | `'16/9'` | Aspect ratio |
|
|
38
|
+
| `className` | `string` | `''` | Additional CSS classes |
|
|
39
|
+
| `title` | `string` | `''` | Optional title above player |
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- **Aspect ratios** - 16:9, 4:3, 1:1 built-in
|
|
44
|
+
- **Poster images** - Show thumbnail before play
|
|
45
|
+
- **Native controls** - Uses browser's native video controls
|
|
46
|
+
- **Dark mode** - Styled for dark mode
|
|
47
|
+
- **Responsive** - Full-width with aspect ratio
|
|
48
|
+
- **Accessible** - Proper fallback text
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const src = $props.src || ''
|
|
3
|
+
export const poster = $props.poster || ''
|
|
4
|
+
export const autoplay = $props.autoplay || false
|
|
5
|
+
export const loop = $props.loop || false
|
|
6
|
+
export const muted = $props.muted || false
|
|
7
|
+
export const controls = $props.controls ?? true
|
|
8
|
+
export const preload = $props.preload || 'metadata'
|
|
9
|
+
export const width = $props.width
|
|
10
|
+
export const height = $props.height
|
|
11
|
+
export const aspectRatio = $props.aspectRatio || '16/9'
|
|
12
|
+
export const className = $props.className || ''
|
|
13
|
+
export const title = $props.title || ''
|
|
14
|
+
|
|
15
|
+
export const containerClasses = `relative w-full overflow-hidden rounded-lg ${className}`.trim()
|
|
16
|
+
export const videoClasses = 'w-full h-full object-cover bg-gray-900 dark:bg-gray-950'
|
|
17
|
+
export const aspectRatioClass = aspectRatio === '16/9' ? 'aspect-video' : aspectRatio === '4/3' ? 'aspect-[4/3]' : aspectRatio === '1/1' ? 'aspect-square' : ''
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<div class="{{ containerClasses }}">
|
|
21
|
+
@if(title)
|
|
22
|
+
<div class="mb-2 px-1">
|
|
23
|
+
<h3 class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
24
|
+
{{ title }}
|
|
25
|
+
</h3>
|
|
26
|
+
</div>
|
|
27
|
+
@endif
|
|
28
|
+
|
|
29
|
+
<div class="{{ aspectRatioClass }}">
|
|
30
|
+
<video
|
|
31
|
+
class="{{ videoClasses }}"
|
|
32
|
+
src="{{ src }}"
|
|
33
|
+
poster="{{ poster }}"
|
|
34
|
+
controls="{{ controls }}"
|
|
35
|
+
autoplay="{{ autoplay }}"
|
|
36
|
+
loop="{{ loop }}"
|
|
37
|
+
muted="{{ muted }}"
|
|
38
|
+
preload="{{ preload }}"
|
|
39
|
+
width="{{ width }}"
|
|
40
|
+
height="{{ height }}"
|
|
41
|
+
>
|
|
42
|
+
Your browser does not support the video element.
|
|
43
|
+
</video>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { default as Video } from './Video.stx'
|
|
2
|
+
|
|
3
|
+
export interface VideoProps {
|
|
4
|
+
src: string
|
|
5
|
+
poster?: string
|
|
6
|
+
autoplay?: boolean
|
|
7
|
+
loop?: boolean
|
|
8
|
+
muted?: boolean
|
|
9
|
+
controls?: boolean
|
|
10
|
+
preload?: 'none' | 'metadata' | 'auto'
|
|
11
|
+
width?: number | string
|
|
12
|
+
height?: number | string
|
|
13
|
+
aspectRatio?: '16/9' | '4/3' | '1/1' | string
|
|
14
|
+
className?: string
|
|
15
|
+
title?: string
|
|
16
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const items = $props.items || []
|
|
3
|
+
export const itemHeight = $props.itemHeight || 50
|
|
4
|
+
export const height = $props.height || 400
|
|
5
|
+
export const overscan = $props.overscan || 3
|
|
6
|
+
export const renderItem = $props.renderItem
|
|
7
|
+
export const className = $props.className || ''
|
|
8
|
+
|
|
9
|
+
// Calculate visible range
|
|
10
|
+
let scrollTop = 0
|
|
11
|
+
const visibleCount = Math.ceil(height / itemHeight)
|
|
12
|
+
const totalHeight = items.length * itemHeight
|
|
13
|
+
|
|
14
|
+
// Calculate start and end indices with overscan
|
|
15
|
+
function getVisibleRange() {
|
|
16
|
+
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan)
|
|
17
|
+
const endIndex = Math.min(items.length - 1, Math.floor((scrollTop + height) / itemHeight) + overscan)
|
|
18
|
+
return { startIndex, endIndex }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { startIndex, endIndex } = getVisibleRange()
|
|
22
|
+
const visibleItems = items.slice(startIndex, endIndex + 1)
|
|
23
|
+
const offsetY = startIndex * itemHeight
|
|
24
|
+
|
|
25
|
+
// Handle scroll
|
|
26
|
+
function handleScroll(event) {
|
|
27
|
+
const target = event.target
|
|
28
|
+
scrollTop = target.scrollTop
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const containerClasses = `overflow-auto ${className}`.trim()
|
|
32
|
+
export const innerClasses = `relative`.trim()
|
|
33
|
+
export const itemClasses = `absolute left-0 right-0`.trim()
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<div
|
|
37
|
+
class="{{ containerClasses }}"
|
|
38
|
+
style="height: {{ height }}px"
|
|
39
|
+
onscroll="handleScroll(event)"
|
|
40
|
+
>
|
|
41
|
+
<div
|
|
42
|
+
class="{{ innerClasses }}"
|
|
43
|
+
style="height: {{ totalHeight }}px"
|
|
44
|
+
>
|
|
45
|
+
@foreach(item in visibleItems)
|
|
46
|
+
<div
|
|
47
|
+
class="{{ itemClasses }}"
|
|
48
|
+
style="top: {{ (startIndex + $loop.index) * itemHeight }}px; height: {{ itemHeight }}px"
|
|
49
|
+
data-index="{{ startIndex + $loop.index }}"
|
|
50
|
+
>
|
|
51
|
+
@if(renderItem)
|
|
52
|
+
{!! renderItem(item, startIndex + $loop.index) !!}
|
|
53
|
+
@else
|
|
54
|
+
<slot item="{{ item }}" index="{{ startIndex + $loop.index }}" />
|
|
55
|
+
@endif
|
|
56
|
+
</div>
|
|
57
|
+
@endforeach
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<script>
|
|
62
|
+
// Re-render on scroll (simplified reactivity)
|
|
63
|
+
if (typeof window !== 'undefined') {
|
|
64
|
+
let ticking = false
|
|
65
|
+
|
|
66
|
+
function updateVisibleItems() {
|
|
67
|
+
const range = getVisibleRange()
|
|
68
|
+
// Trigger re-render by updating visible range
|
|
69
|
+
// In a real implementation, this would update reactive state
|
|
70
|
+
ticking = false
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const scrollContainer = document.querySelector('.overflow-auto')
|
|
74
|
+
if (scrollContainer) {
|
|
75
|
+
scrollContainer.addEventListener('scroll', () => {
|
|
76
|
+
if (!ticking) {
|
|
77
|
+
window.requestAnimationFrame(updateVisibleItems)
|
|
78
|
+
ticking = true
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
</script>
|