negodesign 0.0.81 → 0.0.82
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 +133 -133
- package/dist/components/NegoDesign.svelte +21 -21
- package/dist/components/pages/admin/01/ui/AdminContent.svelte +14 -14
- package/dist/components/pages/admin/01/ui/AdminPanel.svelte +46 -46
- package/dist/components/pages/product-details/shared/ProductDetailFilesVertical.svelte +103 -103
- package/dist/components/pages/profile-user/shared/ProfileUserAddress.svelte +83 -83
- package/dist/components/pages/profile-user/shared/ProfileUserTabs.svelte +80 -80
- package/dist/components/pages/security/auth/PageAuth01.svelte +62 -62
- package/dist/components/pages/security/auth/PageAuth02.svelte +40 -40
- package/dist/components/pages/security/auth/PageAuth03.svelte +40 -40
- package/dist/components/pages/security/login/01/ui/FormLogin.svelte +91 -63
- package/dist/components/pages/security/login/01/ui/FormLogin.svelte.d.ts +2 -0
- package/dist/components/pages/security/login/01/ui/FormLoginEmailPassword.svelte +49 -26
- package/dist/components/pages/security/login/01/ui/FormLoginEmailPassword.svelte.d.ts +2 -0
- package/dist/components/pages/security/login/01/ui/FormLoginPhonePassword.svelte +49 -26
- package/dist/components/pages/security/login/01/ui/FormLoginPhonePassword.svelte.d.ts +2 -0
- package/dist/components/pages/security/login/01/ui/FormLoginUsernamePassword.svelte +49 -26
- package/dist/components/pages/security/login/01/ui/FormLoginUsernamePassword.svelte.d.ts +2 -0
- package/dist/components/pages/security/login/01/ui/LoginCard01.svelte +148 -132
- package/dist/components/pages/security/login/01/ui/LoginCard01.svelte.d.ts +2 -0
- package/dist/components/pages/security/login/01/ui/PageLogin01.svelte +66 -64
- package/dist/components/pages/security/login/02/ui/FormLogin02.svelte +82 -72
- package/dist/components/pages/security/login/02/ui/FormLogin02.svelte.d.ts +1 -0
- package/dist/components/pages/security/login/02/ui/FormLoginEmailPassword02.svelte +27 -26
- package/dist/components/pages/security/login/02/ui/FormLoginEmailPassword02.svelte.d.ts +1 -0
- package/dist/components/pages/security/login/02/ui/FormLoginPhonePassword02.svelte +27 -26
- package/dist/components/pages/security/login/02/ui/FormLoginPhonePassword02.svelte.d.ts +1 -0
- package/dist/components/pages/security/login/02/ui/FormLoginUsernamePassword02.svelte +27 -26
- package/dist/components/pages/security/login/02/ui/FormLoginUsernamePassword02.svelte.d.ts +1 -0
- package/dist/components/pages/security/login/02/ui/LoginCard02.svelte +15 -2
- package/dist/components/pages/security/login/02/ui/LoginCard02.svelte.d.ts +1 -0
- package/dist/components/pages/security/login/02/ui/PageLogin02.svelte +2 -0
- package/dist/components/pages/security/login/03/ui/FormLogin03.svelte +94 -91
- package/dist/components/pages/security/login/03/ui/FormLogin03.svelte.d.ts +1 -0
- package/dist/components/pages/security/login/03/ui/FormLoginEmailPassword03.svelte +27 -26
- package/dist/components/pages/security/login/03/ui/FormLoginEmailPassword03.svelte.d.ts +1 -0
- package/dist/components/pages/security/login/03/ui/FormLoginPhonePassword03.svelte +27 -26
- package/dist/components/pages/security/login/03/ui/FormLoginPhonePassword03.svelte.d.ts +1 -0
- package/dist/components/pages/security/login/03/ui/FormLoginUsernamePassword03.svelte +27 -26
- package/dist/components/pages/security/login/03/ui/FormLoginUsernamePassword03.svelte.d.ts +1 -0
- package/dist/components/pages/security/login/03/ui/LoginCard03.svelte +15 -2
- package/dist/components/pages/security/login/03/ui/LoginCard03.svelte.d.ts +1 -0
- package/dist/components/pages/security/login/03/ui/PageLogin03.svelte +2 -0
- package/dist/components/pages/security/login/PageLogin.svelte +30 -30
- package/dist/components/pages/security/login/types.d.ts +1 -0
- package/dist/components/pages/security/register/01/ui/PageRegister01.svelte +78 -76
- package/dist/components/pages/security/register/01/ui/RegisterCard01.svelte +107 -104
- package/dist/components/pages/security/register/01/ui/RegisterCard01.svelte.d.ts +1 -0
- package/dist/components/pages/security/register/02/ui/PageRegister02.svelte +2 -0
- package/dist/components/pages/security/register/02/ui/RegisterCard02.svelte +3 -1
- package/dist/components/pages/security/register/02/ui/RegisterCard02.svelte.d.ts +1 -0
- package/dist/components/pages/security/register/03/ui/PageRegister03.svelte +2 -0
- package/dist/components/pages/security/register/03/ui/RegisterCard03.svelte +3 -1
- package/dist/components/pages/security/register/03/ui/RegisterCard03.svelte.d.ts +1 -0
- package/dist/components/pages/security/register/FormRegister.svelte +148 -146
- package/dist/components/pages/security/register/FormRegister.svelte.d.ts +1 -0
- package/dist/components/pages/security/register/types.d.ts +1 -0
- package/dist/components/ui/avatar/avatar-fallback.svelte +29 -29
- package/dist/components/ui/avatar/avatar-image.svelte +38 -38
- package/dist/components/ui/avatar/avatar.svelte +39 -39
- package/dist/components/ui/badge/badge.svelte +46 -46
- package/dist/components/ui/banner/AdCardBanner.svelte +74 -74
- package/dist/components/ui/banner/CtaCardBanner.svelte +118 -118
- package/dist/components/ui/banner/CtaCardImageBanner.svelte +94 -94
- package/dist/components/ui/breadcrumb/core/breadcrumb-list.svelte +31 -31
- package/dist/components/ui/button/ButtonBack.svelte +18 -18
- package/dist/components/ui/button/ButtonLogin.svelte +19 -15
- package/dist/components/ui/button/ButtonLoginWith.svelte +57 -57
- package/dist/components/ui/button/ButtonSend.svelte +15 -15
- package/dist/components/ui/button/button.svelte +100 -86
- package/dist/components/ui/button/button.svelte.d.ts +1 -0
- package/dist/components/ui/card/core/highlight/01/CardHighlight01.svelte +103 -103
- package/dist/components/ui/card/core/highlight/02/CardHighlight02.svelte +99 -99
- package/dist/components/ui/card/core/highlight/CardHighlight.svelte +22 -22
- package/dist/components/ui/card/core/media/01/CardMedia01.svelte +133 -133
- package/dist/components/ui/card/core/media/02/CardMedia02.svelte +127 -127
- package/dist/components/ui/card/core/media/CardMedia.svelte +18 -18
- package/dist/components/ui/card/core/product/01/CardProduct01.svelte +136 -136
- package/dist/components/ui/card/core/product/02/CardProduct02.svelte +124 -124
- package/dist/components/ui/card/core/product/CardProduct.svelte +22 -22
- package/dist/components/ui/card/core/profile/01/CardProfile01.svelte +134 -134
- package/dist/components/ui/card/core/profile/02/CardProfile02.svelte +132 -132
- package/dist/components/ui/card/core/profile/CardProfile.svelte +18 -18
- package/dist/components/ui/card/core/promotion/CardPromotion.svelte +139 -139
- package/dist/components/ui/card/core/promotion/CardPromotionPrice.svelte +32 -32
- package/dist/components/ui/card/core/shared/CardButton.svelte +42 -42
- package/dist/components/ui/card/core/shared/CardCart.svelte +36 -36
- package/dist/components/ui/card/core/shared/CardDescription.svelte +41 -41
- package/dist/components/ui/card/core/shared/CardFavorite.svelte +33 -33
- package/dist/components/ui/card/core/shared/CardLogo.svelte +19 -19
- package/dist/components/ui/card/core/shared/CardPhoneOrWhatsapp.svelte +45 -45
- package/dist/components/ui/card/core/shared/CardStarRating.svelte +24 -24
- package/dist/components/ui/card/core/shared/CardTags.svelte +95 -95
- package/dist/components/ui/card/core/shared/CardThumbnailVideo.svelte +232 -232
- package/dist/components/ui/card/core/shared/CardTitlePrice.svelte +24 -24
- package/dist/components/ui/card/core/shared/TruncatableText.svelte +49 -49
- package/dist/components/ui/carousel/carousel-content.svelte +56 -56
- package/dist/components/ui/carousel/carousel-item.svelte +29 -29
- package/dist/components/ui/carousel/carousel-next.svelte +40 -40
- package/dist/components/ui/carousel/carousel-previous.svelte +40 -40
- package/dist/components/ui/carousel/carousel.svelte +225 -225
- package/dist/components/ui/carousel/core/badge/ui/CarouselBadge.svelte +108 -108
- package/dist/components/ui/carousel/core/grid/shared/ui/CarouselGridSlot.svelte +38 -38
- package/dist/components/ui/carousel/core/hero/ui/CarouselHero.svelte +181 -181
- package/dist/components/ui/carousel/core/highlights/ui/CarouselHighlights.svelte +67 -67
- package/dist/components/ui/carousel/core/product/ui/CarouselProduct.svelte +57 -57
- package/dist/components/ui/carousel/core/promotion/ui/CarouselPromotion.svelte +67 -67
- package/dist/components/ui/carousel/core/shared/ui/CarouselHeader.svelte +59 -59
- package/dist/components/ui/checkbox/checkbox.svelte +96 -96
- package/dist/components/ui/collapsible/collapsible-content.svelte +30 -30
- package/dist/components/ui/collapsible/collapsible-trigger.svelte +51 -51
- package/dist/components/ui/collapsible/collapsible.svelte +66 -66
- package/dist/components/ui/data-table/flex-render.svelte +40 -40
- package/dist/components/ui/datatable/ui/DataTableActions.svelte +28 -28
- package/dist/components/ui/datatable/ui/DataTableCheckbox.svelte +22 -22
- package/dist/components/ui/datatable/ui/DataTableContent.svelte +59 -59
- package/dist/components/ui/datatable/ui/DataTableCore.svelte +148 -148
- package/dist/components/ui/datatable/ui/DataTableDragHandle.svelte +8 -8
- package/dist/components/ui/datatable/ui/DataTableListFilter.svelte +60 -60
- package/dist/components/ui/datatable/ui/DataTableListPagination.svelte +35 -35
- package/dist/components/ui/drawer/drawer-close.svelte +41 -41
- package/dist/components/ui/drawer/drawer-content.svelte +81 -81
- package/dist/components/ui/drawer/drawer-description.svelte +19 -19
- package/dist/components/ui/drawer/drawer-footer.svelte +20 -20
- package/dist/components/ui/drawer/drawer-header.svelte +20 -20
- package/dist/components/ui/drawer/drawer-nested.svelte +34 -34
- package/dist/components/ui/drawer/drawer-overlay.svelte +25 -25
- package/dist/components/ui/drawer/drawer-portal.svelte +19 -19
- package/dist/components/ui/drawer/drawer-title.svelte +19 -19
- package/dist/components/ui/drawer/drawer-trigger.svelte +42 -42
- package/dist/components/ui/drawer/drawer.svelte +74 -74
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +25 -25
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +69 -69
- package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +106 -106
- package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +27 -27
- package/dist/components/ui/dropdown-menu/dropdown-menu-group.svelte +21 -21
- package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +60 -60
- package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte +27 -27
- package/dist/components/ui/dropdown-menu/dropdown-menu-portal.svelte +6 -6
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +43 -43
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +70 -70
- package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte +20 -20
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +33 -33
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +57 -57
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub.svelte +76 -76
- package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +68 -68
- package/dist/components/ui/dropdown-menu/dropdown-menu.svelte +53 -53
- package/dist/components/ui/empty/empty-content.svelte +19 -19
- package/dist/components/ui/empty/empty-description.svelte +19 -19
- package/dist/components/ui/empty/empty-header.svelte +19 -19
- package/dist/components/ui/empty/empty-media.svelte +41 -41
- package/dist/components/ui/empty/empty-title.svelte +19 -19
- package/dist/components/ui/empty/empty.svelte +19 -19
- package/dist/components/ui/footer/ui/Footer.svelte +18 -18
- package/dist/components/ui/form/ui/calendar/Calendar.svelte +193 -193
- package/dist/components/ui/form/ui/calendar/CalendarCard.svelte +89 -89
- package/dist/components/ui/form/ui/calendar/DaysGrid.svelte +75 -75
- package/dist/components/ui/form/ui/calendar/MonthSelect.svelte +49 -49
- package/dist/components/ui/form/ui/calendar/YearSelect.svelte +116 -116
- package/dist/components/ui/form/ui/input-base.svelte +83 -83
- package/dist/components/ui/form/ui/input-calendar-interval.svelte +178 -178
- package/dist/components/ui/form/ui/input-calendar.svelte +109 -109
- package/dist/components/ui/form/ui/input-email-or-phone.svelte +23 -23
- package/dist/components/ui/form/ui/input-email.svelte +23 -23
- package/dist/components/ui/form/ui/input-password-confirm.svelte +23 -23
- package/dist/components/ui/form/ui/input-password.svelte +23 -23
- package/dist/components/ui/form/ui/input-phone.svelte +43 -37
- package/dist/components/ui/form/ui/input-select.svelte +246 -246
- package/dist/components/ui/form/ui/input-username.svelte +22 -22
- package/dist/components/ui/grid/core/ui/01/Grid01.svelte +30 -30
- package/dist/components/ui/grid/core/ui/02/Grid02.svelte +30 -30
- package/dist/components/ui/grid/core/ui/ItemsPanel.svelte +26 -26
- package/dist/components/ui/grid/core/ui/ItemsPanelCarousel.svelte +37 -37
- package/dist/components/ui/grid/core/ui/ItemsPanelGrid.svelte +43 -43
- package/dist/components/ui/grid/core/ui/ItemsSimpleGrid.svelte +45 -45
- package/dist/components/ui/grid/core/ui/shared/DescriptionGrid.svelte +23 -23
- package/dist/components/ui/grid/core/ui/shared/IconRenderGrid.svelte +23 -23
- package/dist/components/ui/grid/core/ui/shared/TitleGrid.svelte +19 -19
- package/dist/components/ui/hero/SimpleHero.svelte +65 -65
- package/dist/components/ui/input-otp/input-otp-group.svelte +22 -22
- package/dist/components/ui/input-otp/input-otp-separator.svelte +27 -27
- package/dist/components/ui/input-otp/input-otp-slot.svelte +63 -63
- package/dist/components/ui/input-otp/input-otp.svelte +181 -181
- package/dist/components/ui/label/core/LabelOr.svelte +25 -25
- package/dist/components/ui/label/label.svelte +19 -19
- package/dist/components/ui/language-switcher/language-switcher.svelte +48 -48
- package/dist/components/ui/light-switch/light-switch.svelte +48 -48
- package/dist/components/ui/link/link-forget-password.svelte +12 -12
- package/dist/components/ui/link/link-privacy-polity.svelte +12 -12
- package/dist/components/ui/link/link-terms-of-service.svelte +12 -12
- package/dist/components/ui/modal/core/ui/ModalCore.svelte +132 -132
- package/dist/components/ui/modal/form/index.d.ts +3 -2
- package/dist/components/ui/modal/form/index.js +2 -1
- package/dist/components/ui/modal/form/types.d.ts +41 -0
- package/dist/components/ui/modal/form/ui/ModalForm.svelte +11 -6
- package/dist/components/ui/modal/form/ui/ModalFormEmailOrPhone.svelte +204 -0
- package/dist/components/ui/modal/form/ui/ModalFormEmailOrPhone.svelte.d.ts +4 -0
- package/dist/components/ui/modal/grid/ui/02/ModalGridSelection02.svelte +146 -146
- package/dist/components/ui/modal/share/ui/ModalShareSelection.svelte +156 -156
- package/dist/components/ui/nav/ui/ComplexMenu.svelte +51 -51
- package/dist/components/ui/nav/ui/Menu.svelte +72 -72
- package/dist/components/ui/nav/ui/SimpleMenu.svelte +14 -14
- package/dist/components/ui/nav/ui/mobile/ComplexMenuMobile.svelte +125 -125
- package/dist/components/ui/nav/ui/mobile/MenuMobile.svelte +54 -54
- package/dist/components/ui/nav/ui/shared/MenuButton.svelte +51 -51
- package/dist/components/ui/nav/ui/shared/MenuLinks.svelte +40 -40
- package/dist/components/ui/nav/ui/shared/MenuListItem.svelte +44 -44
- package/dist/components/ui/nav/ui/shared/MenuLogo.svelte +13 -13
- package/dist/components/ui/nav/ui/shared/dropdown/DropdownNavigationMenuItemGrid.svelte +30 -30
- package/dist/components/ui/nav/ui/shared/navigation/NavDropdown.svelte +88 -88
- package/dist/components/ui/nav/ui/shared/navigation/NavigationMenuItem.svelte +17 -17
- package/dist/components/ui/nav/ui/shared/navigation/NavigationMenuItemCard.svelte +53 -53
- package/dist/components/ui/nav/ui/shared/navigation/NavigationMenuItemGrid.svelte +35 -35
- package/dist/components/ui/nav/ui/shared/navigation/NavigationMenuItemList.svelte +24 -24
- package/dist/components/ui/nav/ui/shared/navigation/NavigationMenuItems.svelte +21 -21
- package/dist/components/ui/panel/CarouselSlot.svelte +57 -57
- package/dist/components/ui/panel/LeftHero.svelte +114 -114
- package/dist/components/ui/panel/NotFoundEmpty.svelte +66 -66
- package/dist/components/ui/phone-input/country-selector.svelte +87 -87
- package/dist/components/ui/phone-input/flag.svelte +11 -11
- package/dist/components/ui/phone-input/phone-input.svelte +163 -163
- package/dist/components/ui/popover/popover-close.svelte +25 -25
- package/dist/components/ui/popover/popover-content.svelte +92 -92
- package/dist/components/ui/popover/popover-portal.svelte +6 -6
- package/dist/components/ui/popover/popover-trigger.svelte +68 -68
- package/dist/components/ui/popover/popover.svelte +53 -53
- package/dist/components/ui/search/SearchInput.svelte +32 -32
- package/dist/components/ui/search/shared/SearchInputCore.svelte +66 -66
- package/dist/components/ui/separator/separator.svelte +28 -28
- package/dist/components/ui/sidebar/core/MenuBarSidebar.svelte +70 -70
- package/dist/components/ui/sidebar/core/ui/navbar/NavMenuBarSidebar.svelte +97 -97
- package/dist/components/ui/sidebar/core/ui/navbar/NavMenuBottomSidebar.svelte +33 -33
- package/dist/components/ui/sidebar/core/ui/navbar/NavMenuTopBarSidebar.svelte +101 -101
- package/dist/components/ui/sidebar/core/ui/navbar/nav-menu-header-sidebar.svelte +77 -77
- package/dist/components/ui/sidebar/sidebar-content.svelte +24 -24
- package/dist/components/ui/sidebar/sidebar-footer.svelte +21 -21
- package/dist/components/ui/sidebar/sidebar-group-content.svelte +21 -21
- package/dist/components/ui/sidebar/sidebar-menu-action.svelte +38 -38
- package/dist/components/ui/sidebar/sidebar-menu-button.svelte +70 -70
- package/dist/components/ui/sidebar/sidebar-menu-sub-button.svelte +39 -39
- package/dist/components/ui/sidebar/sidebar-provider.svelte +62 -62
- package/dist/components/ui/sidebar/sidebar-rail.svelte +36 -36
- package/dist/components/ui/sidebar/sidebar-separator.svelte +21 -21
- package/dist/components/ui/sidebar/sidebar-trigger.svelte +36 -36
- package/dist/components/ui/sidebar/sidebar.svelte +118 -118
- package/dist/components/ui/skeleton/skeleton.svelte +19 -19
- package/dist/components/ui/star-rating/star-rating-star.svelte +66 -66
- package/dist/components/ui/star-rating/star-rating.svelte +149 -149
- package/dist/components/ui/table/table-body.svelte +15 -15
- package/dist/components/ui/table/table-caption.svelte +20 -20
- package/dist/components/ui/table/table-cell.svelte +15 -15
- package/dist/components/ui/table/table-footer.svelte +20 -20
- package/dist/components/ui/table/table-head.svelte +15 -15
- package/dist/components/ui/table/table-header.svelte +20 -20
- package/dist/components/ui/table/table-row.svelte +15 -15
- package/dist/components/ui/table/table.svelte +17 -17
- package/dist/components/ui/tabs/data/TabModel.d.ts +1 -0
- package/dist/components/ui/tabs/ui/tab-underline.svelte +47 -43
- package/dist/components/ui/underline-tabs/underline-tabs-content.svelte +33 -33
- package/dist/components/ui/underline-tabs/underline-tabs-list.svelte +40 -40
- package/dist/components/ui/underline-tabs/underline-tabs-trigger.svelte +91 -91
- package/dist/components/ui/underline-tabs/underline-tabs.svelte +61 -61
- package/dist/docs/ApiTable.svelte +42 -42
- package/dist/docs/CodeBlock.svelte +26 -26
- package/dist/docs/DocsHeader.svelte +25 -25
- package/dist/docs/DocsSidebar.svelte +108 -108
- package/dist/docs/components.js +624 -584
- package/dist/globals.css +27 -7
- package/dist/i18n/langs/en.d.ts +3 -1
- package/dist/i18n/langs/en.js +3 -1
- package/dist/i18n/langs/pt.d.ts +3 -1
- package/dist/i18n/langs/pt.js +3 -1
- package/dist/i18n/translations.d.ts +6 -2
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/styles/globals.css +283 -283
- package/dist/types/index.d.ts +3 -1
- package/package.json +81 -81
package/README.md
CHANGED
|
@@ -1,134 +1,134 @@
|
|
|
1
|
-
# NegoDesign
|
|
2
|
-
|
|
3
|
-
<div align="center">
|
|
4
|
-
|
|
5
|
-
**A Svelte 5 component library for building modern, fast and beautiful web applications.**
|
|
6
|
-
|
|
7
|
-
</div>
|
|
8
|
-
|
|
9
|
-
NegoDesign is an open-source collection of accessible, copy-paste-friendly UI components
|
|
10
|
-
for [Svelte](https://svelte.dev) and [SvelteKit](https://svelte.dev/docs/kit). It follows the
|
|
11
|
-
[shadcn](https://ui.shadcn.com) philosophy: components are built with Svelte's runes API,
|
|
12
|
-
styled with [Tailwind CSS](https://tailwindcss.com), and ship as plain `.svelte` files you
|
|
13
|
-
own — no lock-in, no black box.
|
|
14
|
-
|
|
15
|
-
From low-level primitives (buttons, badges, carousels, dropdowns, inputs, sidebars) to
|
|
16
|
-
complete, production-ready pages (admin panel, login, company profile, product details),
|
|
17
|
-
everything is designed to be dropped into your project and customized.
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Features
|
|
22
|
-
|
|
23
|
-
- **Svelte 5 runes** — components use `$state`, `$derived`, `$props`, snippets and
|
|
24
|
-
bindable props. No legacy APIs.
|
|
25
|
-
- **shadcn-style workflow** — copy the component, read the prop table, ship it.
|
|
26
|
-
- **Tailwind CSS v4** — utility-first styling with CSS variables for theming and dark mode.
|
|
27
|
-
- **Theming** — CSS variables for colors and a built-in light/dark switch.
|
|
28
|
-
- **i18n ready** — language switcher and translation helpers included.
|
|
29
|
-
- **Composable** — low-level UI primitives and high-level composite components.
|
|
30
|
-
- **TypeScript** — all components ship with full type definitions.
|
|
31
|
-
|
|
32
|
-
## Requirements
|
|
33
|
-
|
|
34
|
-
- [Svelte](https://svelte.dev) `>= 5`
|
|
35
|
-
- [SvelteKit](https://svelte.dev/docs/kit) `>= 2` (recommended)
|
|
36
|
-
|
|
37
|
-
Tailwind CSS is not a hard dependency, but the components are designed for it and the
|
|
38
|
-
built-in `styles.css` expects the shadcn design tokens.
|
|
39
|
-
|
|
40
|
-
## Installation
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
npm install negodesign
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Import the compiled styles once, in your global stylesheet:
|
|
47
|
-
|
|
48
|
-
```css
|
|
49
|
-
@import "tailwindcss";
|
|
50
|
-
@import "negodesign/styles.css";
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Or from your root layout:
|
|
54
|
-
|
|
55
|
-
```svelte
|
|
56
|
-
<script lang="ts">
|
|
57
|
-
import "negodesign/styles.css";
|
|
58
|
-
import "../app.css";
|
|
59
|
-
</script>
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Usage
|
|
63
|
-
|
|
64
|
-
```svelte
|
|
65
|
-
<script lang="ts">
|
|
66
|
-
import { Menu } from "negodesign";
|
|
67
|
-
import { Home01Icon, Hotel01Icon } from "@hugeicons/core-free-icons";
|
|
68
|
-
|
|
69
|
-
const links = [
|
|
70
|
-
{ label: "Home", url: "/", icon: Home01Icon },
|
|
71
|
-
{ label: "Accommodation", url: "/accommodation", icon: Hotel01Icon },
|
|
72
|
-
];
|
|
73
|
-
</script>
|
|
74
|
-
|
|
75
|
-
<Menu isBorder navMenu={{ links }} />
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Package exports
|
|
79
|
-
|
|
80
|
-
| Export | Description |
|
|
81
|
-
| ------------------------- | ------------------------------------------------------ |
|
|
82
|
-
| `negodesign` | All components, types and utilities |
|
|
83
|
-
| `negodesign/styles.css` | Compiled global styles (design tokens, base, theme) |
|
|
84
|
-
| `negodesign/types` | Shared TypeScript types |
|
|
85
|
-
| `negodesign/i18n` | Translation helpers and dictionaries |
|
|
86
|
-
|
|
87
|
-
## Component categories
|
|
88
|
-
|
|
89
|
-
- **Navigation** — `Menu`, `SearchInput`, `Navbar`
|
|
90
|
-
- **Hero** — `SimpleHero`, `CarouselHero`
|
|
91
|
-
- **Banners** — `CtaCardImageBanner`, `CtaCardBanner`, `AdCardBanner`, `NotificationBanner`
|
|
92
|
-
- **Cards** — `CardMedia`, `CardProfile`, `CardProduct`, `CardPromotion`, `CardHighlight`
|
|
93
|
-
- **Carousels** — `CarouselBadge`, `CarouselHighlights`, `CarouselPromotion`, `CarouselGridMedia/Product/Profile/Promotion`
|
|
94
|
-
- **Item Grid** — `ItemsSimpleGrid`, `ItemsPanel`
|
|
95
|
-
- **Footer** — `Footer`
|
|
96
|
-
- **Admin Panel** — `AdminPanel`, `MenuBarSidebar`, `DataTableCore`
|
|
97
|
-
- **Login / Security** — `PageLogin`, `PageOtpVerification`, `PageForgetPassword`, `PageResetPassword`
|
|
98
|
-
- **Full Pages** — `CompanyProfile`, `ProductDetails`, `ProfileUser`, `PrivacyPolicyOrTermsOfUse`
|
|
99
|
-
|
|
100
|
-
## Documentation
|
|
101
|
-
|
|
102
|
-
During development, a full documentation site is available at `/docs`:
|
|
103
|
-
|
|
104
|
-
- **Getting Started** — `/docs`
|
|
105
|
-
- **Installation** — `/docs/installation`
|
|
106
|
-
- **Components** — `/docs/components` and `/docs/<component>`
|
|
107
|
-
|
|
108
|
-
Each component page includes its description, a copy-paste example, a link to a live demo
|
|
109
|
-
and a complete API reference (props table).
|
|
110
|
-
|
|
111
|
-
## Development
|
|
112
|
-
|
|
113
|
-
Clone the repository and install dependencies:
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
git clone <repository-url>
|
|
117
|
-
cd negodesign
|
|
118
|
-
npm install
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Useful scripts:
|
|
122
|
-
|
|
123
|
-
| Command | Description |
|
|
124
|
-
| ----------------- | -------------------------------------------- |
|
|
125
|
-
| `npm run dev` | Start the SvelteKit dev server |
|
|
126
|
-
| `npm run check` | Run `svelte-check` for type/compile errors |
|
|
127
|
-
| `npm run lint` | Run ESLint |
|
|
128
|
-
| `npm test` | Run the Vitest unit/browser tests |
|
|
129
|
-
| `npm run build` | Build the demo app |
|
|
130
|
-
| `npm run package` | Build the distributable package into `dist/` |
|
|
131
|
-
|
|
132
|
-
## License
|
|
133
|
-
|
|
1
|
+
# NegoDesign
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
**A Svelte 5 component library for building modern, fast and beautiful web applications.**
|
|
6
|
+
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
NegoDesign is an open-source collection of accessible, copy-paste-friendly UI components
|
|
10
|
+
for [Svelte](https://svelte.dev) and [SvelteKit](https://svelte.dev/docs/kit). It follows the
|
|
11
|
+
[shadcn](https://ui.shadcn.com) philosophy: components are built with Svelte's runes API,
|
|
12
|
+
styled with [Tailwind CSS](https://tailwindcss.com), and ship as plain `.svelte` files you
|
|
13
|
+
own — no lock-in, no black box.
|
|
14
|
+
|
|
15
|
+
From low-level primitives (buttons, badges, carousels, dropdowns, inputs, sidebars) to
|
|
16
|
+
complete, production-ready pages (admin panel, login, company profile, product details),
|
|
17
|
+
everything is designed to be dropped into your project and customized.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- **Svelte 5 runes** — components use `$state`, `$derived`, `$props`, snippets and
|
|
24
|
+
bindable props. No legacy APIs.
|
|
25
|
+
- **shadcn-style workflow** — copy the component, read the prop table, ship it.
|
|
26
|
+
- **Tailwind CSS v4** — utility-first styling with CSS variables for theming and dark mode.
|
|
27
|
+
- **Theming** — CSS variables for colors and a built-in light/dark switch.
|
|
28
|
+
- **i18n ready** — language switcher and translation helpers included.
|
|
29
|
+
- **Composable** — low-level UI primitives and high-level composite components.
|
|
30
|
+
- **TypeScript** — all components ship with full type definitions.
|
|
31
|
+
|
|
32
|
+
## Requirements
|
|
33
|
+
|
|
34
|
+
- [Svelte](https://svelte.dev) `>= 5`
|
|
35
|
+
- [SvelteKit](https://svelte.dev/docs/kit) `>= 2` (recommended)
|
|
36
|
+
|
|
37
|
+
Tailwind CSS is not a hard dependency, but the components are designed for it and the
|
|
38
|
+
built-in `styles.css` expects the shadcn design tokens.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install negodesign
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Import the compiled styles once, in your global stylesheet:
|
|
47
|
+
|
|
48
|
+
```css
|
|
49
|
+
@import "tailwindcss";
|
|
50
|
+
@import "negodesign/styles.css";
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or from your root layout:
|
|
54
|
+
|
|
55
|
+
```svelte
|
|
56
|
+
<script lang="ts">
|
|
57
|
+
import "negodesign/styles.css";
|
|
58
|
+
import "../app.css";
|
|
59
|
+
</script>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Usage
|
|
63
|
+
|
|
64
|
+
```svelte
|
|
65
|
+
<script lang="ts">
|
|
66
|
+
import { Menu } from "negodesign";
|
|
67
|
+
import { Home01Icon, Hotel01Icon } from "@hugeicons/core-free-icons";
|
|
68
|
+
|
|
69
|
+
const links = [
|
|
70
|
+
{ label: "Home", url: "/", icon: Home01Icon },
|
|
71
|
+
{ label: "Accommodation", url: "/accommodation", icon: Hotel01Icon },
|
|
72
|
+
];
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<Menu isBorder navMenu={{ links }} />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Package exports
|
|
79
|
+
|
|
80
|
+
| Export | Description |
|
|
81
|
+
| ------------------------- | ------------------------------------------------------ |
|
|
82
|
+
| `negodesign` | All components, types and utilities |
|
|
83
|
+
| `negodesign/styles.css` | Compiled global styles (design tokens, base, theme) |
|
|
84
|
+
| `negodesign/types` | Shared TypeScript types |
|
|
85
|
+
| `negodesign/i18n` | Translation helpers and dictionaries |
|
|
86
|
+
|
|
87
|
+
## Component categories
|
|
88
|
+
|
|
89
|
+
- **Navigation** — `Menu`, `SearchInput`, `Navbar`
|
|
90
|
+
- **Hero** — `SimpleHero`, `CarouselHero`
|
|
91
|
+
- **Banners** — `CtaCardImageBanner`, `CtaCardBanner`, `AdCardBanner`, `NotificationBanner`
|
|
92
|
+
- **Cards** — `CardMedia`, `CardProfile`, `CardProduct`, `CardPromotion`, `CardHighlight`
|
|
93
|
+
- **Carousels** — `CarouselBadge`, `CarouselHighlights`, `CarouselPromotion`, `CarouselGridMedia/Product/Profile/Promotion`
|
|
94
|
+
- **Item Grid** — `ItemsSimpleGrid`, `ItemsPanel`
|
|
95
|
+
- **Footer** — `Footer`
|
|
96
|
+
- **Admin Panel** — `AdminPanel`, `MenuBarSidebar`, `DataTableCore`
|
|
97
|
+
- **Login / Security** — `PageLogin`, `PageOtpVerification`, `PageForgetPassword`, `PageResetPassword`
|
|
98
|
+
- **Full Pages** — `CompanyProfile`, `ProductDetails`, `ProfileUser`, `PrivacyPolicyOrTermsOfUse`
|
|
99
|
+
|
|
100
|
+
## Documentation
|
|
101
|
+
|
|
102
|
+
During development, a full documentation site is available at `/docs`:
|
|
103
|
+
|
|
104
|
+
- **Getting Started** — `/docs`
|
|
105
|
+
- **Installation** — `/docs/installation`
|
|
106
|
+
- **Components** — `/docs/components` and `/docs/<component>`
|
|
107
|
+
|
|
108
|
+
Each component page includes its description, a copy-paste example, a link to a live demo
|
|
109
|
+
and a complete API reference (props table).
|
|
110
|
+
|
|
111
|
+
## Development
|
|
112
|
+
|
|
113
|
+
Clone the repository and install dependencies:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
git clone <repository-url>
|
|
117
|
+
cd negodesign
|
|
118
|
+
npm install
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Useful scripts:
|
|
122
|
+
|
|
123
|
+
| Command | Description |
|
|
124
|
+
| ----------------- | -------------------------------------------- |
|
|
125
|
+
| `npm run dev` | Start the SvelteKit dev server |
|
|
126
|
+
| `npm run check` | Run `svelte-check` for type/compile errors |
|
|
127
|
+
| `npm run lint` | Run ESLint |
|
|
128
|
+
| `npm test` | Run the Vitest unit/browser tests |
|
|
129
|
+
| `npm run build` | Build the demo app |
|
|
130
|
+
| `npm run package` | Build the distributable package into `dist/` |
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
134
|
Licensed under the [MIT](https://opensource.org/license/mit) License.
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { setUserTranslations } from "../i18n/config";
|
|
3
|
-
import type { NegoDesignProps } from "../types";
|
|
4
|
-
import { applyTheme, theme } from "../theme.svelte";
|
|
5
|
-
|
|
6
|
-
let { translations, children }: NegoDesignProps = $props();
|
|
7
|
-
|
|
8
|
-
$effect(() => {
|
|
9
|
-
if (translations) {
|
|
10
|
-
setUserTranslations(translations);
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
$effect(() => {
|
|
15
|
-
if (typeof document !== "undefined") {
|
|
16
|
-
applyTheme(theme.current);
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
</script>
|
|
20
|
-
|
|
21
|
-
{@render children()}
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { setUserTranslations } from "../i18n/config";
|
|
3
|
+
import type { NegoDesignProps } from "../types";
|
|
4
|
+
import { applyTheme, theme } from "../theme.svelte";
|
|
5
|
+
|
|
6
|
+
let { translations, children }: NegoDesignProps = $props();
|
|
7
|
+
|
|
8
|
+
$effect(() => {
|
|
9
|
+
if (translations) {
|
|
10
|
+
setUserTranslations(translations);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
$effect(() => {
|
|
15
|
+
if (typeof document !== "undefined") {
|
|
16
|
+
applyTheme(theme.current);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
{@render children()}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from "svelte";
|
|
3
|
-
|
|
4
|
-
/** Content wrapper for admin pages. */
|
|
5
|
-
type Props = {
|
|
6
|
-
children?: Snippet;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
let { children }: Props = $props();
|
|
10
|
-
</script>
|
|
11
|
-
|
|
12
|
-
<div class="flex flex-1 flex-col gap-4 p-4 pt-0">
|
|
13
|
-
{@render children?.()}
|
|
14
|
-
</div>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
/** Content wrapper for admin pages. */
|
|
5
|
+
type Props = {
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
let { children }: Props = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div class="flex flex-1 flex-col gap-4 p-4 pt-0">
|
|
13
|
+
{@render children?.()}
|
|
14
|
+
</div>
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import LanguageSwitcher from "../../../../ui/language-switcher/language-switcher.svelte";
|
|
3
|
-
import LightSwitch from "../../../../ui/light-switch/light-switch.svelte";
|
|
4
|
-
import { Separator } from "../../../../ui/separator/index.js";
|
|
5
|
-
import * as Sidebar from "../../../../ui/sidebar/index.js";
|
|
6
|
-
import type { Snippet } from "svelte";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Admin layout with sidebar, header, and content.
|
|
10
|
-
* @property {Snippet} appsidebar - Application sidebar
|
|
11
|
-
* @property {Snippet} content - Main content
|
|
12
|
-
* @property {Snippet} breadcrumb - Breadcrumb navigation
|
|
13
|
-
*/
|
|
14
|
-
type Props = {
|
|
15
|
-
appsidebar?: Snippet;
|
|
16
|
-
content?: Snippet;
|
|
17
|
-
breadcrumb?: Snippet;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
let { appsidebar, content, breadcrumb }: Props = $props();
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<Sidebar.Provider>
|
|
24
|
-
{@render appsidebar?.()}
|
|
25
|
-
<Sidebar.Inset>
|
|
26
|
-
<header
|
|
27
|
-
class="flex h-16 shrink-0 items-center md:justify-between gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12"
|
|
28
|
-
>
|
|
29
|
-
<div class="flex items-center gap-2 px-4">
|
|
30
|
-
<Sidebar.Trigger class="-ms-1" />
|
|
31
|
-
<Separator
|
|
32
|
-
orientation="vertical"
|
|
33
|
-
class="me-2 data-[orientation=vertical]:h-4"
|
|
34
|
-
/>
|
|
35
|
-
{@render breadcrumb?.()}
|
|
36
|
-
</div>
|
|
37
|
-
<div class="pr-5">
|
|
38
|
-
<div class="flex gap-3">
|
|
39
|
-
<LanguageSwitcher />
|
|
40
|
-
<LightSwitch />
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
</header>
|
|
44
|
-
{@render content?.()}
|
|
45
|
-
</Sidebar.Inset>
|
|
46
|
-
</Sidebar.Provider>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import LanguageSwitcher from "../../../../ui/language-switcher/language-switcher.svelte";
|
|
3
|
+
import LightSwitch from "../../../../ui/light-switch/light-switch.svelte";
|
|
4
|
+
import { Separator } from "../../../../ui/separator/index.js";
|
|
5
|
+
import * as Sidebar from "../../../../ui/sidebar/index.js";
|
|
6
|
+
import type { Snippet } from "svelte";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Admin layout with sidebar, header, and content.
|
|
10
|
+
* @property {Snippet} appsidebar - Application sidebar
|
|
11
|
+
* @property {Snippet} content - Main content
|
|
12
|
+
* @property {Snippet} breadcrumb - Breadcrumb navigation
|
|
13
|
+
*/
|
|
14
|
+
type Props = {
|
|
15
|
+
appsidebar?: Snippet;
|
|
16
|
+
content?: Snippet;
|
|
17
|
+
breadcrumb?: Snippet;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
let { appsidebar, content, breadcrumb }: Props = $props();
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<Sidebar.Provider>
|
|
24
|
+
{@render appsidebar?.()}
|
|
25
|
+
<Sidebar.Inset>
|
|
26
|
+
<header
|
|
27
|
+
class="flex h-16 shrink-0 items-center md:justify-between gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12"
|
|
28
|
+
>
|
|
29
|
+
<div class="flex items-center gap-2 px-4">
|
|
30
|
+
<Sidebar.Trigger class="-ms-1" />
|
|
31
|
+
<Separator
|
|
32
|
+
orientation="vertical"
|
|
33
|
+
class="me-2 data-[orientation=vertical]:h-4"
|
|
34
|
+
/>
|
|
35
|
+
{@render breadcrumb?.()}
|
|
36
|
+
</div>
|
|
37
|
+
<div class="pr-5">
|
|
38
|
+
<div class="flex gap-3">
|
|
39
|
+
<LanguageSwitcher />
|
|
40
|
+
<LightSwitch />
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</header>
|
|
44
|
+
{@render content?.()}
|
|
45
|
+
</Sidebar.Inset>
|
|
46
|
+
</Sidebar.Provider>
|
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
/**
|
|
3
|
-
* Lista vertical de ficheiros/thumbnails do produto com seleção de imagem/vídeo.
|
|
4
|
-
* Quando `isLoading` é true, exibe Skeletons.
|
|
5
|
-
* Se houver um item do tipo "video", ele é priorizado (aparece primeiro).
|
|
6
|
-
* @component
|
|
7
|
-
* @example
|
|
8
|
-
* ```svelte
|
|
9
|
-
* <ProductDetailFilesVertical files={data.gallery} />
|
|
10
|
-
* ```
|
|
11
|
-
*/
|
|
12
|
-
</script>
|
|
13
|
-
|
|
14
|
-
<script lang="ts">
|
|
15
|
-
import Skeleton from "../../../ui/skeleton/skeleton.svelte";
|
|
16
|
-
import type { ProductDetailsImage } from "../types";
|
|
17
|
-
|
|
18
|
-
let {
|
|
19
|
-
files,
|
|
20
|
-
className,
|
|
21
|
-
isLoading = false,
|
|
22
|
-
}: {
|
|
23
|
-
files: ProductDetailsImage[];
|
|
24
|
-
className?: string;
|
|
25
|
-
isLoading?: boolean;
|
|
26
|
-
} = $props();
|
|
27
|
-
|
|
28
|
-
// Vídeo primeiro, mantendo a ordem relativa do resto
|
|
29
|
-
const sortedFiles = $derived(
|
|
30
|
-
[...(files ?? [])].sort(
|
|
31
|
-
(a, b) => (a.type === "video" ? 0 : 1) - (b.type === "video" ? 0 : 1),
|
|
32
|
-
),
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
let activeIndex = $state(0);
|
|
36
|
-
const active = $derived(sortedFiles[activeIndex]);
|
|
37
|
-
</script>
|
|
38
|
-
|
|
39
|
-
{#snippet media(file: ProductDetailsImage, cls: string)}
|
|
40
|
-
{#if file.type === "video"}
|
|
41
|
-
<video src={file.src} class={cls} muted playsinline></video>
|
|
42
|
-
{:else}
|
|
43
|
-
<img src={file.src} alt={file.alt ?? ""} class={cls} />
|
|
44
|
-
{/if}
|
|
45
|
-
{/snippet}
|
|
46
|
-
|
|
47
|
-
<div class="flex flex-col gap-3 {className}">
|
|
48
|
-
{#if isLoading}
|
|
49
|
-
<div class="flex gap-3">
|
|
50
|
-
<div class="flex shrink-0 flex-col gap-2">
|
|
51
|
-
{#each Array.from( { length: Math.min(files.length || 3, 4) }, ) as _, i (i)}
|
|
52
|
-
<Skeleton class="h-16 w-16 rounded-md" />
|
|
53
|
-
{/each}
|
|
54
|
-
</div>
|
|
55
|
-
<Skeleton class="aspect-4/3 min-w-0 flex-1 rounded-lg" />
|
|
56
|
-
</div>
|
|
57
|
-
{:else if sortedFiles.length > 0}
|
|
58
|
-
<div class="flex gap-3">
|
|
59
|
-
<div class="flex shrink-0 flex-col gap-2">
|
|
60
|
-
{#each sortedFiles as file, i (file.src)}
|
|
61
|
-
<button
|
|
62
|
-
type="button"
|
|
63
|
-
aria-label={file.alt ?? `Item ${i + 1}`}
|
|
64
|
-
onclick={() => (activeIndex = i)}
|
|
65
|
-
class="relative overflow-hidden rounded-md border transition-opacity {i ===
|
|
66
|
-
activeIndex
|
|
67
|
-
? 'border-foreground opacity-100'
|
|
68
|
-
: 'border-muted opacity-60 hover:opacity-100'}"
|
|
69
|
-
>
|
|
70
|
-
{@render media(file, "h-16 w-16 object-cover")}
|
|
71
|
-
{#if file.type === "video"}
|
|
72
|
-
<span
|
|
73
|
-
class="absolute inset-0 flex items-center justify-center bg-black/30"
|
|
74
|
-
>
|
|
75
|
-
<svg
|
|
76
|
-
viewBox="0 0 24 24"
|
|
77
|
-
class="h-4 w-4 fill-white"
|
|
78
|
-
aria-hidden="true"
|
|
79
|
-
>
|
|
80
|
-
<path d="M8 5v14l11-7z" />
|
|
81
|
-
</svg>
|
|
82
|
-
</span>
|
|
83
|
-
{/if}
|
|
84
|
-
</button>
|
|
85
|
-
{/each}
|
|
86
|
-
</div>
|
|
87
|
-
|
|
88
|
-
<div class="min-w-0 flex-1 overflow-hidden rounded-lg border">
|
|
89
|
-
{#if active.type === "video"}
|
|
90
|
-
<!-- svelte-ignore a11y_media_has_caption -->
|
|
91
|
-
<video
|
|
92
|
-
src={active.src}
|
|
93
|
-
class="aspect-4/3 w-full object-cover"
|
|
94
|
-
controls
|
|
95
|
-
playsinline
|
|
96
|
-
></video>
|
|
97
|
-
{:else}
|
|
98
|
-
{@render media(active, "aspect-4/3 w-full object-cover")}
|
|
99
|
-
{/if}
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
{/if}
|
|
103
|
-
</div>
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/**
|
|
3
|
+
* Lista vertical de ficheiros/thumbnails do produto com seleção de imagem/vídeo.
|
|
4
|
+
* Quando `isLoading` é true, exibe Skeletons.
|
|
5
|
+
* Se houver um item do tipo "video", ele é priorizado (aparece primeiro).
|
|
6
|
+
* @component
|
|
7
|
+
* @example
|
|
8
|
+
* ```svelte
|
|
9
|
+
* <ProductDetailFilesVertical files={data.gallery} />
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
import Skeleton from "../../../ui/skeleton/skeleton.svelte";
|
|
16
|
+
import type { ProductDetailsImage } from "../types";
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
files,
|
|
20
|
+
className,
|
|
21
|
+
isLoading = false,
|
|
22
|
+
}: {
|
|
23
|
+
files: ProductDetailsImage[];
|
|
24
|
+
className?: string;
|
|
25
|
+
isLoading?: boolean;
|
|
26
|
+
} = $props();
|
|
27
|
+
|
|
28
|
+
// Vídeo primeiro, mantendo a ordem relativa do resto
|
|
29
|
+
const sortedFiles = $derived(
|
|
30
|
+
[...(files ?? [])].sort(
|
|
31
|
+
(a, b) => (a.type === "video" ? 0 : 1) - (b.type === "video" ? 0 : 1),
|
|
32
|
+
),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
let activeIndex = $state(0);
|
|
36
|
+
const active = $derived(sortedFiles[activeIndex]);
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
{#snippet media(file: ProductDetailsImage, cls: string)}
|
|
40
|
+
{#if file.type === "video"}
|
|
41
|
+
<video src={file.src} class={cls} muted playsinline></video>
|
|
42
|
+
{:else}
|
|
43
|
+
<img src={file.src} alt={file.alt ?? ""} class={cls} />
|
|
44
|
+
{/if}
|
|
45
|
+
{/snippet}
|
|
46
|
+
|
|
47
|
+
<div class="flex flex-col gap-3 {className}">
|
|
48
|
+
{#if isLoading}
|
|
49
|
+
<div class="flex gap-3">
|
|
50
|
+
<div class="flex shrink-0 flex-col gap-2">
|
|
51
|
+
{#each Array.from( { length: Math.min(files.length || 3, 4) }, ) as _, i (i)}
|
|
52
|
+
<Skeleton class="h-16 w-16 rounded-md" />
|
|
53
|
+
{/each}
|
|
54
|
+
</div>
|
|
55
|
+
<Skeleton class="aspect-4/3 min-w-0 flex-1 rounded-lg" />
|
|
56
|
+
</div>
|
|
57
|
+
{:else if sortedFiles.length > 0}
|
|
58
|
+
<div class="flex gap-3">
|
|
59
|
+
<div class="flex shrink-0 flex-col gap-2">
|
|
60
|
+
{#each sortedFiles as file, i (file.src)}
|
|
61
|
+
<button
|
|
62
|
+
type="button"
|
|
63
|
+
aria-label={file.alt ?? `Item ${i + 1}`}
|
|
64
|
+
onclick={() => (activeIndex = i)}
|
|
65
|
+
class="relative overflow-hidden rounded-md border transition-opacity {i ===
|
|
66
|
+
activeIndex
|
|
67
|
+
? 'border-foreground opacity-100'
|
|
68
|
+
: 'border-muted opacity-60 hover:opacity-100'}"
|
|
69
|
+
>
|
|
70
|
+
{@render media(file, "h-16 w-16 object-cover")}
|
|
71
|
+
{#if file.type === "video"}
|
|
72
|
+
<span
|
|
73
|
+
class="absolute inset-0 flex items-center justify-center bg-black/30"
|
|
74
|
+
>
|
|
75
|
+
<svg
|
|
76
|
+
viewBox="0 0 24 24"
|
|
77
|
+
class="h-4 w-4 fill-white"
|
|
78
|
+
aria-hidden="true"
|
|
79
|
+
>
|
|
80
|
+
<path d="M8 5v14l11-7z" />
|
|
81
|
+
</svg>
|
|
82
|
+
</span>
|
|
83
|
+
{/if}
|
|
84
|
+
</button>
|
|
85
|
+
{/each}
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div class="min-w-0 flex-1 overflow-hidden rounded-lg border">
|
|
89
|
+
{#if active.type === "video"}
|
|
90
|
+
<!-- svelte-ignore a11y_media_has_caption -->
|
|
91
|
+
<video
|
|
92
|
+
src={active.src}
|
|
93
|
+
class="aspect-4/3 w-full object-cover"
|
|
94
|
+
controls
|
|
95
|
+
playsinline
|
|
96
|
+
></video>
|
|
97
|
+
{:else}
|
|
98
|
+
{@render media(active, "aspect-4/3 w-full object-cover")}
|
|
99
|
+
{/if}
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
{/if}
|
|
103
|
+
</div>
|