@ziyarago/ui 0.3.0 → 0.5.0
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/CHANGELOG.md +12 -0
- package/README.md +23 -2
- package/dist/llms-full.txt +32 -3
- package/dist/llms.txt +3 -2
- package/dist/manifest.json +123 -6
- package/dist/style.css +41 -0
- package/package.json +2 -2
- package/src/core/components.js +15 -14
- package/src/core/components2.js +37 -36
- package/src/core/components3.js +29 -7
- package/src/core/components4.js +23 -22
- package/src/core/components6.js +8 -7
- package/src/core/components7.js +10 -9
- package/src/core/index.js +1 -1
- package/src/core/pages.js +1 -0
- package/src/i18n/components/ActionCard.js +5 -0
- package/src/i18n/components/Actionsheet.js +5 -0
- package/src/i18n/components/Alert.js +5 -0
- package/src/i18n/components/Breadcrumbs.js +5 -0
- package/src/i18n/components/Calendar.js +5 -0
- package/src/i18n/components/CallCard.js +5 -0
- package/src/i18n/components/CallRow.js +5 -0
- package/src/i18n/components/Carousel.js +5 -0
- package/src/i18n/components/ChartCard.js +5 -0
- package/src/i18n/components/ChatAi.js +5 -0
- package/src/i18n/components/Chip.js +5 -0
- package/src/i18n/components/ChipInput.js +5 -0
- package/src/i18n/components/CodeInput.js +5 -0
- package/src/i18n/components/Combobox.js +5 -0
- package/src/i18n/components/CommandPalette.js +5 -0
- package/src/i18n/components/ConfirmDialog.js +5 -0
- package/src/i18n/components/ContactPanel.js +5 -0
- package/src/i18n/components/CopyButton.js +2 -0
- package/src/i18n/components/DataGroup.js +5 -0
- package/src/i18n/components/DataState.js +5 -0
- package/src/i18n/components/DataTable.js +5 -0
- package/src/i18n/components/DatePicker.js +5 -0
- package/src/i18n/components/DealCard.js +5 -0
- package/src/i18n/components/Drawer.js +5 -0
- package/src/i18n/components/ErrorPage.js +5 -0
- package/src/i18n/components/FileUpload.js +5 -0
- package/src/i18n/components/FullscreenDialog.js +5 -0
- package/src/i18n/components/Gallery.js +5 -0
- package/src/i18n/components/GuestCard.js +5 -0
- package/src/i18n/components/ImageUpload.js +5 -0
- package/src/i18n/components/ImageViewer.js +5 -0
- package/src/i18n/components/IncomingCall.js +5 -0
- package/src/i18n/components/InlineEdit.js +5 -0
- package/src/i18n/components/IntegrationCard.js +5 -0
- package/src/i18n/components/LoginForm.js +5 -0
- package/src/i18n/components/Map.js +5 -0
- package/src/i18n/components/Modal.js +5 -0
- package/src/i18n/components/MultiSelect.js +5 -0
- package/src/i18n/components/NotificationBell.js +5 -0
- package/src/i18n/components/NumberInput.js +5 -0
- package/src/i18n/components/Pagination.js +5 -0
- package/src/i18n/components/PasswordInput.js +5 -0
- package/src/i18n/components/Popconfirm.js +5 -0
- package/src/i18n/components/PricingCard.js +5 -0
- package/src/i18n/components/RamadanTracker.js +5 -0
- package/src/i18n/components/RangeCalendar.js +5 -0
- package/src/i18n/components/RangeSlider.js +5 -0
- package/src/i18n/components/Rating.js +5 -0
- package/src/i18n/components/RichEditor.js +5 -0
- package/src/i18n/components/Screensaver.js +5 -0
- package/src/i18n/components/SearchInput.js +5 -0
- package/src/i18n/components/SideNav.js +5 -0
- package/src/i18n/components/Snackbar.js +5 -0
- package/src/i18n/components/Sortable.js +5 -0
- package/src/i18n/components/Spinner.js +5 -0
- package/src/i18n/components/StatusPage.js +5 -0
- package/src/i18n/components/StepDialog.js +5 -0
- package/src/i18n/components/StepForm.js +5 -0
- package/src/i18n/components/TabTiles.js +5 -0
- package/src/i18n/components/Tag.js +5 -0
- package/src/i18n/components/TopNav.js +5 -0
- package/src/i18n/components/Tour.js +5 -0
- package/src/i18n/components/TourCard.js +5 -0
- package/src/i18n/components/TourPopover.js +5 -0
- package/src/i18n/components/TreeSelect.js +5 -0
- package/src/i18n/components/Validation.js +5 -0
- package/src/i18n/components/index.js +130 -0
- package/src/i18n/index.js +8 -6
- package/src/meta/domain.js +20 -0
- package/src/meta/groups.js +1 -1
- package/src/meta/pages.js +9 -1
- package/src/meta/templates.json +1 -1
- package/src/react/index.js +1 -0
- package/src/styles/components8.css +41 -0
- package/src/vue/index.js +1 -1
- package/types/i18n.d.ts +4 -2
- package/types/react.d.ts +80 -2
- package/types/vue-global.d.ts +1 -0
- package/types/vue.d.ts +83 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [0.5.0](https://github.com/ziyarago/ui/compare/v0.4.0...v0.5.0) (2026-10-21)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* AuthShowcase login layouts (photo, light, tint) and PageAuth variants ([4ea5503](https://github.com/ziyarago/ui/commit/4ea5503e223f2c651292372be91987538f0517c8))
|
|
6
|
+
|
|
7
|
+
## [0.4.0](https://github.com/ziyarago/ui/compare/v0.3.0...v0.4.0) (2026-10-21)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* per-component translations (en, ar, ru free; 13 more languages in Pro pack), screenshots in README ([5fc8d9f](https://github.com/ziyarago/ui/commit/5fc8d9ffd85be7cacf42c5413561159d287d0316))
|
|
12
|
+
|
|
1
13
|
## [0.3.0](https://github.com/ziyarago/ui/compare/v0.2.0...v0.3.0) (2026-10-21)
|
|
2
14
|
|
|
3
15
|
### Features
|
package/README.md
CHANGED
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
# @ziyarago/ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@ziyarago/ui) [](LICENSE) [](https://ziyara.vip)
|
|
4
|
+
|
|
5
|
+
<p align="center"><a href="https://ziyara.vip"><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/hero-dark.png" alt="ziyarago/ui: glass design system for Vue, Nuxt and React" width="100%"></a></p>
|
|
6
|
+
|
|
7
|
+
Glass design system for Vue 3, Nuxt and React: 183 components, 17 page templates, OKLCH tokens, dark and light, RTL and Arabic, 16 interface languages. One framework-neutral core with thin adapters. Built for Umrah and Hajj products, usable anywhere.
|
|
8
|
+
|
|
9
|
+
<table>
|
|
10
|
+
<tr>
|
|
11
|
+
<td width="50%"><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/hero-light.png" alt="Light theme"><br><sub>Light and dark themes</sub></td>
|
|
12
|
+
<td width="50%"><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/hero-rtl.png" alt="Arabic RTL"><br><sub>Arabic, RTL mirrored</sub></td>
|
|
13
|
+
</tr>
|
|
14
|
+
<tr>
|
|
15
|
+
<td><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/components.png" alt="Component catalog"><br><sub>183 components with props and live examples</sub></td>
|
|
16
|
+
<td><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/templates.png" alt="Page templates"><br><sub>17 page templates</sub></td>
|
|
17
|
+
</tr>
|
|
18
|
+
<tr>
|
|
19
|
+
<td><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/button-docs.png" alt="Props docs"><br><sub>Every prop and variant documented</sub></td>
|
|
20
|
+
<td><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/backgrounds.png" alt="Animated backgrounds"><br><sub>Animated backgrounds and gradient cards</sub></td>
|
|
21
|
+
</tr>
|
|
22
|
+
</table>
|
|
23
|
+
|
|
24
|
+
Files:
|
|
4
25
|
|
|
5
26
|
```
|
|
6
|
-
src/core/ components
|
|
27
|
+
src/core/ components*.js, pages.js: 183 components and 17 page templates as { state, slots, render(ctx) }; no framework imports
|
|
7
28
|
src/vue/ Vue 3 adapter (UiButton, UiAppShell … + install(app))
|
|
8
29
|
src/react/ React adapter (same names)
|
|
9
30
|
src/theme.js useUiTheme(): theme · palette · dir → <html> attributes, persisted
|
package/dist/llms-full.txt
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @ziyarago/ui
|
|
2
2
|
|
|
3
|
-
> ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters,
|
|
3
|
+
> ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters, 183 components and 17 page templates.
|
|
4
4
|
|
|
5
|
-
Version 0.
|
|
5
|
+
Version 0.5.0. Docs: https://ziyara.vip . Manifest (machine readable): https://ziyara.vip/manifest.json
|
|
6
6
|
|
|
7
7
|
## Setup
|
|
8
8
|
- import { UiButton } from '@ziyarago/ui/vue'
|
|
@@ -2110,6 +2110,27 @@ Slots:
|
|
|
2110
2110
|
Notes:
|
|
2111
2111
|
- logo and aside are props in both Vue and React (pass a VNode / element).
|
|
2112
2112
|
|
|
2113
|
+
## UiAuthShowcase (domain)
|
|
2114
|
+
Sign-in layout with a media panel: dark photo card with floating avatars, light gradient card with a social-proof pill, or a tinted panel with a glass caption. The default slot is the form.
|
|
2115
|
+
Props:
|
|
2116
|
+
- `variant`: 'photo' | 'light' | 'tint' = "photo". 'photo': one framed card, media on the left. 'light': gradient form card, proof pill below, glass caption on the media. 'tint': like light, with a coloured panel, a heading on the media and cyan accent.
|
|
2117
|
+
- `title` (required): string. Form heading.
|
|
2118
|
+
- `subtitle`: string. Text under the heading.
|
|
2119
|
+
- `logo`: VNode. Logo node above the heading.
|
|
2120
|
+
- `footer`: VNode. Content under the fields, for example "No account? Sign up".
|
|
2121
|
+
- `image`: string. URL of the media image. Without it the panel shows an accent gradient.
|
|
2122
|
+
- `headline`: string. Large text on the media panel (photo and tint).
|
|
2123
|
+
- `caption`: string. Small text on the media panel (photo) or inside the glass bar (light, tint).
|
|
2124
|
+
- `quote`: string. Line at the bottom of the media panel; photo only.
|
|
2125
|
+
- `chips`: string[]. Pill labels inside the glass bar; light and tint.
|
|
2126
|
+
- `people`: { name: string; src?: string }[]. Avatars: floating on the photo, stacked in the proof pill.
|
|
2127
|
+
- `proof`: string. Social-proof title, e.g. "Join 20k+ users"; light and tint. Shows the pill under the form.
|
|
2128
|
+
- `proofHint`: string. Second line of the proof pill.
|
|
2129
|
+
Slots:
|
|
2130
|
+
- default: Form fields and buttons. React passes them as children.
|
|
2131
|
+
Notes:
|
|
2132
|
+
- logo, footer, image, people are props in both Vue and React. The media panel is hidden below the form on narrow screens (stacks under it).
|
|
2133
|
+
|
|
2113
2134
|
## UiPageDashboard (pages)
|
|
2114
2135
|
Dashboard template: greeting hero, KPI tiles, occupancy chart, sales funnel, top list and activity feed.
|
|
2115
2136
|
Props:
|
|
@@ -2351,7 +2372,15 @@ Notes:
|
|
|
2351
2372
|
## UiPageAuth (pages)
|
|
2352
2373
|
Sign-in page template without the app shell, as a centered card or a split layout.
|
|
2353
2374
|
Props:
|
|
2354
|
-
- `layout`: 'card' | 'split' = "card". 'split' uses AuthSplit with a side panel
|
|
2375
|
+
- `layout`: 'card' | 'split' | 'photo' | 'light' | 'tint' = "card". 'split' uses AuthSplit with a side panel. 'photo', 'light' and 'tint' use AuthShowcase with a media panel. Anything else renders a centered AuthLayout card over the background.
|
|
2376
|
+
- `image`: string. Media image URL; photo, light and tint layouts.
|
|
2377
|
+
- `headline`: string. Large text on the media panel; photo and tint.
|
|
2378
|
+
- `caption`: string. Small text on the media panel or glass bar.
|
|
2379
|
+
- `quote`: string. Bottom line of the media panel; photo only.
|
|
2380
|
+
- `chips`: string[]. Pill labels in the glass bar; light and tint.
|
|
2381
|
+
- `people`: { name: string; src?: string }[]. Avatars on the media panel or in the proof pill.
|
|
2382
|
+
- `proof`: string. Social-proof title under the form; light and tint.
|
|
2383
|
+
- `proofHint`: string. Second line of the proof pill.
|
|
2355
2384
|
- `title`: string. Form heading.
|
|
2356
2385
|
- `subtitle`: string. Text under the heading.
|
|
2357
2386
|
Slots:
|
package/dist/llms.txt
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @ziyarago/ui
|
|
2
2
|
|
|
3
|
-
> ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters,
|
|
3
|
+
> ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters, 183 components and 17 page templates.
|
|
4
4
|
|
|
5
|
-
Version 0.
|
|
5
|
+
Version 0.5.0. Docs: https://ziyara.vip . Manifest (machine readable): https://ziyara.vip/manifest.json
|
|
6
6
|
|
|
7
7
|
## Setup
|
|
8
8
|
- import { UiButton } from '@ziyarago/ui/vue'
|
|
@@ -214,6 +214,7 @@ Umrah, Hajj, hotel and CRM blocks.
|
|
|
214
214
|
- UiCallCard: Call recording card with status badge, a playback progress bar and duration.
|
|
215
215
|
- UiIncomingCall: Glass banner for an incoming call with Decline and Answer buttons.
|
|
216
216
|
- UiAuthSplit: Two-column sign-in layout with a brand panel on the left and the form on the right.
|
|
217
|
+
- UiAuthShowcase: Sign-in layout with a media panel: dark photo card with floating avatars, light gradient card with a social-proof pill, or a tinted panel with a glass caption. The default slot is the form.
|
|
217
218
|
|
|
218
219
|
## Page templates
|
|
219
220
|
Ready layouts with named slots.
|
package/dist/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziyarago/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"homepage": "https://ziyara.vip",
|
|
5
5
|
"imports": {
|
|
6
6
|
"vue": "import { UiButton } from '@ziyarago/ui/vue'",
|
|
@@ -251,7 +251,8 @@
|
|
|
251
251
|
"CallRow",
|
|
252
252
|
"CallCard",
|
|
253
253
|
"IncomingCall",
|
|
254
|
-
"AuthSplit"
|
|
254
|
+
"AuthSplit",
|
|
255
|
+
"AuthShowcase"
|
|
255
256
|
]
|
|
256
257
|
},
|
|
257
258
|
{
|
|
@@ -2273,7 +2274,7 @@
|
|
|
2273
2274
|
"id": "auth",
|
|
2274
2275
|
"label": "Authentication (6)",
|
|
2275
2276
|
"component": "UiPageAuth",
|
|
2276
|
-
"description": "
|
|
2277
|
+
"description": "Seven sign-in screens in five layouts: centered card, split with a side panel, and three with a media panel (photo, light, tint).",
|
|
2277
2278
|
"shell": false,
|
|
2278
2279
|
"slots": [
|
|
2279
2280
|
{
|
|
@@ -10050,6 +10051,79 @@
|
|
|
10050
10051
|
"logo and aside are props in both Vue and React (pass a VNode / element)."
|
|
10051
10052
|
]
|
|
10052
10053
|
},
|
|
10054
|
+
{
|
|
10055
|
+
"name": "AuthShowcase",
|
|
10056
|
+
"tag": "UiAuthShowcase",
|
|
10057
|
+
"group": "domain",
|
|
10058
|
+
"description": "Sign-in layout with a media panel: dark photo card with floating avatars, light gradient card with a social-proof pill, or a tinted panel with a glass caption. The default slot is the form.",
|
|
10059
|
+
"props": {
|
|
10060
|
+
"variant": {
|
|
10061
|
+
"type": "'photo' | 'light' | 'tint'",
|
|
10062
|
+
"values": [
|
|
10063
|
+
"photo",
|
|
10064
|
+
"light",
|
|
10065
|
+
"tint"
|
|
10066
|
+
],
|
|
10067
|
+
"default": "photo",
|
|
10068
|
+
"description": "'photo': one framed card, media on the left. 'light': gradient form card, proof pill below, glass caption on the media. 'tint': like light, with a coloured panel, a heading on the media and cyan accent."
|
|
10069
|
+
},
|
|
10070
|
+
"title": {
|
|
10071
|
+
"type": "string",
|
|
10072
|
+
"required": true,
|
|
10073
|
+
"description": "Form heading."
|
|
10074
|
+
},
|
|
10075
|
+
"subtitle": {
|
|
10076
|
+
"type": "string",
|
|
10077
|
+
"description": "Text under the heading."
|
|
10078
|
+
},
|
|
10079
|
+
"logo": {
|
|
10080
|
+
"type": "VNode",
|
|
10081
|
+
"description": "Logo node above the heading."
|
|
10082
|
+
},
|
|
10083
|
+
"footer": {
|
|
10084
|
+
"type": "VNode",
|
|
10085
|
+
"description": "Content under the fields, for example \"No account? Sign up\"."
|
|
10086
|
+
},
|
|
10087
|
+
"image": {
|
|
10088
|
+
"type": "string",
|
|
10089
|
+
"description": "URL of the media image. Without it the panel shows an accent gradient."
|
|
10090
|
+
},
|
|
10091
|
+
"headline": {
|
|
10092
|
+
"type": "string",
|
|
10093
|
+
"description": "Large text on the media panel (photo and tint)."
|
|
10094
|
+
},
|
|
10095
|
+
"caption": {
|
|
10096
|
+
"type": "string",
|
|
10097
|
+
"description": "Small text on the media panel (photo) or inside the glass bar (light, tint)."
|
|
10098
|
+
},
|
|
10099
|
+
"quote": {
|
|
10100
|
+
"type": "string",
|
|
10101
|
+
"description": "Line at the bottom of the media panel; photo only."
|
|
10102
|
+
},
|
|
10103
|
+
"chips": {
|
|
10104
|
+
"type": "string[]",
|
|
10105
|
+
"description": "Pill labels inside the glass bar; light and tint."
|
|
10106
|
+
},
|
|
10107
|
+
"people": {
|
|
10108
|
+
"type": "{ name: string; src?: string }[]",
|
|
10109
|
+
"description": "Avatars: floating on the photo, stacked in the proof pill."
|
|
10110
|
+
},
|
|
10111
|
+
"proof": {
|
|
10112
|
+
"type": "string",
|
|
10113
|
+
"description": "Social-proof title, e.g. \"Join 20k+ users\"; light and tint. Shows the pill under the form."
|
|
10114
|
+
},
|
|
10115
|
+
"proofHint": {
|
|
10116
|
+
"type": "string",
|
|
10117
|
+
"description": "Second line of the proof pill."
|
|
10118
|
+
}
|
|
10119
|
+
},
|
|
10120
|
+
"slots": {
|
|
10121
|
+
"default": "Form fields and buttons. React passes them as children."
|
|
10122
|
+
},
|
|
10123
|
+
"notes": [
|
|
10124
|
+
"logo, footer, image, people are props in both Vue and React. The media panel is hidden below the form on narrow screens (stacks under it)."
|
|
10125
|
+
]
|
|
10126
|
+
},
|
|
10053
10127
|
{
|
|
10054
10128
|
"name": "PageDashboard",
|
|
10055
10129
|
"tag": "UiPageDashboard",
|
|
@@ -10548,13 +10622,56 @@
|
|
|
10548
10622
|
"props": {
|
|
10549
10623
|
"layout": {
|
|
10550
10624
|
"forwarded": true,
|
|
10551
|
-
"type": "'card' | 'split'",
|
|
10625
|
+
"type": "'card' | 'split' | 'photo' | 'light' | 'tint'",
|
|
10552
10626
|
"values": [
|
|
10553
10627
|
"card",
|
|
10554
|
-
"split"
|
|
10628
|
+
"split",
|
|
10629
|
+
"photo",
|
|
10630
|
+
"light",
|
|
10631
|
+
"tint"
|
|
10555
10632
|
],
|
|
10556
10633
|
"default": "card",
|
|
10557
|
-
"description": "'split' uses AuthSplit with a side panel
|
|
10634
|
+
"description": "'split' uses AuthSplit with a side panel. 'photo', 'light' and 'tint' use AuthShowcase with a media panel. Anything else renders a centered AuthLayout card over the background."
|
|
10635
|
+
},
|
|
10636
|
+
"image": {
|
|
10637
|
+
"forwarded": true,
|
|
10638
|
+
"type": "string",
|
|
10639
|
+
"description": "Media image URL; photo, light and tint layouts."
|
|
10640
|
+
},
|
|
10641
|
+
"headline": {
|
|
10642
|
+
"forwarded": true,
|
|
10643
|
+
"type": "string",
|
|
10644
|
+
"description": "Large text on the media panel; photo and tint."
|
|
10645
|
+
},
|
|
10646
|
+
"caption": {
|
|
10647
|
+
"forwarded": true,
|
|
10648
|
+
"type": "string",
|
|
10649
|
+
"description": "Small text on the media panel or glass bar."
|
|
10650
|
+
},
|
|
10651
|
+
"quote": {
|
|
10652
|
+
"forwarded": true,
|
|
10653
|
+
"type": "string",
|
|
10654
|
+
"description": "Bottom line of the media panel; photo only."
|
|
10655
|
+
},
|
|
10656
|
+
"chips": {
|
|
10657
|
+
"forwarded": true,
|
|
10658
|
+
"type": "string[]",
|
|
10659
|
+
"description": "Pill labels in the glass bar; light and tint."
|
|
10660
|
+
},
|
|
10661
|
+
"people": {
|
|
10662
|
+
"forwarded": true,
|
|
10663
|
+
"type": "{ name: string; src?: string }[]",
|
|
10664
|
+
"description": "Avatars on the media panel or in the proof pill."
|
|
10665
|
+
},
|
|
10666
|
+
"proof": {
|
|
10667
|
+
"forwarded": true,
|
|
10668
|
+
"type": "string",
|
|
10669
|
+
"description": "Social-proof title under the form; light and tint."
|
|
10670
|
+
},
|
|
10671
|
+
"proofHint": {
|
|
10672
|
+
"forwarded": true,
|
|
10673
|
+
"type": "string",
|
|
10674
|
+
"description": "Second line of the proof pill."
|
|
10558
10675
|
},
|
|
10559
10676
|
"title": {
|
|
10560
10677
|
"forwarded": true,
|
package/dist/style.css
CHANGED
|
@@ -494,6 +494,47 @@
|
|
|
494
494
|
/* CommandPalette active row, dialog focus */
|
|
495
495
|
.zg-menu__i.is-active,.zg-menu__i[aria-selected="true"]{background:var(--zg-s2)}
|
|
496
496
|
.zg-modal:focus,.zg-sheet:focus,.zg-fsd:focus,.zg-saver:focus{outline:0}
|
|
497
|
+
/* AuthShowcase: photo (dark framed card), light (gradient card + proof pill + glass caption), tint (coloured panel, pill fields) */
|
|
498
|
+
.zg-ash{min-height:100%;padding:clamp(12px,3vw,40px);display:grid;place-items:center;--ash-r:28px;--ash-acc:var(--zg-brand)}
|
|
499
|
+
.zg-ash--tint{--ash-acc:oklch(.7 .13 215)}
|
|
500
|
+
.zg-ash__title{margin:0;font:300 clamp(28px,3.4vw,40px)/1.1 var(--zg-font);letter-spacing:-.03em}
|
|
501
|
+
.zg-ash__sub{margin:10px 0 0;color:var(--zg-text-2);line-height:1.55}
|
|
502
|
+
.zg-ash__head{display:flex;flex-direction:column;gap:6px}
|
|
503
|
+
.zg-ash__fields{display:flex;flex-direction:column;gap:14px;margin-top:22px}
|
|
504
|
+
.zg-ash__foot{margin-top:18px;color:var(--zg-text-2);font-size:14px;text-align:center}
|
|
505
|
+
.zg-ash__form{width:100%;max-width:400px;margin-inline:auto}
|
|
506
|
+
.zg-ash__media{position:relative;overflow:hidden;background:linear-gradient(150deg,color-mix(in oklch,var(--ash-acc) 55%,var(--zg-s2)),color-mix(in oklch,var(--ash-acc) 15%,var(--zg-s1)));background-size:cover;background-position:center}
|
|
507
|
+
.zg-ash--photo .zg-ash__frame{width:100%;max-width:1160px;display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);border-radius:var(--ash-r);overflow:hidden;background:var(--zg-s1);box-shadow:var(--zg-rim),var(--zg-shadow-1);min-height:560px}
|
|
508
|
+
.zg-ash--photo .zg-ash__media::before{content:"";position:absolute;inset:0;background:linear-gradient(90deg,color-mix(in oklch,var(--zg-bg) 15%,transparent),var(--zg-s1) 100%),linear-gradient(0deg,color-mix(in oklch,var(--zg-bg) 70%,transparent),transparent 55%)}
|
|
509
|
+
.zg-ash__copy{position:relative;z-index:1;padding:clamp(24px,5vw,64px);padding-top:clamp(60px,12vw,150px);max-width:480px}
|
|
510
|
+
.zg-ash__copy h2{margin:0 0 14px;font:500 clamp(30px,4vw,46px)/1.08 var(--zg-font);letter-spacing:-.03em}
|
|
511
|
+
.zg-ash__copy p{margin:0;color:var(--zg-text-2);font-size:18px;line-height:1.5}
|
|
512
|
+
.zg-ash__float{position:absolute;z-index:1;border-radius:50%;box-shadow:0 0 28px color-mix(in oklch,var(--zg-brand) 55%,transparent)}
|
|
513
|
+
.zg-ash__float--a{inset-inline-start:7%;top:60%}.zg-ash__float--b{inset-inline-start:52%;top:50%}.zg-ash__float--c{inset-inline-start:36%;top:76%}
|
|
514
|
+
@media(prefers-reduced-motion:no-preference){.zg-ash__float{animation:zg-ash-bob 6s ease-in-out infinite alternate}.zg-ash__float--b{animation-delay:-2s}.zg-ash__float--c{animation-delay:-4s}}
|
|
515
|
+
@keyframes zg-ash-bob{to{transform:translateY(-10px)}}
|
|
516
|
+
.zg-ash__quote{position:absolute;z-index:1;inset-inline-start:clamp(24px,5vw,64px);bottom:28px;color:var(--zg-text-1)}
|
|
517
|
+
.zg-ash--photo .zg-ash__side{display:grid;place-items:center;padding:clamp(24px,5vw,56px)}
|
|
518
|
+
.zg-ash__grid{width:100%;max-width:1180px;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:16px}
|
|
519
|
+
.zg-ash__col{display:flex;flex-direction:column;gap:16px;min-width:0}
|
|
520
|
+
.zg-ash__card{flex:1;display:grid;align-content:center;padding:clamp(24px,4vw,48px);border-radius:var(--ash-r);background:linear-gradient(165deg,var(--zg-s1) 40%,color-mix(in oklch,var(--ash-acc) 26%,var(--zg-s1)));box-shadow:var(--zg-rim)}
|
|
521
|
+
.zg-ash--light .zg-ash__head,.zg-ash--tint .zg-ash__head{align-items:center;text-align:center}
|
|
522
|
+
.zg-ash--tint .zg-ash__card{background:var(--zg-s1)}
|
|
523
|
+
.zg-ash--tint .zg-ash__title{font-weight:400}
|
|
524
|
+
.zg-ash__proof{display:flex;align-items:center;gap:14px;padding:14px 18px;border-radius:var(--ash-r);background:var(--zg-s1);box-shadow:var(--zg-rim)}
|
|
525
|
+
.zg-ash__stack{display:inline-flex}.zg-ash__stack .zg-avatar{margin-inline-end:-10px;box-shadow:0 0 0 2px var(--zg-s1)}
|
|
526
|
+
.zg-ash__proofT{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;margin-inline-start:10px}.zg-ash__proofT span{color:var(--zg-text-3);font-size:13px}
|
|
527
|
+
.zg-ash--light .zg-ash__grid>.zg-ash__media,.zg-ash--tint .zg-ash__grid>.zg-ash__media{border-radius:var(--ash-r);min-height:520px;display:flex;flex-direction:column;justify-content:flex-end;box-shadow:var(--zg-rim)}
|
|
528
|
+
.zg-ash__mh{position:absolute;inset-inline:0;top:0;margin:0;padding:clamp(20px,3vw,36px);font:400 clamp(24px,2.8vw,34px)/1.2 var(--zg-font);color:#fff;text-shadow:0 1px 16px rgb(0 0 0/.25)}
|
|
529
|
+
.zg-ash__glass{position:relative;margin:12px;padding:18px;border-radius:calc(var(--ash-r) - 8px);display:flex;flex-direction:column;gap:12px;background:color-mix(in oklch,var(--zg-bg) 38%,transparent);backdrop-filter:blur(22px) saturate(1.4);-webkit-backdrop-filter:blur(22px) saturate(1.4);box-shadow:var(--zg-rim);color:var(--zg-text-1)}
|
|
530
|
+
.zg-ash__glass p{margin:0;color:var(--zg-text-2)}
|
|
531
|
+
.zg-ash__chips{display:flex;gap:10px;flex-wrap:wrap}
|
|
532
|
+
.zg-ash__chip{flex:1;text-align:center;padding:14px 18px;border-radius:999px;box-shadow:inset 0 0 0 1px color-mix(in oklch,var(--zg-text-1) 40%,transparent)}
|
|
533
|
+
@media(max-width:860px){.zg-ash--photo .zg-ash__frame,.zg-ash__grid{grid-template-columns:1fr}.zg-ash--photo .zg-ash__media{min-height:300px}.zg-ash__grid>.zg-ash__media{min-height:320px!important;order:2}}
|
|
534
|
+
/* default media art when no image is given */
|
|
535
|
+
.zg-ash__media::after{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at 78% 24%,color-mix(in oklch,#fff 55%,transparent) 0,transparent 30%),radial-gradient(circle at 18% 78%,color-mix(in oklch,var(--ash-acc) 70%,transparent) 0,transparent 38%),conic-gradient(from 210deg at 62% 48%,transparent 0 .08turn,color-mix(in oklch,#fff 22%,transparent) .1turn .18turn,transparent .2turn 1turn)}
|
|
536
|
+
.zg-ash__media[style*="url("]::after{display:none}
|
|
537
|
+
.zg-ash--light .zg-ash__media{--ash-acc:oklch(.72 .08 80)}
|
|
497
538
|
|
|
498
539
|
|
|
499
540
|
/* ./sizes/typography.css */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziyarago/ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters,
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters, 183 components and 17 page templates.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui",
|
|
7
7
|
"design-system",
|
package/src/core/components.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Each component: { state?, render({ h, props, children, slots, state, set }) }.
|
|
3
3
|
// h(tag, props, ...children) is supplied by the Vue / React adapter. No hooks inside.
|
|
4
4
|
import { trap } from './dialog.js';
|
|
5
|
+
import { t } from '../i18n/index.js';
|
|
5
6
|
export const cx = (...a) => a.filter(Boolean).join(' ');
|
|
6
7
|
// Copies the listed props that are set; used to forward native attributes and listeners to the real <input>/<textarea>.
|
|
7
8
|
export const pick = (p, keys) => { const o = {}; for (const k of keys) if (p[k] !== undefined) o[k] = p[k]; return o; };
|
|
@@ -78,22 +79,22 @@ export const components = {
|
|
|
78
79
|
p.name ? h('input', { type: 'hidden', name: p.name, value: p.modelValue ?? '' }) : null, p.error ? h('span', { class: 'zg-hint zg-hint--error', role: 'alert' }, p.error) : p.hint ? h('span', { class: 'zg-hint' }, p.hint) : null); } },
|
|
79
80
|
CodeInput: { render: ({ h, props: p }) => { const n = p.length || 6, v = p.modelValue || '';
|
|
80
81
|
return h('div', { class: 'zg-code', style: { '--n': n } }, ...Array.from({ length: n }, (_, i) => h('span', { key: i, class: cx('zg-code__cell', i === v.length && 'zg-code__cell--on') }, v[i] || '')),
|
|
81
|
-
h('input', { value: v, inputMode: 'numeric', autoComplete: 'one-time-code', maxLength: n, 'aria-label': p.label || '
|
|
82
|
+
h('input', { value: v, inputMode: 'numeric', autoComplete: 'one-time-code', maxLength: n, 'aria-label': p.label || t('CodeInput', 'label'), onInput: e => { const x = e.target.value.replace(/\D/g, '').slice(0, n); p.onChange && p.onChange(x); if (x.length === n && v.length !== n) p.onComplete && p.onComplete(x); } })); } },
|
|
82
83
|
Stepper: { render: ({ h, props: p }) => h('div', { class: 'zg-stepper' }, ...(p.steps || []).flatMap((s, i) => [i ? h('span', { key: 'b' + i, class: 'zg-step__bar' }) : null, h('span', { key: i, class: cx('zg-step', i < p.current && 'zg-step--done', i === p.current && 'zg-step--cur') }, h('span', { class: 'zg-step__n' }, i < p.current ? icon(h, 'check', 14) : i + 1), s)])) },
|
|
83
84
|
Card: { slots: ['header', 'footer'], render: ({ h, props: p, children, slots }) => h('section', { class: cx('zg-card', 'zg-glass', p.variant === 'inverse' && 'zg-card--inverse', p.variant === 'gradient' && 'zg-card--grad', p.variant === 'gradient' && 'zg-card--t-' + (p.tone || 'brand'), p.variant === 'gradient' && p.intensity === 'vivid' && 'zg-card--vivid', p.variant === 'outline' && 'zg-card--outline', p.padding === 'none' && 'zg-card--flush', p.interactive && 'zg-card--click'), 'data-theme': p.variant === 'inverse' ? 'dark' : (p.variant === 'gradient' && p.intensity === 'vivid' ? (['brand', 'warning', undefined].includes(p.tone) ? 'light' : 'dark') : undefined), onClick: p.onClick, tabIndex: p.interactive && p.onClick ? 0 : undefined, onKeydown: p.interactive && p.onClick ? e => { if (e.key === 'Enter' && e.target === e.currentTarget) p.onClick(e); } : undefined },
|
|
84
85
|
slots.header ? h('div', { class: 'zg-card__head' }, slots.header) : (p.title || slots.actions) ? h('div', { class: 'zg-card__head' }, h('div', { style: { flex: 1, minWidth: 0 } }, h('h3', { class: 'zg-card__title' }, p.title), p.subtitle ? h('div', { class: 'zg-card__sub' }, p.subtitle) : null), slots.actions || null) : null, ...children, slots.footer ? h('div', { class: 'zg-card__foot' }, slots.footer) : null) },
|
|
85
86
|
Modal: { slots: ['title', 'subtitle'], render: ({ h, props: p, children, slots }) => { const foot = slots.footer || p.footer, sub = slots.subtitle || p.subtitle, ttl = slots.title || p.title; return !p.open ? null : h('div', { class: 'zg-modal-scrim', onClick: p.closeOnBackdrop === false ? undefined : p.onClose }, h('div', { class: 'zg-modal', role: 'dialog', 'aria-modal': 'true', 'aria-label': p.title, style: p.width ? { maxWidth: p.width } : undefined, ref: trap({ onClose: p.closeOnEsc === false ? undefined : p.onClose }), onClick: e => e.stopPropagation() },
|
|
86
|
-
h('div', { class: 'zg-modal__head' }, sub ? h('div', { style: { flex: 1, minWidth: 0 } }, h('h3', null, ttl), h('div', { class: 'zg-modal__sub' }, sub)) : h('h3', null, ttl), p.closable === false ? null : h('button', { type: 'button', class: 'zg-ibtn zg-ibtn--sm zg-ibtn--ghost', 'aria-label': '
|
|
87
|
+
h('div', { class: 'zg-modal__head' }, sub ? h('div', { style: { flex: 1, minWidth: 0 } }, h('h3', null, ttl), h('div', { class: 'zg-modal__sub' }, sub)) : h('h3', null, ttl), p.closable === false ? null : h('button', { type: 'button', class: 'zg-ibtn zg-ibtn--sm zg-ibtn--ghost', 'aria-label': t('Modal', 'close'), onClick: p.onClose }, icon(h, 'x'))), h('div', { class: 'zg-modal__body' }, ...children), foot ? h('div', { class: 'zg-modal__foot' }, foot) : null)); } },
|
|
87
88
|
Drawer: { render: ({ h, props: p, children, slots }) => { const foot = slots.footer || p.footer; return !p.open ? null : h('div', { class: cx('zg-modal-scrim', 'zg-drawer-scrim', 'zg-drawer-scrim--' + (p.side || 'end')), onClick: p.onClose }, h('div', { class: 'zg-modal zg-drawer', role: 'dialog', 'aria-modal': 'true', 'aria-label': p.title, ref: trap({ onClose: p.onClose }), onClick: e => e.stopPropagation() },
|
|
88
|
-
h('div', { class: 'zg-modal__head' }, h('h3', null, p.title), h('button', { type: 'button', class: 'zg-ibtn zg-ibtn--sm zg-ibtn--ghost', 'aria-label': '
|
|
89
|
+
h('div', { class: 'zg-modal__head' }, h('h3', null, p.title), h('button', { type: 'button', class: 'zg-ibtn zg-ibtn--sm zg-ibtn--ghost', 'aria-label': t('Drawer', 'close'), onClick: p.onClose }, icon(h, 'x'))), h('div', { class: 'zg-modal__body' }, ...children), foot ? h('div', { class: 'zg-modal__foot' }, foot) : null)); } },
|
|
89
90
|
Toast: { render: ({ h, props: p, children }) => h('div', { class: cx('zg-toast', 'zg-toast--' + (p.tone || 'info')), role: 'status' }, h('span', { class: 'zg-toast__icon' }, icon(h, { success: 'check', danger: 'circle-alert', warning: 'triangle-alert' }[p.tone] || 'info', 18)), h('div', null, p.title ? h('b', null, p.title) : null, h('span', null, ...children))) },
|
|
90
|
-
Breadcrumbs: { render: ({ h, props: p }) => h('nav', { class: 'zg-crumbs', 'aria-label': '
|
|
91
|
+
Breadcrumbs: { render: ({ h, props: p }) => h('nav', { class: 'zg-crumbs', 'aria-label': t('Breadcrumbs', 'label') }, ...(p.items || []).flatMap((it, i, a) => [i ? (p.separator === 'slash' || p.separator === 'dot' ? h('span', { key: 's' + i, class: 'zg-crumbs__sep', 'aria-hidden': 'true' }, p.separator === 'slash' ? '/' : '\u00b7') : icon(h, 'chevron-right', 14)) : null, i === a.length - 1 ? h('b', { key: i, 'aria-current': 'page' }, it.label) : h('a', { key: i, href: it.href || '#' }, it.label)])) },
|
|
91
92
|
Pagination: { render: ({ h, props: p }) => { const total = Math.max(1, Math.floor(+p.pages) || 1), cur = Math.min(total, Math.max(1, +p.page || 1)), s = Math.max(0, p.siblings == null ? 1 : Math.floor(+p.siblings) || 0), go = n => p.onChange && n >= 1 && n <= total && n !== p.page && p.onChange(n);
|
|
92
93
|
const set = new Set([1, total]); for (let i = cur - s; i <= cur + s; i++) if (i >= 1 && i <= total) set.add(i);
|
|
93
94
|
const nums = [...set].sort((a, b) => a - b), items = []; nums.forEach((n, i) => { const prev = nums[i - 1]; if (prev && n - prev === 2) items.push(prev + 1); else if (prev && n - prev > 2) items.push('…'); items.push(n); });
|
|
94
|
-
return h('nav', { class: 'zg-pager', 'aria-label': p.label || 'Pagination' }, p.hideArrows ? null : h('button', { key: 'prev', type: 'button', class: 'zg-pager__nav', 'aria-label': (p.labels && p.labels.prev) || '
|
|
95
|
-
...items.map((n, i) => n === '…' ? h('span', { key: 'e' + i, class: 'zg-pager__gap', 'aria-hidden': 'true' }, '…') : h('button', { key: n, type: 'button', 'aria-label': ((p.labels && p.labels.page) || '
|
|
96
|
-
p.hideArrows ? null : h('button', { key: 'next', type: 'button', class: 'zg-pager__nav', 'aria-label': (p.labels && p.labels.next) || '
|
|
95
|
+
return h('nav', { class: 'zg-pager', 'aria-label': p.label || t('Pagination', 'label') }, p.hideArrows ? null : h('button', { key: 'prev', type: 'button', class: 'zg-pager__nav', 'aria-label': (p.labels && p.labels.prev) || t('Pagination', 'prev'), disabled: cur <= 1, onClick: () => go(cur - 1) }, icon(h, 'chevron-left', 16)),
|
|
96
|
+
...items.map((n, i) => n === '…' ? h('span', { key: 'e' + i, class: 'zg-pager__gap', 'aria-hidden': 'true' }, '…') : h('button', { key: n, type: 'button', 'aria-label': ((p.labels && p.labels.page) || t('Pagination', 'page')) + ' ' + n, 'aria-current': n === p.page ? 'page' : undefined, onClick: () => go(n) }, n)),
|
|
97
|
+
p.hideArrows ? null : h('button', { key: 'next', type: 'button', class: 'zg-pager__nav', 'aria-label': (p.labels && p.labels.next) || t('Pagination', 'next'), disabled: cur >= total, onClick: () => go(cur + 1) }, icon(h, 'chevron-right', 16))); } },
|
|
97
98
|
Progress: { render: ({ h, props: p }) => h('div', { class: cx('zg-progress', p.tone && 'zg-progress--' + p.tone) }, (p.label || p.meta) ? h('div', { class: 'zg-progress__row' }, h('span', null, p.label), h('span', { class: 'zg-mono' }, p.meta)) : null, h('div', { class: 'zg-progress__track', role: 'progressbar', 'aria-valuenow': p.value, 'aria-valuemin': 0, 'aria-valuemax': 100 }, h('div', { class: 'zg-progress__bar', style: { width: Math.max(0, Math.min(100, p.value || 0)) + '%' } }))) },
|
|
98
99
|
Ring: { render: ({ h, props: p }) => { const s = { xs: 72, sm: 120, md: 180, lg: 240, xl: 300 }[p.size] || (Number(p.size) > 0 ? Number(p.size) : 180), th = p.thickness || Math.max(8, Math.round(s * .078)), ticks = p.ticks !== false, v = Math.max(0, Math.min(100, p.value || 0)) / 100, col = p.color || 'var(--zg-brand)';
|
|
99
100
|
const r = (s - th) / 2 - (ticks ? 12 : 0), cir = 2 * Math.PI * r, tk = [];
|
|
@@ -105,14 +106,14 @@ export const components = {
|
|
|
105
106
|
KpiTile: { render: ({ h, props: p }) => h('div', { class: cx('zg-kpi', p.variant && 'zg-kpi--' + p.variant) }, h('span', { class: 'zg-kpi__label' }, p.label), h('span', { class: 'zg-kpi__value zg-num' }, p.value), (p.sub || p.delta) ? h('span', { class: 'zg-kpi__sub' }, p.delta ? h('span', { class: cx('zg-delta', String(p.delta).startsWith('-') ? 'zg-delta--down' : 'zg-delta--up') }, p.delta + ' ') : null, p.sub) : null) },
|
|
106
107
|
DataTable: { render: ({ h, props: p, slots }) => { const cs = c => (c.align || c.width) ? { textAlign: c.align, width: c.width } : undefined, cols = p.columns || [], rows = p.rows || [], sel = p.selected || [], all = rows.length && rows.every(r => sel.includes(r.id));
|
|
107
108
|
const th = c => h('th', { key: c.key, 'aria-sort': p.sortKey === c.key ? (p.sortDir === 'desc' ? 'descending' : 'ascending') : undefined, class: c.num ? 'num' : undefined, style: cs(c) }, c.sortable ? h('button', { type: 'button', onClick: () => p.onSort && p.onSort(c.key, p.sortKey === c.key && p.sortDir !== 'desc' ? 'desc' : 'asc') }, c.label, p.sortKey === c.key ? icon(h, p.sortDir === 'desc' ? 'arrow-down' : 'arrow-up', 12) : null) : c.label);
|
|
108
|
-
return h('div', { class: 'zg-table-wrap' }, h('table', { class: 'zg-table' }, h('thead', null, h('tr', null, p.onSelect ? h('th', { style: { width: '44px' } }, h('button', { type: 'button', role: 'checkbox', class: 'zg-check', 'aria-checked': String(!!all), 'aria-label': (p.labels && p.labels.selectAll) || '
|
|
109
|
-
h('tbody', null, ...(rows.length ? rows.map((r, ri) => h('tr', { key: r.id, 'aria-selected': sel.includes(r.id) ? 'true' : undefined, class: p.onRowClick ? 'zg-tr--click' : undefined, onClick: p.onRowClick ? () => p.onRowClick(r, ri) : undefined }, p.onSelect ? h('td', null, h('button', { type: 'button', role: 'checkbox', class: 'zg-check', 'aria-checked': String(sel.includes(r.id)), 'aria-label': (p.labels && p.labels.selectRow) || '
|
|
109
|
+
return h('div', { class: 'zg-table-wrap' }, h('table', { class: 'zg-table' }, h('thead', null, h('tr', null, p.onSelect ? h('th', { style: { width: '44px' } }, h('button', { type: 'button', role: 'checkbox', class: 'zg-check', 'aria-checked': String(!!all), 'aria-label': (p.labels && p.labels.selectAll) || t('DataTable', 'selectAll'), onClick: () => p.onSelect(all ? [] : rows.map(r => r.id)) }, h('span', { class: 'zg-check__box' }, all ? icon(h, 'check', 14) : null))) : null, ...cols.map(th))),
|
|
110
|
+
h('tbody', null, ...(rows.length ? rows.map((r, ri) => h('tr', { key: r.id, 'aria-selected': sel.includes(r.id) ? 'true' : undefined, class: p.onRowClick ? 'zg-tr--click' : undefined, onClick: p.onRowClick ? () => p.onRowClick(r, ri) : undefined }, p.onSelect ? h('td', null, h('button', { type: 'button', role: 'checkbox', class: 'zg-check', 'aria-checked': String(sel.includes(r.id)), 'aria-label': (p.labels && p.labels.selectRow) || t('DataTable', 'selectRow'), onClick: () => p.onSelect(sel.includes(r.id) ? sel.filter(i => i !== r.id) : [...sel, r.id]) }, h('span', { class: 'zg-check__box' }, sel.includes(r.id) ? icon(h, 'check', 14) : null))) : null, ...cols.map(c => h('td', { key: c.key, class: c.num ? 'num' : undefined, style: cs(c) }, c.render ? c.render(r[c.key], r, h) : (r[c.key] ?? '—'))))) : [h('tr', { key: 'e' }, h('td', { colSpan: cols.length + 1 }, h('div', { class: 'zg-empty' }, slots.empty || p.empty || '—')))])))); } },
|
|
110
111
|
KanbanColumn: { render: ({ h, props: p, children }) => h('div', { class: 'zg-kcol zg-dm' }, h('div', { class: 'zg-kcol__head' }, p.title, h('span', { class: 'zg-badge' }, p.count ?? children.length)), ...children) },
|
|
111
112
|
KanbanCard: { render: ({ h, props: p, children }) => h('div', { class: 'zg-kcard zg-dm' }, h('b', { style: { fontSize: dzf(14) } }, p.title), p.sub ? h('span', { class: 'zg-hint' }, p.sub) : null, ...children, p.tag ? h('span', { class: cx('zg-badge', p.tone && 'zg-badge--' + p.tone), style: { alignSelf: 'flex-start' } }, p.tag) : null) },
|
|
112
113
|
Timeline: { render: ({ h, props: p }) => h('div', { class: 'zg-timeline' }, ...(p.items || []).map((it, i, a) => h('div', { key: i, class: 'zg-tl' }, h('div', { class: 'zg-tl__rail' }, h('span', { class: 'zg-tl__dot' }), i < a.length - 1 ? h('span', { class: 'zg-tl__line' }) : null), h('div', { class: 'zg-tl__body' }, h('b', null, it.title), it.text ? h('span', null, it.text) : null, h('span', { class: 'zg-tl__time' }, it.time))))) },
|
|
113
|
-
TopNav: { render: ({ h, props: p, slots }) => h('header', { class: 'zg-topnav' }, h('div', { class: 'zg-topnav__brand' }, p.brand), h('nav', { class: 'zg-topnav__items', 'aria-label': '
|
|
114
|
+
TopNav: { render: ({ h, props: p, slots }) => h('header', { class: 'zg-topnav' }, h('div', { class: 'zg-topnav__brand' }, p.brand), h('nav', { class: 'zg-topnav__items', 'aria-label': t('TopNav', 'label') }, ...(p.items || []).map(it => nav(h, it, p.value, p.onChange))), h('div', { class: 'zg-topnav__actions' }, slots.actions || p.actions || null)) },
|
|
114
115
|
SideNav: { render: ({ h, props: p, slots }) => h('aside', { class: cx('zg-sidenav', p.collapsed && 'zg-sidenav--collapsed') }, h('div', { class: 'zg-sidenav__brand' }, p.brand), ...(p.items || []).map(it => it.group ? h('div', { key: it.group, class: 'zg-sidenav__group', style: p.collapsed ? { display: 'none' } : undefined }, it.group) : nav(h, it, p.value, p.onChange, p.collapsed)), h('div', { class: 'zg-sidenav__sp' }), slots.footer || p.footer || null,
|
|
115
|
-
p.onToggle ? h('button', { type: 'button', class: 'zg-navitem', onClick: p.onToggle, 'aria-label': '
|
|
116
|
+
p.onToggle ? h('button', { type: 'button', class: 'zg-navitem', onClick: p.onToggle, 'aria-label': t('SideNav', 'toggle') }, icon(h, p.collapsed ? 'panel-left-open' : 'panel-left-close', 18), h('span', null, p.collapseLabel || t('SideNav', 'collapse'))) : null) },
|
|
116
117
|
AppShell: { state: { collapsed: false }, render: ({ C, h, props: p, children, slots, state, set }) => {
|
|
117
118
|
const brand = p.brand, top = (p.nav || 'top') === 'top', col = p.collapsed ?? state.collapsed, toggle = () => p.onCollapse ? p.onCollapse(!col) : set({ collapsed: !state.collapsed });
|
|
118
119
|
return h('div', { class: cx('zg-root', 'zg-shell', top && 'zg-shell--top', p.frame && 'zg-shell--frame'), style: { background: 'var(--zg-bg)' } },
|
|
@@ -124,9 +125,9 @@ export const components = {
|
|
|
124
125
|
HotelCard: { render: ({ h, props: p }) => h('article', { class: 'zg-hotel zg-dm' }, h('div', { class: 'zg-hotel__img', style: { backgroundImage: p.image ? `url(${p.image})` : undefined } }, h('div', { class: 'zg-hotel__tags' }, ...(p.tags || []).map(t => h('span', { key: t, class: 'zg-badge zg-badge--brand' }, t)))), h('div', { class: 'zg-hotel__body' },
|
|
125
126
|
h('div', { class: 'zg-hotel__row' }, h('div', { style: { minWidth: 0 } }, h('b', { style: { fontSize: dzf(17) } }, p.name), h('div', { class: 'zg-hint' }, '★'.repeat(p.stars || 0), ' · ', p.distance)), h('span', { class: 'zg-badge zg-badge--success', style: { marginInlineStart: 'auto' } }, p.rating)),
|
|
126
127
|
h('div', { class: 'zg-hotel__row', style: { alignItems: 'center' } }, h('span', { class: 'zg-hint' }, 'SAR / ', p.night || 'night'), h('div', { class: 'zg-hotel__price' }, h('div', { class: 'zg-num', style: { fontSize: dzf(26), fontWeight: 300 } }, fmt.num(p.price))))) ) },
|
|
127
|
-
TourCard: { render: ({ C, h, props: p, slots }) => { const seats = Number(p.seats) || 0, booked = Number(p.booked) || 0; return h('article', { class: 'zg-card zg-glass zg-dm' }, h('div', { class: 'zg-card__head' }, h('div', { style: { flex: 1 } }, h('h3', { class: 'zg-card__title' }, p.name), h('div', { class: 'zg-card__sub' }, [p.city, p.sessions != null && p.sessions !== '' ? p.sessions + ' sessions' : null].filter(Boolean).join(' · '))), h('span', { class: 'zg-badge zg-badge--info' }, '360°')), h(C.Progress, { value: seats > 0 ? Math.min(100, booked / seats * 100) : 0, label: '
|
|
128
|
+
TourCard: { render: ({ C, h, props: p, slots }) => { const seats = Number(p.seats) || 0, booked = Number(p.booked) || 0; return h('article', { class: 'zg-card zg-glass zg-dm' }, h('div', { class: 'zg-card__head' }, h('div', { style: { flex: 1 } }, h('h3', { class: 'zg-card__title' }, p.name), h('div', { class: 'zg-card__sub' }, [p.city, p.sessions != null && p.sessions !== '' ? p.sessions + ' sessions' : null].filter(Boolean).join(' · '))), h('span', { class: 'zg-badge zg-badge--info' }, '360°')), h(C.Progress, { value: seats > 0 ? Math.min(100, booked / seats * 100) : 0, label: t('TourCard', 'seats'), meta: `${booked}/${seats}` }), slots.actions || null); } },
|
|
128
129
|
PrayerTimes: { render: ({ h, props: p }) => h('div', { class: 'zg-pt zg-dm' }, ...(p.items || []).map((it, i) => { it = it || {}; return h('div', { key: i, class: 'zg-pt__i', 'aria-current': i === p.next ? 'true' : undefined }, it.name, h('b', null, it.time)); })) },
|
|
129
|
-
RamadanTracker: { render: ({ h, props: p }) => { const day = Number(p.day) || 0; return h('div', { class: 'zg-rt zg-dm', role: 'img', 'aria-label': day ?
|
|
130
|
-
CallRow: { render: ({ C, h, props: p }) => h('div', { class: 'zg-call zg-dm' }, h(C.Avatar, { name: p.name, size: dzp(p, 36) }), h('div', { style: { minWidth: 0 } }, h('b', { style: { fontSize: dzf(14) } }, p.name), h('div', { class: 'zg-hint' }, [p.status, fmt.dur(p.duration, p.durUnits)].filter(Boolean).join(' · '))), h(C.IconButton, { icon: 'play', label: (p.labels && p.labels.play) || '
|
|
130
|
+
RamadanTracker: { render: ({ h, props: p }) => { const day = Number(p.day) || 0; return h('div', { class: 'zg-rt zg-dm', role: 'img', 'aria-label': day ? t('RamadanTracker', 'day', { day }) : t('RamadanTracker', 'label') }, ...Array.from({ length: 30 }, (_, i) => { const n = i + 1; return h('i', { key: n, 'data-s': n === day ? 'cur' : n < day ? 'past' : n > 20 ? 'last' : undefined, 'data-odd': n > 20 && n % 2 ? '' : undefined, title: String(n) }); })); } },
|
|
131
|
+
CallRow: { render: ({ C, h, props: p }) => h('div', { class: 'zg-call zg-dm' }, h(C.Avatar, { name: p.name, size: dzp(p, 36) }), h('div', { style: { minWidth: 0 } }, h('b', { style: { fontSize: dzf(14) } }, p.name), h('div', { class: 'zg-hint' }, [p.status, fmt.dur(p.duration, p.durUnits)].filter(Boolean).join(' · '))), h(C.IconButton, { icon: 'play', label: (p.labels && p.labels.play) || t('CallRow', 'play'), size: dzb(p), onClick: p.onPlay })) },
|
|
131
132
|
};
|
|
132
133
|
export { fmt };
|