@stacksjs/defaults 0.70.54
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.md +21 -0
- package/README.md +2 -0
- package/package.json +20 -0
- package/resources/assets/fonts/013717e9.woff +0 -0
- package/resources/assets/fonts/03355817.woff2 +0 -0
- package/resources/assets/fonts/0380de7e.woff2 +0 -0
- package/resources/assets/fonts/0ba5d32a.woff +0 -0
- package/resources/assets/fonts/0bd57ddf.woff2 +0 -0
- package/resources/assets/fonts/19de0a60.woff2 +0 -0
- package/resources/assets/fonts/20b87e84.woff +0 -0
- package/resources/assets/fonts/4bbc67e1.woff +0 -0
- package/resources/assets/fonts/576fb0d7.woff +0 -0
- package/resources/assets/fonts/5ea10ee5.woff2 +0 -0
- package/resources/assets/fonts/6413fc0a.woff +0 -0
- package/resources/assets/fonts/72c114cf.woff +0 -0
- package/resources/assets/fonts/73c57e73.woff2 +0 -0
- package/resources/assets/fonts/813dca6c.woff +0 -0
- package/resources/assets/fonts/8a9b0995.woff2 +0 -0
- package/resources/assets/fonts/90b4832f.woff +0 -0
- package/resources/assets/fonts/9394ab85.woff2 +0 -0
- package/resources/assets/fonts/9b3c7d7f.woff2 +0 -0
- package/resources/assets/fonts/Monaco.ttf +0 -0
- package/resources/assets/fonts/b1b36d74.woff +0 -0
- package/resources/assets/fonts/b5d74798.woff2 +0 -0
- package/resources/assets/fonts/b9f81a4d.woff +0 -0
- package/resources/assets/fonts/c9784008.woff2 +0 -0
- package/resources/assets/fonts/cf692227.woff +0 -0
- package/resources/assets/fonts/d1c6ab51.woff2 +0 -0
- package/resources/assets/fonts/e2eb62fc.woff +0 -0
- package/resources/assets/fonts/fe21f8d8.woff2 +0 -0
- package/resources/assets/fonts/nps/CampmateScript-Regular.woff2 +0 -0
- package/resources/assets/fonts/nps/NPS_2026-variable.woff2 +0 -0
- package/resources/assets/fonts/nps/RedwoodSerif-Regular.woff2 +0 -0
- package/resources/assets/fonts/nps/SequoiaSans-Regular.woff2 +0 -0
- package/resources/assets/fonts/nps/Switchback-Regular.woff2 +0 -0
- package/resources/assets/images/app-icon.png +0 -0
- package/resources/assets/images/bg-img.png +0 -0
- package/resources/assets/images/park-ridge.svg +15 -0
- package/resources/assets/images/topography.svg +1 -0
- package/resources/assets/scripts/main.ts +2153 -0
- package/resources/assets/styles/README.md +19 -0
- package/resources/assets/styles/docs/main.css +1 -0
- package/resources/assets/styles/docs/overrides.css +1 -0
- package/resources/assets/styles/docs/vars.css +8 -0
- package/resources/assets/styles/main.css +1419 -0
- package/resources/components/Button.stx +70 -0
- package/resources/components/Buttons/BaseButton.stx +39 -0
- package/resources/components/Buttons/Counter.stx +28 -0
- package/resources/components/Buttons/ToggleDark.stx +11 -0
- package/resources/components/Container.stx +10 -0
- package/resources/components/Dashboard/.gitkeep +0 -0
- package/resources/components/Dashboard/Auth/ForgotPassword.stx +127 -0
- package/resources/components/Dashboard/Auth/Login.stx +212 -0
- package/resources/components/Dashboard/Auth/Register.stx +306 -0
- package/resources/components/Dashboard/Avatar.stx +123 -0
- package/resources/components/Dashboard/Billing/ActivePlan.stx +57 -0
- package/resources/components/Dashboard/Billing/CardBrands.stx +32 -0
- package/resources/components/Dashboard/Billing/CardForm.stx +34 -0
- package/resources/components/Dashboard/Billing/OneTimePayment.stx +28 -0
- package/resources/components/Dashboard/Billing/PaymentForm.stx +33 -0
- package/resources/components/Dashboard/Billing/PaymentMethod.stx +66 -0
- package/resources/components/Dashboard/Billing/PaymentMethodList.stx +52 -0
- package/resources/components/Dashboard/Billing/Plans.stx +136 -0
- package/resources/components/Dashboard/Buttons/AppButton.stx +48 -0
- package/resources/components/Dashboard/Buttons/IconButton.stx +20 -0
- package/resources/components/Dashboard/Card.stx +78 -0
- package/resources/components/Dashboard/Chart.stx +75 -0
- package/resources/components/Dashboard/CodeEditor.stx +41 -0
- package/resources/components/Dashboard/Commerce/BulkActionBar.stx +70 -0
- package/resources/components/Dashboard/Commerce/CategoriesTable.stx +133 -0
- package/resources/components/Dashboard/Commerce/CouponsTable.stx +161 -0
- package/resources/components/Dashboard/Commerce/CustomersTable.stx +142 -0
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesTable.stx +80 -0
- package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveryTable.stx +85 -0
- package/resources/components/Dashboard/Commerce/Delivery/DriversTable.stx +99 -0
- package/resources/components/Dashboard/Commerce/Delivery/LicenseKeysTable.stx +92 -0
- package/resources/components/Dashboard/Commerce/Delivery/LicenseTemplatesTable.stx +88 -0
- package/resources/components/Dashboard/Commerce/Delivery/Pagination.stx +122 -0
- package/resources/components/Dashboard/Commerce/Delivery/SearchFilter.stx +23 -0
- package/resources/components/Dashboard/Commerce/Delivery/ShippingMethodsTable.stx +80 -0
- package/resources/components/Dashboard/Commerce/Delivery/ShippingRatesTable.stx +59 -0
- package/resources/components/Dashboard/Commerce/Delivery/ShippingZonesTable.stx +71 -0
- package/resources/components/Dashboard/Commerce/Delivery/TabNavigation.stx +36 -0
- package/resources/components/Dashboard/Commerce/Forms/CouponsForm.stx +191 -0
- package/resources/components/Dashboard/Commerce/OrdersTable.stx +131 -0
- package/resources/components/Dashboard/Commerce/ProductsTable.stx +170 -0
- package/resources/components/Dashboard/CreateRecordModal.stx +363 -0
- package/resources/components/Dashboard/DashboardLayout.stx +155 -0
- package/resources/components/Dashboard/Deployments/ActivityFeed.stx +54 -0
- package/resources/components/Dashboard/Deployments/DeployScript.stx +23 -0
- package/resources/components/Dashboard/Deployments/DeploymentHistory.stx +97 -0
- package/resources/components/Dashboard/Deployments/DeploymentList.stx +94 -0
- package/resources/components/Dashboard/Deployments/LiveTerminalOutput.stx +18 -0
- package/resources/components/Dashboard/Elements/Dropdown.stx +57 -0
- package/resources/components/Dashboard/Email/EmailActivityChart.stx +111 -0
- package/resources/components/Dashboard/Email/EmailCompose.stx +72 -0
- package/resources/components/Dashboard/Email/EmailDetail.stx +105 -0
- package/resources/components/Dashboard/Email/EmailList.stx +117 -0
- package/resources/components/Dashboard/Email/EmailSearch.stx +40 -0
- package/resources/components/Dashboard/Email/EmailSidebar.stx +133 -0
- package/resources/components/Dashboard/GlobalSearch.stx +124 -0
- package/resources/components/Dashboard/MobileSidebar.stx +47 -0
- package/resources/components/Dashboard/Modals/BaseModal.stx +50 -0
- package/resources/components/Dashboard/Modals/Popups/Alert.stx +112 -0
- package/resources/components/Dashboard/Modals/Popups/Toast.stx +70 -0
- package/resources/components/Dashboard/Modals/ToastWrapper.stx +35 -0
- package/resources/components/Dashboard/Monitoring/ErrorDetailModal.stx +228 -0
- package/resources/components/Dashboard/Monitoring/ErrorsTable.stx +184 -0
- package/resources/components/Dashboard/Navbar.stx +194 -0
- package/resources/components/Dashboard/NavbarModern.stx +188 -0
- package/resources/components/Dashboard/NotificationErrorModal.stx +105 -0
- package/resources/components/Dashboard/NotificationStatusBadge.stx +20 -0
- package/resources/components/Dashboard/Pagination.stx +90 -0
- package/resources/components/Dashboard/Queue/QueueTable.stx +127 -0
- package/resources/components/Dashboard/Settings/Forms/AIForm.stx +126 -0
- package/resources/components/Dashboard/Settings/Forms/AnalyticsForm.stx +52 -0
- package/resources/components/Dashboard/Settings/Forms/AppForm.stx +157 -0
- package/resources/components/Dashboard/Settings/Forms/CLIForm.stx +66 -0
- package/resources/components/Dashboard/Settings/Forms/CacheForm.stx +253 -0
- package/resources/components/Dashboard/Settings/Forms/CloudForm.stx +441 -0
- package/resources/components/Dashboard/Settings/Forms/DNSForm.stx +307 -0
- package/resources/components/Dashboard/Settings/Forms/DatabaseForm.stx +168 -0
- package/resources/components/Dashboard/Settings/Forms/EmailForm.stx +100 -0
- package/resources/components/Dashboard/Settings/Forms/FileSystemForm.stx +259 -0
- package/resources/components/Dashboard/Settings/Forms/HashingForm.stx +119 -0
- package/resources/components/Dashboard/Settings/Forms/LibraryForm.stx +389 -0
- package/resources/components/Dashboard/Settings/Forms/QueueForm.stx +219 -0
- package/resources/components/Dashboard/Settings/Forms/SearchEngineForm.stx +48 -0
- package/resources/components/Dashboard/Settings/Forms/SecurityForm.stx +143 -0
- package/resources/components/Dashboard/Settings/Forms/ServicesForm.stx +194 -0
- package/resources/components/Dashboard/Settings/Forms/StorageForm.stx +191 -0
- package/resources/components/Dashboard/Settings/Forms/TeamsForm.stx +33 -0
- package/resources/components/Dashboard/Settings/SettingsFormManager.stx +181 -0
- package/resources/components/Dashboard/SettingsHeader.stx +88 -0
- package/resources/components/Dashboard/SettingsSidebar.stx +234 -0
- package/resources/components/Dashboard/Sidebar.stx +23 -0
- package/resources/components/Dashboard/SidebarModern.stx +249 -0
- package/resources/components/Dashboard/Skeleton/LoadingCard.stx +15 -0
- package/resources/components/Dashboard/Skeleton/LoadingDetails.stx +26 -0
- package/resources/components/Dashboard/Table.stx +82 -0
- package/resources/components/Dashboard/TerminalContainer.stx +97 -0
- package/resources/components/Dashboard/Tooltip.stx +166 -0
- package/resources/components/Dashboard/Transaction/index.stx +88 -0
- package/resources/components/Dashboard/UI/ActivityTable.stx +54 -0
- package/resources/components/Dashboard/UI/Avatar.stx +102 -0
- package/resources/components/Dashboard/UI/Badge.stx +66 -0
- package/resources/components/Dashboard/UI/Button.stx +73 -0
- package/resources/components/Dashboard/UI/Card.stx +51 -0
- package/resources/components/Dashboard/UI/ChartCard.stx +36 -0
- package/resources/components/Dashboard/UI/Checkbox.stx +74 -0
- package/resources/components/Dashboard/UI/ConfirmDialog.stx +74 -0
- package/resources/components/Dashboard/UI/DataTable.stx +45 -0
- package/resources/components/Dashboard/UI/Dropdown.stx +72 -0
- package/resources/components/Dashboard/UI/DropdownItem.stx +80 -0
- package/resources/components/Dashboard/UI/EmptyState.stx +50 -0
- package/resources/components/Dashboard/UI/FilterBar.stx +48 -0
- package/resources/components/Dashboard/UI/Input.stx +77 -0
- package/resources/components/Dashboard/UI/Modal.stx +75 -0
- package/resources/components/Dashboard/UI/PageHeader.stx +69 -0
- package/resources/components/Dashboard/UI/PageLayout.stx +84 -0
- package/resources/components/Dashboard/UI/Pagination.stx +126 -0
- package/resources/components/Dashboard/UI/QuickLinks.stx +31 -0
- package/resources/components/Dashboard/UI/RouterLink.stx +31 -0
- package/resources/components/Dashboard/UI/Select.stx +84 -0
- package/resources/components/Dashboard/UI/ServiceHealth.stx +38 -0
- package/resources/components/Dashboard/UI/StatsCard.stx +79 -0
- package/resources/components/Dashboard/UI/Table.stx +143 -0
- package/resources/components/Dashboard/UI/Tabs.stx +45 -0
- package/resources/components/Dashboard/UI/Textarea.stx +93 -0
- package/resources/components/Dashboard/UI/Toggle.stx +84 -0
- package/resources/components/Dashboard/UI/WindowControls.stx +135 -0
- package/resources/components/Dashboard/Widget.stx +89 -0
- package/resources/components/Dashboard/WindowControls.stx +119 -0
- package/resources/components/Dashboard/index.ts +106 -0
- package/resources/components/Docs/Demo/ComboboxCode.md +114 -0
- package/resources/components/Docs/Demo/ComboboxDemo.stx +91 -0
- package/resources/components/Docs/Demo/DialogCode.md +42 -0
- package/resources/components/Docs/Demo/DialogDemo.stx +76 -0
- package/resources/components/Docs/Demo/DocsPlayground.stx +39 -0
- package/resources/components/Docs/Demo/DropdownCode.md +109 -0
- package/resources/components/Docs/Demo/DropdownDemo.stx +82 -0
- package/resources/components/Docs/Demo/Hero.stx +100 -0
- package/resources/components/Docs/Demo/ListboxCode.md +87 -0
- package/resources/components/Docs/Demo/ListboxDemo.stx +59 -0
- package/resources/components/Docs/Demo/NotificationCode.md +173 -0
- package/resources/components/Docs/Demo/NotificationDemo.stx +99 -0
- package/resources/components/Docs/Demo/PopoverCode.md +161 -0
- package/resources/components/Docs/Demo/PopoverDemo.stx +96 -0
- package/resources/components/Docs/Demo/RadioGroupCode.md +98 -0
- package/resources/components/Docs/Demo/RadioGroupDemo.stx +53 -0
- package/resources/components/Docs/Demo/StepperCode.md +216 -0
- package/resources/components/Docs/Demo/StepperDemo.stx +137 -0
- package/resources/components/Docs/Demo/SwitchCode.md +24 -0
- package/resources/components/Docs/Demo/SwitchDemo.stx +28 -0
- package/resources/components/Docs/Home.stx +7 -0
- package/resources/components/Docs/HomeContributors.stx +23 -0
- package/resources/components/Docs/HomeSponsors.stx +17 -0
- package/resources/components/Docs/HomeTeam.stx +18 -0
- package/resources/components/Docs/TeamMember.stx +79 -0
- package/resources/components/Email/EmailButton.stx +32 -0
- package/resources/components/Email/EmailDivider.stx +20 -0
- package/resources/components/Email/EmailImage.stx +24 -0
- package/resources/components/Email/EmailLayout.stx +55 -0
- package/resources/components/Email/EmailLink.stx +22 -0
- package/resources/components/Email/EmailSection.stx +20 -0
- package/resources/components/Email/EmailText.stx +30 -0
- package/resources/components/Forum/ForumBreadcrumb.stx +38 -0
- package/resources/components/Forum/ForumCategory.stx +16 -0
- package/resources/components/Forum/ForumCategoryItem.stx +46 -0
- package/resources/components/Forum/ForumLayout.stx +36 -0
- package/resources/components/Forum/ForumPagination.stx +39 -0
- package/resources/components/Forum/ForumPost.stx +71 -0
- package/resources/components/Forum/ForumReplyForm.stx +76 -0
- package/resources/components/Forum/ForumSidebar.stx +86 -0
- package/resources/components/Forum/ForumTopicItem.stx +77 -0
- package/resources/components/Forum/ForumTopicList.stx +23 -0
- package/resources/components/HelloWorld.stx +253 -0
- package/resources/components/Logo.stx +10 -0
- package/resources/components/Marketing/CallToAction.stx +30 -0
- package/resources/components/Marketing/ComingSoon.stx +155 -0
- package/resources/components/Marketing/Faqs.stx +99 -0
- package/resources/components/Marketing/Feature.stx +32 -0
- package/resources/components/Marketing/Fields.stx +28 -0
- package/resources/components/Marketing/Footer.stx +33 -0
- package/resources/components/Marketing/Header.stx +51 -0
- package/resources/components/Marketing/Hero.stx +46 -0
- package/resources/components/Marketing/Layout.stx +26 -0
- package/resources/components/Marketing/NavLink.stx +13 -0
- package/resources/components/Marketing/Plan.stx +43 -0
- package/resources/components/Marketing/Pricing.stx +120 -0
- package/resources/components/Marketing/PrimaryFeatures.stx +80 -0
- package/resources/components/Marketing/PrimaryFeatures2.stx +98 -0
- package/resources/components/Marketing/SecondaryFeatures.stx +80 -0
- package/resources/components/Marketing/Testimonials.stx +120 -0
- package/resources/components/Pagination.stx +149 -0
- package/resources/components/Playground.stx +9 -0
- package/resources/components/README.md +87 -0
- package/resources/components/Storefront/AnnouncementBar.stx +17 -0
- package/resources/components/Storefront/CartDrawer.stx +227 -0
- package/resources/components/Storefront/CheckoutSteps.stx +50 -0
- package/resources/components/Storefront/Footer.stx +81 -0
- package/resources/components/Storefront/Header.stx +66 -0
- package/resources/components/Storefront/OrderSummary.stx +79 -0
- package/resources/components/Storefront/ProductCard.stx +63 -0
- package/resources/components/Storefront/ProductGallery.stx +88 -0
- package/resources/components/Storefront/QuantitySelector.stx +97 -0
- package/resources/components/index.ts +13 -0
- package/resources/emails/email-verification.stx +46 -0
- package/resources/emails/layouts/base.html +113 -0
- package/resources/emails/password-changed.stx +55 -0
- package/resources/emails/password-reset.stx +43 -0
- package/resources/emails/subscription-confirmation.stx +99 -0
- package/resources/emails/welcome.stx +48 -0
- package/resources/functions/README.md +28 -0
- package/resources/functions/counter.ts +13 -0
- package/resources/functions/dark.ts +4 -0
- package/resources/functions/dashboard/analytics.ts +238 -0
- package/resources/functions/dashboard/config-io.ts +316 -0
- package/resources/functions/dashboard/data.ts +375 -0
- package/resources/functions/dashboard/library.ts +250 -0
- package/resources/functions/dashboard/sidebar.ts +537 -0
- package/resources/functions/dashboard/system.ts +150 -0
- package/resources/layouts/403.stx +188 -0
- package/resources/layouts/404.stx +34 -0
- package/resources/layouts/README.md +14 -0
- package/resources/layouts/dashboard/default.stx +20 -0
- package/resources/layouts/dashboard/guest.stx +10 -0
- package/resources/layouts/default.stx +3 -0
- package/resources/layouts/maintenance.stx +11 -0
- package/resources/layouts/storefront.stx +73 -0
- package/resources/layouts/system-tray/default.stx +3 -0
- package/resources/modules/.gitkeep +0 -0
- package/resources/plugins/.gitkeep +0 -0
- package/resources/plugins/preloader.ts +308 -0
- package/resources/types/README.md +3 -0
- package/resources/views/cart.stx +149 -0
- package/resources/views/checkout/contact.stx +101 -0
- package/resources/views/checkout/payment.stx +114 -0
- package/resources/views/checkout/shipping.stx +120 -0
- package/resources/views/coming-soon.stx +9 -0
- package/resources/views/dashboard/custom-page.stx +45 -0
- package/resources/views/dashboard/index.stx +24 -0
- package/resources/views/emails/welcome.stx +0 -0
- package/resources/views/errors/tester.stx +26 -0
- package/resources/views/index.stx +2865 -0
- package/resources/views/orders/[id].stx +130 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
interface QuickLinksProps {
|
|
3
|
+
title?: string
|
|
4
|
+
links?: any[]
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const { title = 'Quick Links', links = [] } = defineProps<QuickLinksProps>()
|
|
8
|
+
</script>
|
|
9
|
+
<div class="bg-white dark:bg-neutral-800/90 border border-black/5 rounded-xl dark:border-white/5">
|
|
10
|
+
<div class="p-5 border-b border-neutral-200/60 dark:border-white/5">
|
|
11
|
+
<h3 class="font-semibold text-[14px] text-neutral-900 tracking-tight dark:text-white">{{ title }}</h3>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="divide-neutral-100 divide-y dark:divide-white/5">
|
|
14
|
+
@foreach(links as link)
|
|
15
|
+
<a href="{{ link.href || '#' }}" class="flex gap-3.5 items-center px-5 py-3.5 hover:bg-neutral-50 dark:hover:bg-white/5 transition-colors group">
|
|
16
|
+
<div class="flex flex-shrink-0 items-center justify-center h-9 w-9 bg-neutral-100 dark:bg-white/10 rounded-lg">
|
|
17
|
+
@if(link.icon)
|
|
18
|
+
<div class="{{ link.icon }} w-4.5 h-4.5 text-neutral-500 dark:text-neutral-400"></div>
|
|
19
|
+
@else
|
|
20
|
+
<span class="font-semibold text-[13px] text-neutral-500 dark:text-neutral-400">{{ link.title.charAt(0) }}</span>
|
|
21
|
+
@endif
|
|
22
|
+
</div>
|
|
23
|
+
<div class="flex-1 min-w-0">
|
|
24
|
+
<div class="font-medium text-[13px] text-neutral-900 dark:text-white group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors">{{ link.title }}</div>
|
|
25
|
+
<div class="text-[12px] text-neutral-500 truncate dark:text-neutral-400">{{ link.description }}</div>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="flex-shrink-0 h-4 w-4 text-neutral-300 dark:text-neutral-600 group-hover:text-neutral-400 dark:group-hover:text-neutral-500 transition-colors i-hugeicons-arrow-right-01"></div>
|
|
28
|
+
</a>
|
|
29
|
+
@endforeach
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
/**
|
|
3
|
+
* RouterLink — bridge component for templates that use Vue Router-style
|
|
4
|
+
* `<RouterLink to="…">` syntax. The dashboard is server-rendered MPA, so
|
|
5
|
+
* there's no actual client router; this just maps the `to` prop to a
|
|
6
|
+
* standard `href` and forwards the rest. Keeps existing pages from
|
|
7
|
+
* breaking with `[Error loading component: 'router-link']` while we
|
|
8
|
+
* migrate them off the syntax.
|
|
9
|
+
*
|
|
10
|
+
* `replace` is accepted for parity with Vue's RouterLink; it has no
|
|
11
|
+
* effect under MPA navigation but makes drop-in usage error-free.
|
|
12
|
+
*/
|
|
13
|
+
interface RouterLinkProps {
|
|
14
|
+
to?: string
|
|
15
|
+
href?: string
|
|
16
|
+
replace?: boolean
|
|
17
|
+
target?: string
|
|
18
|
+
rel?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { to = '', href = '', target = '', rel = '' } = defineProps<RouterLinkProps>()
|
|
22
|
+
const finalHref = href || to
|
|
23
|
+
const finalRel = rel || (target === '_blank' ? 'noopener' : '')
|
|
24
|
+
</script>
|
|
25
|
+
<a
|
|
26
|
+
href="{{ finalHref }}"
|
|
27
|
+
@if(target) target="{{ target }}" @endif
|
|
28
|
+
@if(finalRel) rel="{{ finalRel }}" @endif
|
|
29
|
+
>
|
|
30
|
+
<slot />
|
|
31
|
+
</a>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
interface SelectProps {
|
|
3
|
+
value?: string
|
|
4
|
+
options?: any[]
|
|
5
|
+
placeholder?: string
|
|
6
|
+
label?: string
|
|
7
|
+
error?: string
|
|
8
|
+
hint?: string
|
|
9
|
+
disabled?: boolean
|
|
10
|
+
required?: boolean
|
|
11
|
+
size?: 'xs' | 'sm' | 'md' | 'lg'
|
|
12
|
+
name?: string
|
|
13
|
+
id?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { value = '', options = [], placeholder = 'Select an option', label = '', error = '', hint = '', disabled = false, required = false, size = 'md', name = '', id = '' } = defineProps<SelectProps>()
|
|
17
|
+
|
|
18
|
+
const sizeClasses = {
|
|
19
|
+
xs: 'h-6 text-[11px] px-2',
|
|
20
|
+
sm: 'h-7 text-[12px] px-2.5',
|
|
21
|
+
md: 'h-8 text-[13px] px-3',
|
|
22
|
+
lg: 'h-9 text-[14px] px-3.5'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const borderClass = error
|
|
26
|
+
? 'border-red-400/60 dark:border-red-500/60 focus:ring-red-500/30 focus:border-red-500'
|
|
27
|
+
: 'border-black/10 dark:border-white/15 focus:ring-blue-500/40 focus:border-blue-500/50 hover:border-black/20 dark:hover:border-white/25'
|
|
28
|
+
|
|
29
|
+
const disabledClass = disabled
|
|
30
|
+
? 'bg-black/5 dark:bg-white/5 text-neutral-400 cursor-not-allowed border-black/5 dark:border-white/10 opacity-40'
|
|
31
|
+
: 'bg-white dark:bg-white/10'
|
|
32
|
+
|
|
33
|
+
const selectClass = [
|
|
34
|
+
'w-full flex items-center justify-between gap-2 rounded-md border transition-all duration-150',
|
|
35
|
+
'focus:outline-none focus:ring-2',
|
|
36
|
+
sizeClasses[size],
|
|
37
|
+
borderClass,
|
|
38
|
+
disabledClass
|
|
39
|
+
].join(' ')
|
|
40
|
+
|
|
41
|
+
const selectId = id || 'select-' + Math.random().toString(36).slice(2, 11)
|
|
42
|
+
</script>
|
|
43
|
+
<div class="relative">
|
|
44
|
+
@if(label)
|
|
45
|
+
<label for="{{ selectId }}" class="block mb-1.5 font-medium text-[13px] text-neutral-600 tracking-tight dark:text-neutral-400">
|
|
46
|
+
{{ label }}
|
|
47
|
+
@if(required)
|
|
48
|
+
<span class="text-red-500">*</span>
|
|
49
|
+
@endif
|
|
50
|
+
</label>
|
|
51
|
+
@endif
|
|
52
|
+
|
|
53
|
+
<div class="relative">
|
|
54
|
+
<select
|
|
55
|
+
id="{{ selectId }}"
|
|
56
|
+
name="{{ name }}"
|
|
57
|
+
class="{{ selectClass }} appearance-none pr-8"
|
|
58
|
+
@if(disabled) disabled @endif
|
|
59
|
+
@if(required) required @endif
|
|
60
|
+
>
|
|
61
|
+
@if(placeholder)
|
|
62
|
+
<option value="" disabled @if(!value) selected @endif>{{ placeholder }}</option>
|
|
63
|
+
@endif
|
|
64
|
+
@foreach(options as option)
|
|
65
|
+
<option
|
|
66
|
+
value="{{ typeof option === 'object' && option !== null ? (option.value ?? option.id ?? option.label ?? '') : option }}"
|
|
67
|
+
@if(option && option.disabled) disabled @endif
|
|
68
|
+
@if((typeof option === 'object' && option !== null ? option.value : option) == value) selected @endif
|
|
69
|
+
>
|
|
70
|
+
{{ typeof option === 'object' && option !== null ? (option.label ?? option.text ?? option.value ?? '') : option }}
|
|
71
|
+
</option>
|
|
72
|
+
@endforeach
|
|
73
|
+
</select>
|
|
74
|
+
<div class="flex absolute inset-y-0 right-0 items-center pr-2 pointer-events-none">
|
|
75
|
+
<div class="h-3.5 w-3.5 text-neutral-400 i-hugeicons-arrow-down-01"></div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
@if(error)
|
|
80
|
+
<p class="mt-1.5 text-[12px] text-red-600 dark:text-red-400">{{ error }}</p>
|
|
81
|
+
@elseif(hint)
|
|
82
|
+
<p class="mt-1.5 text-[12px] text-neutral-500 dark:text-neutral-400">{{ hint }}</p>
|
|
83
|
+
@endif
|
|
84
|
+
</div>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
interface ServiceHealthProps {
|
|
3
|
+
services?: any[]
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { services = [] } = defineProps<ServiceHealthProps>()
|
|
7
|
+
|
|
8
|
+
const allHealthy = services.every(s => s.status === 'healthy')
|
|
9
|
+
</script>
|
|
10
|
+
<div class="bg-white dark:bg-neutral-800/90 border border-black/5 rounded-xl dark:border-white/5">
|
|
11
|
+
<div class="flex items-center justify-between p-5 border-b border-neutral-200/60 dark:border-white/5">
|
|
12
|
+
<h3 class="font-semibold text-[14px] text-neutral-900 tracking-tight dark:text-white">System Health</h3>
|
|
13
|
+
@if(allHealthy)
|
|
14
|
+
<Badge variant="success" size="xs" dot>All Systems Operational</Badge>
|
|
15
|
+
@else
|
|
16
|
+
<Badge variant="warning" size="xs" dot>Degraded</Badge>
|
|
17
|
+
@endif
|
|
18
|
+
</div>
|
|
19
|
+
<div class="divide-neutral-100 divide-y dark:divide-white/5">
|
|
20
|
+
@foreach(services as service)
|
|
21
|
+
<div class="flex items-center justify-between px-5 py-3">
|
|
22
|
+
<div>
|
|
23
|
+
<div class="font-medium text-[13px] text-neutral-900 dark:text-white">{{ service.name }}</div>
|
|
24
|
+
@if(service.latency)
|
|
25
|
+
<div class="text-[12px] text-neutral-500 dark:text-neutral-400">{{ service.latency }}</div>
|
|
26
|
+
@endif
|
|
27
|
+
</div>
|
|
28
|
+
<Badge
|
|
29
|
+
variant="{{ service.status === 'healthy' ? 'success' : service.status === 'offline' ? 'danger' : 'warning' }}"
|
|
30
|
+
size="xs"
|
|
31
|
+
dot
|
|
32
|
+
>
|
|
33
|
+
{{ service.status }}
|
|
34
|
+
</Badge>
|
|
35
|
+
</div>
|
|
36
|
+
@endforeach
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
interface StatsCardProps {
|
|
3
|
+
title?: string
|
|
4
|
+
value?: string
|
|
5
|
+
subtitle?: string
|
|
6
|
+
trendLabel?: string
|
|
7
|
+
icon?: string
|
|
8
|
+
iconBg?: 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral'
|
|
9
|
+
loading?: boolean
|
|
10
|
+
trend?: any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { title = '', value = '', subtitle = '', trendLabel = '', icon = '', iconBg = 'primary', loading = false, trend = null } = defineProps<StatsCardProps>()
|
|
14
|
+
|
|
15
|
+
const iconBgClasses = {
|
|
16
|
+
primary: 'bg-blue-500/15 text-blue-600 dark:bg-blue-400/20 dark:text-blue-400',
|
|
17
|
+
success: 'bg-green-500/15 text-green-600 dark:bg-green-400/20 dark:text-green-400',
|
|
18
|
+
warning: 'bg-orange-500/15 text-orange-600 dark:bg-orange-400/20 dark:text-orange-400',
|
|
19
|
+
danger: 'bg-red-500/15 text-red-600 dark:bg-red-400/20 dark:text-red-400',
|
|
20
|
+
info: 'bg-cyan-500/15 text-cyan-600 dark:bg-cyan-400/20 dark:text-cyan-400',
|
|
21
|
+
neutral: 'bg-black/[0.06] text-neutral-600 dark:bg-white/10 dark:text-neutral-400'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const trendDirection = trend === null || trend === 0 ? 'neutral' : (trend > 0 ? 'up' : 'down')
|
|
25
|
+
|
|
26
|
+
const trendClasses = {
|
|
27
|
+
up: 'text-green-500 dark:text-green-400',
|
|
28
|
+
down: 'text-red-500 dark:text-red-400',
|
|
29
|
+
neutral: 'text-neutral-500 dark:text-neutral-400'
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
<div class="p-5 bg-white dark:bg-neutral-800/90 border border-black/5 rounded-xl dark:border-white/5 hover:shadow-black/5 hover:shadow-md dark:hover:shadow-black/20 duration-150 transition-all">
|
|
33
|
+
<div class="flex items-start justify-between">
|
|
34
|
+
<div class="flex-1 min-w-0">
|
|
35
|
+
<p class="font-medium text-[13px] text-neutral-500 tracking-tight truncate dark:text-neutral-400">
|
|
36
|
+
{{ title }}
|
|
37
|
+
</p>
|
|
38
|
+
|
|
39
|
+
<div class="flex gap-2 items-baseline mt-1.5">
|
|
40
|
+
@if(loading)
|
|
41
|
+
<div class="h-7 w-20 bg-black/5 dark:bg-white/10 rounded-md animate-pulse"></div>
|
|
42
|
+
@else
|
|
43
|
+
<p class="font-semibold text-[22px] text-neutral-900 tracking-tight dark:text-white">
|
|
44
|
+
{{ value }}
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
@if(trend !== null)
|
|
48
|
+
<span class="inline-flex items-center text-[12px] font-medium {{ trendClasses[trendDirection] }}">
|
|
49
|
+
@if(trendDirection == 'up')
|
|
50
|
+
<svg class="mr-0.5 h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
51
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 15l7-7 7 7"></path>
|
|
52
|
+
</svg>
|
|
53
|
+
@elseif(trendDirection == 'down')
|
|
54
|
+
<svg class="mr-0.5 h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
55
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path>
|
|
56
|
+
</svg>
|
|
57
|
+
@endif
|
|
58
|
+
{{ Math.abs(trend) }}%
|
|
59
|
+
</span>
|
|
60
|
+
@endif
|
|
61
|
+
@endif
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
@if(subtitle || trendLabel)
|
|
65
|
+
<p class="mt-1 text-[12px] text-neutral-500 dark:text-neutral-400">
|
|
66
|
+
{{ trendLabel || subtitle }}
|
|
67
|
+
</p>
|
|
68
|
+
@endif
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
@if(icon)
|
|
72
|
+
<div class="p-2.5 rounded-lg flex-shrink-0 {{ iconBgClasses[iconBg] }}">
|
|
73
|
+
<div class="{{ icon }} h-5 w-5"></div>
|
|
74
|
+
</div>
|
|
75
|
+
@endif
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<slot />
|
|
79
|
+
</div>
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
interface TableProps {
|
|
3
|
+
columns?: any[]
|
|
4
|
+
data?: any[]
|
|
5
|
+
loading?: boolean
|
|
6
|
+
selectable?: boolean
|
|
7
|
+
striped?: boolean
|
|
8
|
+
bordered?: boolean
|
|
9
|
+
compact?: boolean
|
|
10
|
+
stickyHeader?: boolean
|
|
11
|
+
emptyTitle?: string
|
|
12
|
+
emptyDescription?: string
|
|
13
|
+
rowKey?: string
|
|
14
|
+
hoverable?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { columns = [], data = [], loading = false, selectable = false, striped = false, bordered = false, compact = false, stickyHeader = false, emptyTitle = 'No data', emptyDescription = 'There are no items to display.', rowKey = 'id', hoverable = true } = defineProps<TableProps>()
|
|
18
|
+
|
|
19
|
+
function getAlignClass(align) {
|
|
20
|
+
switch (align) {
|
|
21
|
+
case 'center': return 'text-center'
|
|
22
|
+
case 'right': return 'text-right'
|
|
23
|
+
default: return 'text-left'
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
</script>
|
|
27
|
+
<div class="overflow-hidden relative bg-white dark:bg-neutral-900 border border-stone-200/80 rounded-xl dark:border-neutral-700 shadow-sm">
|
|
28
|
+
<!-- Loading overlay -->
|
|
29
|
+
@if(loading)
|
|
30
|
+
<div class="flex absolute inset-0 z-10 items-center justify-center bg-white/80 dark:bg-neutral-900/80 backdrop-blur-sm">
|
|
31
|
+
<div class="flex gap-2 items-center text-neutral-500 dark:text-neutral-400">
|
|
32
|
+
<div class="h-5 w-5 animate-spin i-hugeicons-loading-03"></div>
|
|
33
|
+
<span class="text-sm">Loading...</span>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
@endif
|
|
37
|
+
|
|
38
|
+
<div class="overflow-x-auto">
|
|
39
|
+
<table class="w-full">
|
|
40
|
+
<!-- Header -->
|
|
41
|
+
<thead class="bg-stone-50/80 dark:bg-neutral-800/50 {{ stickyHeader ? 'sticky top-0 z-10' : '' }}">
|
|
42
|
+
<tr>
|
|
43
|
+
<!-- Selection checkbox column -->
|
|
44
|
+
@if(selectable)
|
|
45
|
+
<th class="pl-6 pr-2 py-3 w-12">
|
|
46
|
+
<div class="flex items-center justify-center">
|
|
47
|
+
<input
|
|
48
|
+
type="checkbox"
|
|
49
|
+
class="h-4 w-4 text-blue-600 dark:bg-neutral-700 border-stone-300 rounded dark:border-neutral-600 focus:ring-blue-500"
|
|
50
|
+
data-select-all
|
|
51
|
+
>
|
|
52
|
+
</div>
|
|
53
|
+
</th>
|
|
54
|
+
@endif
|
|
55
|
+
|
|
56
|
+
<!-- Data columns -->
|
|
57
|
+
@foreach(columns as column, index)
|
|
58
|
+
<th
|
|
59
|
+
class="{{ index === 0 && !selectable ? 'pl-6' : 'pl-4' }} pr-4 text-xs font-semibold uppercase tracking-wider text-stone-500 dark:text-neutral-400 {{ compact ? 'py-2' : 'py-3.5' }} {{ getAlignClass(column.align) }} {{ column.sortable ? 'cursor-pointer select-none hover:text-stone-700 dark:hover:text-neutral-200' : '' }} {{ column.class || '' }}"
|
|
60
|
+
@if(column.width) style="width: {{ column.width }}" @endif
|
|
61
|
+
data-column="{{ column.key }}"
|
|
62
|
+
>
|
|
63
|
+
<div class="inline-flex gap-1.5 items-center">
|
|
64
|
+
<span>{{ column.label }}</span>
|
|
65
|
+
@if(column.sortable)
|
|
66
|
+
<div class="h-3.5 w-3.5 opacity-30 i-hugeicons-arrow-up-down-01"></div>
|
|
67
|
+
@endif
|
|
68
|
+
</div>
|
|
69
|
+
</th>
|
|
70
|
+
@endforeach
|
|
71
|
+
|
|
72
|
+
<!-- Actions column slot -->
|
|
73
|
+
@if($slots.actions)
|
|
74
|
+
<th class="px-4 py-3 w-12"></th>
|
|
75
|
+
@endif
|
|
76
|
+
</tr>
|
|
77
|
+
</thead>
|
|
78
|
+
|
|
79
|
+
<!-- Body -->
|
|
80
|
+
<tbody class="{{ striped ? 'divide-y-0' : 'divide-y divide-stone-100 dark:divide-neutral-700' }}">
|
|
81
|
+
@foreach(data as row, rowIndex)
|
|
82
|
+
<tr
|
|
83
|
+
class="{{ hoverable ? 'hover:bg-stone-50/50 dark:hover:bg-neutral-800/50' : '' }} {{ striped && rowIndex % 2 == 1 ? 'bg-stone-50/30 dark:bg-neutral-800/25' : '' }} transition-colors"
|
|
84
|
+
data-row-id="{{ row[rowKey] }}"
|
|
85
|
+
>
|
|
86
|
+
<!-- Selection checkbox -->
|
|
87
|
+
@if(selectable)
|
|
88
|
+
<td class="w-12 pl-6 pr-2 {{ compact ? 'py-2' : 'py-3.5' }}">
|
|
89
|
+
<div class="flex items-center justify-center">
|
|
90
|
+
<input
|
|
91
|
+
type="checkbox"
|
|
92
|
+
class="h-4 w-4 text-blue-600 dark:bg-neutral-700 border-stone-300 rounded dark:border-neutral-600 focus:ring-blue-500"
|
|
93
|
+
data-row-select
|
|
94
|
+
>
|
|
95
|
+
</div>
|
|
96
|
+
</td>
|
|
97
|
+
@endif
|
|
98
|
+
|
|
99
|
+
<!-- Data cells -->
|
|
100
|
+
@foreach(columns as column, colIndex)
|
|
101
|
+
<td class="{{ colIndex === 0 && !selectable ? 'pl-6' : 'pl-4' }} pr-4 text-sm text-stone-700 dark:text-neutral-200 {{ compact ? 'py-2' : 'py-3.5' }} {{ getAlignClass(column.align) }} {{ column.class || '' }}">
|
|
102
|
+
{{ row[column.key] }}
|
|
103
|
+
</td>
|
|
104
|
+
@endforeach
|
|
105
|
+
|
|
106
|
+
<!-- Actions cell -->
|
|
107
|
+
@if($slots.actions)
|
|
108
|
+
<td class="w-12 px-4 {{ compact ? 'py-2' : 'py-3' }}">
|
|
109
|
+
<slot name="actions" />
|
|
110
|
+
</td>
|
|
111
|
+
@endif
|
|
112
|
+
</tr>
|
|
113
|
+
@endforeach
|
|
114
|
+
</tbody>
|
|
115
|
+
</table>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<!-- Empty state -->
|
|
119
|
+
@if(data.length == 0 && !loading)
|
|
120
|
+
<div class="flex flex-col items-center justify-center px-4 py-12">
|
|
121
|
+
@if($slots.empty)
|
|
122
|
+
<slot name="empty" />
|
|
123
|
+
@else
|
|
124
|
+
<div class="mb-3 p-3 bg-neutral-100 dark:bg-neutral-800 rounded-full">
|
|
125
|
+
<div class="h-6 w-6 text-neutral-400 i-hugeicons-inbox-01"></div>
|
|
126
|
+
</div>
|
|
127
|
+
<p class="font-medium text-neutral-700 text-sm dark:text-neutral-200">
|
|
128
|
+
{{ emptyTitle }}
|
|
129
|
+
</p>
|
|
130
|
+
<p class="mt-1 text-neutral-500 text-sm dark:text-neutral-400">
|
|
131
|
+
{{ emptyDescription }}
|
|
132
|
+
</p>
|
|
133
|
+
@endif
|
|
134
|
+
</div>
|
|
135
|
+
@endif
|
|
136
|
+
|
|
137
|
+
<!-- Footer slot -->
|
|
138
|
+
@if($slots.footer)
|
|
139
|
+
<div class="px-4 py-3 bg-neutral-50/50 dark:bg-neutral-800/25 border-neutral-200 border-t dark:border-neutral-700">
|
|
140
|
+
<slot name="footer" />
|
|
141
|
+
</div>
|
|
142
|
+
@endif
|
|
143
|
+
</div>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
interface TabsProps {
|
|
3
|
+
tabs?: any[]
|
|
4
|
+
activeTab?: string
|
|
5
|
+
variant?: 'underline' | 'pills' | 'boxed'
|
|
6
|
+
size?: 'sm' | 'md' | 'lg'
|
|
7
|
+
fullWidth?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { tabs = [], activeTab = '', variant = 'underline', size = 'md', fullWidth = false } = defineProps<TabsProps>()
|
|
11
|
+
|
|
12
|
+
const sizeClasses = {
|
|
13
|
+
sm: 'text-sm py-2 px-3',
|
|
14
|
+
md: 'text-sm py-2.5 px-4',
|
|
15
|
+
lg: 'text-base py-3 px-5'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const containerClasses = {
|
|
19
|
+
underline: 'border-b border-neutral-200 dark:border-neutral-700',
|
|
20
|
+
pills: 'gap-2 bg-neutral-100 dark:bg-neutral-800 p-1 rounded-lg',
|
|
21
|
+
boxed: 'border border-neutral-200 dark:border-neutral-700 rounded-lg p-1'
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
<div class="flex {{ fullWidth ? 'w-full' : '' }} {{ containerClasses[variant] }}" role="tablist">
|
|
25
|
+
@foreach(tabs as tab)
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
role="tab"
|
|
29
|
+
aria-selected="{{ activeTab == tab.id ? 'true' : 'false' }}"
|
|
30
|
+
class="inline-flex items-center justify-center gap-2 font-medium transition-all duration-150 {{ sizeClasses[size] }} {{ fullWidth ? 'flex-1' : '' }} {{ tab.disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer' }} @if(variant == 'underline') -mb-px {{ activeTab == tab.id ? 'border-b-2 border-blue-500 text-blue-600 dark:text-blue-400' : 'border-b-2 border-transparent text-neutral-500 hover:text-neutral-700 hover:border-neutral-300 dark:text-neutral-400 dark:hover:text-neutral-200' }} @elseif(variant == 'pills') rounded-md {{ activeTab == tab.id ? 'bg-white dark:bg-neutral-700 text-neutral-900 dark:text-white shadow-sm' : 'text-neutral-500 hover:text-neutral-700 dark:text-neutral-400 dark:hover:text-neutral-200' }} @elseif(variant == 'boxed') rounded-md {{ activeTab == tab.id ? 'bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-white' : 'text-neutral-500 hover:text-neutral-700 hover:bg-neutral-50 dark:text-neutral-400 dark:hover:text-neutral-200 dark:hover:bg-neutral-800/50' }} @endif"
|
|
31
|
+
data-tab-id="{{ tab.id }}"
|
|
32
|
+
@if(tab.disabled) disabled @endif
|
|
33
|
+
>
|
|
34
|
+
@if(tab.icon)
|
|
35
|
+
<div class="{{ tab.icon }} w-4 h-4"></div>
|
|
36
|
+
@endif
|
|
37
|
+
<span>{{ tab.label }}</span>
|
|
38
|
+
@if(tab.badge !== undefined)
|
|
39
|
+
<span class="inline-flex items-center justify-center min-w-[1.25rem] h-5 px-1.5 text-xs font-medium rounded-full {{ activeTab == tab.id ? 'bg-blue-100 text-blue-600 dark:bg-blue-900/50 dark:text-blue-400' : 'bg-neutral-200 text-neutral-600 dark:bg-neutral-700 dark:text-neutral-300' }}">
|
|
40
|
+
{{ tab.badge }}
|
|
41
|
+
</span>
|
|
42
|
+
@endif
|
|
43
|
+
</button>
|
|
44
|
+
@endforeach
|
|
45
|
+
</div>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
interface TextareaProps {
|
|
3
|
+
value?: string
|
|
4
|
+
label?: string
|
|
5
|
+
placeholder?: string
|
|
6
|
+
error?: string
|
|
7
|
+
hint?: string
|
|
8
|
+
disabled?: boolean
|
|
9
|
+
readonly?: boolean
|
|
10
|
+
required?: boolean
|
|
11
|
+
rows?: number
|
|
12
|
+
maxLength?: number
|
|
13
|
+
showCount?: boolean
|
|
14
|
+
resize?: 'none' | 'vertical' | 'horizontal' | 'both'
|
|
15
|
+
name?: string
|
|
16
|
+
id?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { value = '', label = '', placeholder = '', error = '', hint = '', disabled = false, readonly = false, required = false, rows = 3, maxLength = 0, showCount = false, resize = 'vertical', name = '', id = '' } = defineProps<TextareaProps>()
|
|
20
|
+
|
|
21
|
+
const resizeClasses = {
|
|
22
|
+
none: 'resize-none',
|
|
23
|
+
vertical: 'resize-y',
|
|
24
|
+
horizontal: 'resize-x',
|
|
25
|
+
both: 'resize'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const borderClass = error
|
|
29
|
+
? 'border-red-300 dark:border-red-700 focus:ring-red-500/20 focus:border-red-500'
|
|
30
|
+
: 'border-neutral-300 dark:border-neutral-600 hover:border-neutral-400 dark:hover:border-neutral-500 focus:ring-blue-500/20 focus:border-blue-500'
|
|
31
|
+
|
|
32
|
+
const disabledClass = disabled
|
|
33
|
+
? 'bg-neutral-100 dark:bg-neutral-800 text-neutral-400 cursor-not-allowed border-neutral-200 dark:border-neutral-700'
|
|
34
|
+
: 'bg-white dark:bg-neutral-900'
|
|
35
|
+
|
|
36
|
+
const textareaClass = [
|
|
37
|
+
'block w-full rounded-lg border transition-all duration-150',
|
|
38
|
+
'text-sm text-neutral-900 dark:text-white',
|
|
39
|
+
'placeholder:text-neutral-400 dark:placeholder:text-neutral-500',
|
|
40
|
+
'focus:outline-none focus:ring-2',
|
|
41
|
+
'px-3 py-2.5',
|
|
42
|
+
resizeClasses[resize],
|
|
43
|
+
borderClass,
|
|
44
|
+
disabledClass
|
|
45
|
+
].join(' ')
|
|
46
|
+
|
|
47
|
+
const textareaId = id || 'textarea-' + Math.random().toString(36).slice(2, 11)
|
|
48
|
+
</script>
|
|
49
|
+
<div>
|
|
50
|
+
@if(label || (showCount && maxLength))
|
|
51
|
+
<div class="flex items-center justify-between mb-1.5">
|
|
52
|
+
@if(label)
|
|
53
|
+
<label for="{{ textareaId }}" class="block font-medium text-neutral-700 text-sm dark:text-neutral-200">
|
|
54
|
+
{{ label }}
|
|
55
|
+
@if(required)
|
|
56
|
+
<span class="text-red-500">*</span>
|
|
57
|
+
@endif
|
|
58
|
+
</label>
|
|
59
|
+
@endif
|
|
60
|
+
@if(showCount && maxLength)
|
|
61
|
+
<span class="text-neutral-400 text-xs dark:text-neutral-500">
|
|
62
|
+
<span data-char-count>{{ value.length || 0 }}</span>/{{ maxLength }}
|
|
63
|
+
</span>
|
|
64
|
+
@endif
|
|
65
|
+
</div>
|
|
66
|
+
@endif
|
|
67
|
+
|
|
68
|
+
<div class="relative">
|
|
69
|
+
<textarea
|
|
70
|
+
id="{{ textareaId }}"
|
|
71
|
+
name="{{ name }}"
|
|
72
|
+
rows="{{ rows }}"
|
|
73
|
+
placeholder="{{ placeholder }}"
|
|
74
|
+
class="{{ textareaClass }}"
|
|
75
|
+
@if(disabled) disabled @endif
|
|
76
|
+
@if(readonly) readonly @endif
|
|
77
|
+
@if(required) required @endif
|
|
78
|
+
@if(maxLength) maxlength="{{ maxLength }}" @endif
|
|
79
|
+
>{{ value }}</textarea>
|
|
80
|
+
|
|
81
|
+
@if(showCount && !maxLength)
|
|
82
|
+
<span class="absolute bottom-2 right-2 px-1 text-neutral-400 text-xs dark:text-neutral-500 bg-white dark:bg-neutral-900">
|
|
83
|
+
<span data-char-count>{{ value.length || 0 }}</span> characters
|
|
84
|
+
</span>
|
|
85
|
+
@endif
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
@if(error)
|
|
89
|
+
<p class="mt-1.5 text-red-600 text-sm dark:text-red-400">{{ error }}</p>
|
|
90
|
+
@elseif(hint)
|
|
91
|
+
<p class="mt-1.5 text-neutral-500 text-sm dark:text-neutral-400">{{ hint }}</p>
|
|
92
|
+
@endif
|
|
93
|
+
</div>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
interface ToggleProps {
|
|
3
|
+
checked?: boolean
|
|
4
|
+
label?: string
|
|
5
|
+
description?: string
|
|
6
|
+
disabled?: boolean
|
|
7
|
+
size?: 'sm' | 'md' | 'lg'
|
|
8
|
+
labelPosition?: 'left' | 'right'
|
|
9
|
+
name?: string
|
|
10
|
+
id?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { checked = false, label = '', description = '', disabled = false, size = 'md', labelPosition = 'right', name = '', id = '' } = defineProps<ToggleProps>()
|
|
14
|
+
|
|
15
|
+
const trackSizes = {
|
|
16
|
+
sm: 'h-4 w-7',
|
|
17
|
+
md: 'h-5 w-9',
|
|
18
|
+
lg: 'h-6 w-11'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const thumbSizes = {
|
|
22
|
+
sm: 'h-3 w-3',
|
|
23
|
+
md: 'h-4 w-4',
|
|
24
|
+
lg: 'h-5 w-5'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const translateSizes = {
|
|
28
|
+
sm: 'translate-x-3',
|
|
29
|
+
md: 'translate-x-4',
|
|
30
|
+
lg: 'translate-x-5'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const toggleId = id || 'toggle-' + Math.random().toString(36).slice(2, 11)
|
|
34
|
+
</script>
|
|
35
|
+
<div class="flex items-center gap-3 {{ labelPosition === 'left' ? 'flex-row-reverse justify-end' : '' }} {{ disabled ? 'opacity-60 cursor-not-allowed' : '' }}">
|
|
36
|
+
<input
|
|
37
|
+
type="checkbox"
|
|
38
|
+
id="{{ toggleId }}"
|
|
39
|
+
name="{{ name }}"
|
|
40
|
+
class="sr-only peer"
|
|
41
|
+
@if(checked) checked @endif
|
|
42
|
+
@if(disabled) disabled @endif
|
|
43
|
+
>
|
|
44
|
+
<label
|
|
45
|
+
for="{{ toggleId }}"
|
|
46
|
+
class="relative inline-flex shrink-0 rounded-full transition-all duration-150 focus-within:ring-2 focus-within:ring-blue-500/40 {{ trackSizes[size] }} {{ disabled ? 'cursor-not-allowed opacity-40' : 'cursor-pointer' }} peer-checked:bg-green-500 bg-black/15 dark:bg-white/20"
|
|
47
|
+
>
|
|
48
|
+
<span
|
|
49
|
+
class="pointer-events-none inline-block rounded-full bg-white shadow-sm ring-0 transition-transform duration-150 ease-in-out {{ thumbSizes[size] }} peer-checked:{{ translateSizes[size] }} translate-x-0.5"
|
|
50
|
+
></span>
|
|
51
|
+
</label>
|
|
52
|
+
|
|
53
|
+
@if(label || description)
|
|
54
|
+
<div class="flex-1 min-w-0">
|
|
55
|
+
@if(label)
|
|
56
|
+
<label
|
|
57
|
+
for="{{ toggleId }}"
|
|
58
|
+
class="text-[13px] font-medium tracking-tight {{ disabled ? 'text-neutral-400 dark:text-neutral-600' : 'text-neutral-700 dark:text-neutral-200' }} {{ disabled ? '' : 'cursor-pointer' }}"
|
|
59
|
+
>
|
|
60
|
+
{{ label }}
|
|
61
|
+
</label>
|
|
62
|
+
@endif
|
|
63
|
+
@if(description)
|
|
64
|
+
<p class="text-[12px] mt-0.5 {{ disabled ? 'text-neutral-300 dark:text-neutral-600' : 'text-neutral-500 dark:text-neutral-400' }}">
|
|
65
|
+
{{ description }}
|
|
66
|
+
</p>
|
|
67
|
+
@endif
|
|
68
|
+
</div>
|
|
69
|
+
@endif
|
|
70
|
+
</div>
|
|
71
|
+
<style>
|
|
72
|
+
input:checked + label {
|
|
73
|
+
background-color: rgb(34 197 94);
|
|
74
|
+
}
|
|
75
|
+
input:checked + label span {
|
|
76
|
+
transform: translateX(1rem);
|
|
77
|
+
}
|
|
78
|
+
input:checked + label span[class*="sm"] {
|
|
79
|
+
transform: translateX(0.75rem);
|
|
80
|
+
}
|
|
81
|
+
input:checked + label span[class*="lg"] {
|
|
82
|
+
transform: translateX(1.25rem);
|
|
83
|
+
}
|
|
84
|
+
</style>
|