@ziyarago/ui 0.0.0-stage → 0.1.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/LICENSE +25 -0
  3. package/README.md +121 -2
  4. package/THIRD_PARTY_NOTICES.md +12 -0
  5. package/assets/3d/_sheet.png +0 -0
  6. package/assets/3d/archive.png +0 -0
  7. package/assets/3d/bag.png +0 -0
  8. package/assets/3d/cameraoff.png +0 -0
  9. package/assets/3d/comment.png +0 -0
  10. package/assets/3d/crowd.png +0 -0
  11. package/assets/3d/firstaid.png +0 -0
  12. package/assets/3d/flag.png +0 -0
  13. package/assets/3d/grain.png +0 -0
  14. package/assets/3d/guide.png +0 -0
  15. package/assets/3d/hotel.png +0 -0
  16. package/assets/3d/kaaba.png +0 -0
  17. package/assets/3d/logo.png +0 -0
  18. package/assets/3d/luggage.png +0 -0
  19. package/assets/3d/map.png +0 -0
  20. package/assets/3d/mosque.png +0 -0
  21. package/assets/3d/plane.png +0 -0
  22. package/assets/3d/profile.png +0 -0
  23. package/assets/3d/restaurant.png +0 -0
  24. package/assets/3d/ring360.png +0 -0
  25. package/assets/3d/shoes.png +0 -0
  26. package/assets/3d/star.png +0 -0
  27. package/assets/3d/store.png +0 -0
  28. package/assets/3d/taxi.png +0 -0
  29. package/assets/3d/threads.png +0 -0
  30. package/assets/3d/tour360.png +0 -0
  31. package/assets/3d/trash.png +0 -0
  32. package/assets/3d/umbrella.png +0 -0
  33. package/assets/3d/video.png +0 -0
  34. package/assets/3d/water.png +0 -0
  35. package/assets/brand/mark.png +0 -0
  36. package/assets/brand/ziyarago-logo-light.svg +21 -0
  37. package/assets/brand/ziyarago-logo.svg +21 -0
  38. package/assets/brand/ziyarago-mark-light.svg +13 -0
  39. package/assets/brand/ziyarago-mark.svg +13 -0
  40. package/assets/fonts/thmanyah/thmanyahsans-Black.otf +0 -0
  41. package/assets/fonts/thmanyah/thmanyahsans-Bold.otf +0 -0
  42. package/assets/fonts/thmanyah/thmanyahsans-Light.otf +0 -0
  43. package/assets/fonts/thmanyah/thmanyahsans-Medium.otf +0 -0
  44. package/assets/fonts/thmanyah/thmanyahsans-Regular.otf +0 -0
  45. package/assets/fonts/thmanyah/thmanyahserifdisplay-Black.otf +0 -0
  46. package/assets/fonts/thmanyah/thmanyahserifdisplay-Bold.otf +0 -0
  47. package/assets/fonts/thmanyah/thmanyahserifdisplay-Light.otf +0 -0
  48. package/assets/fonts/thmanyah/thmanyahserifdisplay-Medium.otf +0 -0
  49. package/assets/fonts/thmanyah/thmanyahserifdisplay-Regular.otf +0 -0
  50. package/assets/fonts/thmanyah/thmanyahseriftext-Black.otf +0 -0
  51. package/assets/fonts/thmanyah/thmanyahseriftext-Bold.otf +0 -0
  52. package/assets/fonts/thmanyah/thmanyahseriftext-Light.otf +0 -0
  53. package/assets/fonts/thmanyah/thmanyahseriftext-Medium.otf +0 -0
  54. package/assets/fonts/thmanyah/thmanyahseriftext-Regular.otf +0 -0
  55. package/dist/llms-full.txt +2293 -0
  56. package/dist/llms.txt +239 -0
  57. package/dist/manifest.json +10254 -0
  58. package/dist/style.css +1357 -0
  59. package/dist/tokens.css +48 -0
  60. package/package.json +118 -4
  61. package/src/catalog.js +4 -0
  62. package/src/core/components.js +124 -0
  63. package/src/core/components2.js +163 -0
  64. package/src/core/components3.js +35 -0
  65. package/src/core/components4.js +58 -0
  66. package/src/core/components6.js +48 -0
  67. package/src/core/components7.js +42 -0
  68. package/src/core/dialog.js +65 -0
  69. package/src/core/index.js +9 -0
  70. package/src/core/pages.js +51 -0
  71. package/src/meta/README.md +29 -0
  72. package/src/meta/actions.js +161 -0
  73. package/src/meta/data.js +327 -0
  74. package/src/meta/domain.js +212 -0
  75. package/src/meta/feedback.js +153 -0
  76. package/src/meta/forms.js +453 -0
  77. package/src/meta/groups.js +16 -0
  78. package/src/meta/index.js +14 -0
  79. package/src/meta/layout.js +159 -0
  80. package/src/meta/media.js +104 -0
  81. package/src/meta/navigation.js +99 -0
  82. package/src/meta/overlay.js +264 -0
  83. package/src/meta/pages.js +229 -0
  84. package/src/meta/templates.json +2422 -0
  85. package/src/meta/typography.js +58 -0
  86. package/src/nuxt/module.mjs +29 -0
  87. package/src/nuxt/plugin.mjs +9 -0
  88. package/src/react/index.js +31 -0
  89. package/src/styles/components.css +162 -0
  90. package/src/styles/components2.css +68 -0
  91. package/src/styles/components3.css +29 -0
  92. package/src/styles/components4.css +18 -0
  93. package/src/styles/components5.css +5 -0
  94. package/src/styles/components6.css +19 -0
  95. package/src/styles/components7.css +44 -0
  96. package/src/styles/components8.css +47 -0
  97. package/src/styles/effects.css +43 -0
  98. package/src/styles/fonts.css +17 -0
  99. package/src/styles/index.css +21 -0
  100. package/src/styles/sizes/actions.css +126 -0
  101. package/src/styles/sizes/data.css +161 -0
  102. package/src/styles/sizes/domain.css +35 -0
  103. package/src/styles/sizes/feedback.css +69 -0
  104. package/src/styles/sizes/forms.css +122 -0
  105. package/src/styles/sizes/layout.css +15 -0
  106. package/src/styles/sizes/media.css +28 -0
  107. package/src/styles/sizes/navigation.css +146 -0
  108. package/src/styles/sizes/overlay.css +79 -0
  109. package/src/styles/sizes/typography.css +12 -0
  110. package/src/styles/tokens.css +47 -0
  111. package/src/theme.js +52 -0
  112. package/src/vue/index.js +29 -0
  113. package/tokens/tokens.json +9 -0
  114. package/types/core.d.ts +4 -0
  115. package/types/nuxt.d.ts +11 -0
  116. package/types/react.d.ts +189 -0
  117. package/types/shared.d.ts +18 -0
  118. package/types/theme.d.ts +6 -0
  119. package/types/vue.d.ts +192 -0
@@ -0,0 +1,14 @@
1
+ import typography from './typography.js';
2
+ import layout from './layout.js';
3
+ import actions from './actions.js';
4
+ import forms from './forms.js';
5
+ import data from './data.js';
6
+ import feedback from './feedback.js';
7
+ import overlay from './overlay.js';
8
+ import navigation from './navigation.js';
9
+ import media from './media.js';
10
+ import domain from './domain.js';
11
+ import pages from './pages.js';
12
+
13
+ export const meta = { ...typography, ...layout, ...actions, ...forms, ...data, ...feedback, ...overlay, ...navigation, ...media, ...domain, ...pages };
14
+ export default meta;
@@ -0,0 +1,159 @@
1
+ // API metadata for the layout group. Format: see src/meta/README.md
2
+ const SIZES = ['xs', 'sm', 'md', 'lg', 'xl'];
3
+ const sizeProp = (def = 'md', desc = 'Component size.') => ({ type: "'xs' | 'sm' | 'md' | 'lg' | 'xl'", values: SIZES, default: def, description: desc });
4
+ const as = { type: 'string', default: 'div', description: 'HTML tag to render.' };
5
+ const style = { type: 'Record<string, string | number>', description: 'Inline styles merged over the component styles.' };
6
+ const gap = { type: 'number | string', default: 12, description: 'Gap between children. Numbers are pixels, strings any CSS length.' };
7
+ const stackProps = {
8
+ gap: { ...gap, forwarded: true },
9
+ align: { forwarded: true, type: 'string', description: 'CSS align-items value, for example center, flex-start, stretch.' },
10
+ justify: { forwarded: true, type: 'string', description: 'CSS justify-content value, for example space-between, center.' },
11
+ wrap: { forwarded: true, type: 'boolean', default: false, description: 'Allows children to wrap onto several lines.' },
12
+ as: { ...as, forwarded: true },
13
+ style: { ...style, forwarded: true },
14
+ };
15
+
16
+ export default {
17
+ Box: {
18
+ description: 'Generic container with optional padding and glass background.',
19
+ props: {
20
+ p: { type: 'number | string', description: 'Padding. Numbers are pixels, strings any CSS value.' },
21
+ glass: { type: 'boolean', default: false, description: 'Applies the glass surface style.' },
22
+ as,
23
+ style,
24
+ },
25
+ slots: { default: 'Box content.' },
26
+ },
27
+ Center: {
28
+ description: 'Centers its children horizontally and vertically.',
29
+ props: { as, style },
30
+ slots: { default: 'Centered content.' },
31
+ },
32
+ HStack: {
33
+ description: 'Horizontal flex row with a configurable gap.',
34
+ props: { ...stackProps },
35
+ slots: { default: 'Items laid out in a row.' },
36
+ },
37
+ VStack: {
38
+ description: 'Vertical flex column with a configurable gap.',
39
+ props: { ...stackProps },
40
+ slots: { default: 'Items laid out in a column.' },
41
+ },
42
+ Grid: {
43
+ description: 'CSS grid with a fixed column count or auto-fit columns of a minimum width.',
44
+ props: {
45
+ cols: { type: 'number', description: 'Fixed number of equal columns. When set, min is ignored.' },
46
+ min: { type: 'number | string', default: 240, description: 'Minimum column width for auto-fit layout (used when cols is not set).' },
47
+ gap: { type: 'number | string', default: 16, description: 'Gap between cells. Numbers are pixels, strings any CSS length.' },
48
+ as,
49
+ style,
50
+ },
51
+ slots: { default: 'Grid items.' },
52
+ },
53
+ Divider: {
54
+ description: 'Horizontal separator line with an optional centered label.',
55
+ props: {
56
+ label: { type: 'string', description: 'Text shown in the middle of the line.' },
57
+ size: sizeProp('md', 'Label font size and spacing.'),
58
+ },
59
+ notes: ['Renders role="separator".'],
60
+ },
61
+ ScrollArea: {
62
+ description: 'Vertically scrollable container with a max height and styled scrollbar.',
63
+ props: {
64
+ maxHeight: { type: 'string', default: '240px', description: 'CSS max-height; content scrolls beyond it. Must be a string with a unit.' },
65
+ },
66
+ slots: { default: 'Scrollable content.' },
67
+ },
68
+ SplitLayout: {
69
+ description: 'Two-column layout with configurable column widths; use for master-detail or side-by-side content.',
70
+ props: {
71
+ columns: { type: 'string', default: '1fr 1fr', description: 'CSS grid-template-columns value, for example "280px 1fr".' },
72
+ },
73
+ slots: { left: 'First column. Falls back to the first child.', right: 'Second column. Falls back to the second child.' },
74
+ notes: ['Exactly two columns; extra children beyond the second are ignored.'],
75
+ },
76
+ AppShell: {
77
+ description: 'Application frame with a top or side navigation and a main content area.',
78
+ props: {
79
+ nav: { type: "'top' | 'side'", values: ['top', 'side'], default: 'top', description: 'Navigation placement: TopNav or SideNav.' },
80
+ brand: { type: 'VNode | string', description: 'Brand/logo content shown in the navigation.' },
81
+ items: { type: '{ value: string; label: string; icon?: string }[]', description: 'Navigation items, passed to TopNav or SideNav.' },
82
+ value: { type: 'string', description: 'Value of the active navigation item. Changed via the change event.' },
83
+ actions: { type: 'VNode', description: 'Right side of the top navigation (nav="top" only).' },
84
+ collapsed: { type: 'boolean', description: 'Controlled collapsed state of the side navigation. Uncontrolled (internal state, initially false) when omitted.' },
85
+ collapsible: { type: 'boolean', default: true, description: 'Shows the collapse toggle in the side navigation. Set false to hide it.' },
86
+ collapseLabel: { type: 'string', description: 'Label of the collapse toggle in the side navigation.' },
87
+ frame: { type: 'boolean', default: false, description: 'Renders the shell as a framed block instead of full viewport (useful in previews).' },
88
+ },
89
+ events: { change: 'Called with the value of the clicked navigation item.', collapse: 'Called with the new collapsed boolean when the toggle is pressed.' },
90
+ slots: { default: 'Main page content.', footer: 'Bottom area of the side navigation (nav="side" only).' },
91
+ notes: ['The footer slot and collapse props apply only to nav="side".'],
92
+ },
93
+ PageHeader: {
94
+ description: 'Page title block with optional breadcrumbs, subtitle and an actions area.',
95
+ props: {
96
+ title: { type: 'string', required: true, description: 'Page title (rendered as h1).' },
97
+ subtitle: { type: 'string', description: 'Secondary text below the title.' },
98
+ crumbs: { type: '{ label: string; href?: string }[]', description: 'Breadcrumb items shown above the title.' },
99
+ size: sizeProp('md', 'Title and subtitle size.'),
100
+ },
101
+ slots: { actions: 'Right side of the header, usually buttons.' },
102
+ },
103
+ AuthLayout: {
104
+ description: 'Centered card layout for sign-in and sign-up pages.',
105
+ props: {
106
+ title: { type: 'string', required: true, description: 'Heading inside the card.' },
107
+ subtitle: { type: 'string', description: 'Text below the title.' },
108
+ logo: { type: 'VNode', description: 'Logo element shown at the top of the card.' },
109
+ style,
110
+ },
111
+ slots: { default: 'Card body, usually a form.' },
112
+ },
113
+ SettingsSection: {
114
+ description: 'Settings block with a title and description on the left and fields below.',
115
+ props: {
116
+ title: { type: 'string', required: true, description: 'Section title.' },
117
+ description: { type: 'string', description: 'Helper text under the title.' },
118
+ size: sizeProp('md', 'Padding, title and field spacing.'),
119
+ },
120
+ slots: { default: 'Fields and controls of the section.' },
121
+ },
122
+ LiquidGlass: {
123
+ description: 'Frosted liquid-glass panel with adjustable corner radius and blur.',
124
+ props: {
125
+ radius: { type: 'number | string', default: 24, description: 'Corner radius. Numbers are pixels.' },
126
+ blur: { type: 'number | string', default: 18, description: 'Backdrop blur radius. Numbers are pixels.' },
127
+ size: sizeProp('md', 'Inner padding.'),
128
+ },
129
+ slots: { default: 'Panel content.' },
130
+ notes: ['Backdrop blur is visible only over a colorful or image background.'],
131
+ },
132
+ Footer: {
133
+ description: 'Page footer with a copyright line and a row of links.',
134
+ props: {
135
+ copyright: { type: 'string', default: '© 2026 ZiyaraGo', description: 'Copyright text.' },
136
+ links: { type: '{ label: string; href?: string }[]', default: '[]', description: 'Footer links; href defaults to "#".' },
137
+ size: sizeProp('md', 'Padding and font size.'),
138
+ },
139
+ slots: { default: 'Extra content after the links.' },
140
+ },
141
+ Sortable: {
142
+ description: 'Drag-and-drop reorderable list with grip handles and up/down buttons.',
143
+ props: {
144
+ items: { type: '{ id?: string | number; label: string }[]', default: '[]', required: true, description: 'Items in current order. id is used as the key, falling back to the index.' },
145
+ render: { type: '(item, index) => VNode', description: 'Custom item renderer. Defaults to the bold item label.' },
146
+ size: sizeProp('md', 'Row padding, gap and font size.'),
147
+ },
148
+ events: { change: 'Called with the reordered items array after a drop or up/down click.' },
149
+ notes: ['Controlled: the parent must store the array from change and pass it back as items.', 'Keyboard-friendly reordering is available through the up/down buttons.'],
150
+ },
151
+ Portal: {
152
+ description: 'Renders its children outside the component tree, for example into document.body.',
153
+ props: {
154
+ to: { type: 'string | HTMLElement', description: 'Target element or CSS selector. The adapter default (body) is used when omitted.' },
155
+ },
156
+ slots: { default: 'Content to teleport.' },
157
+ notes: ['Renders nothing when the framework adapter provides no portal.'],
158
+ },
159
+ };
@@ -0,0 +1,104 @@
1
+ // API metadata for the media group. Format: see src/meta/README.md
2
+ const SZ = ['xs', 'sm', 'md', 'lg', 'xl'];
3
+ const SIZE = "'xs' | 'sm' | 'md' | 'lg' | 'xl'";
4
+ const size = (d, extra) => ({ type: SIZE, values: SZ, default: d || 'md', description: extra || 'Thumbnail strip size.' });
5
+
6
+ export default {
7
+ Avatar: {
8
+ description: 'Round user picture that falls back to initials when there is no image.',
9
+ props: {
10
+ name: { type: 'string', description: 'Full name. Shown as the tooltip and used for up to two initials; "?" when empty.' },
11
+ src: { type: 'string', description: 'Image URL. Initials are shown without it.' },
12
+ size: { type: "'xs' | 'sm' | 'md' | 'lg' | 'xl' | number", values: SZ, default: 'md', description: 'Diameter: xs 24, sm 32, md 40, lg 56, xl 72 px. A number is used directly as px.' },
13
+ },
14
+ notes: ['Avatar sizes differ from other components (lg 56, xl 72), and a number gives any custom size.'],
15
+ },
16
+
17
+ Icon: {
18
+ description: 'Renders a named icon from the library at a chosen size.',
19
+ props: {
20
+ name: { type: 'string', default: 'circle', description: 'Icon name from the library, for example "check" or "map-pin".' },
21
+ size: { type: "'xs' | 'sm' | 'md' | 'lg' | 'xl' | number", values: SZ, default: 'md', description: 'Pixel size: xs 14, sm 16, md 20, lg 24, xl 32. A number is used directly as px.' },
22
+ },
23
+ notes: ['The icon is aria-hidden; add a label on the surrounding control for accessibility.', 'Icons load lazily, so the glyph may appear a moment after first render.'],
24
+ },
25
+
26
+ LazyImage: {
27
+ description: 'Image box with a fixed aspect ratio and rounded corners, rendered as a background image.',
28
+ props: {
29
+ src: { type: 'string', description: 'Image URL. An empty placeholder is shown without it.' },
30
+ alt: { type: 'string', default: '', description: 'Accessible description, exposed as aria-label on role="img".' },
31
+ aspectRatio: { type: 'string', default: '16/9', description: 'CSS aspect-ratio value such as "1/1" or "4/3".' },
32
+ rounded: { type: 'number', default: 20, description: 'Corner radius in px.' },
33
+ },
34
+ },
35
+
36
+ Gallery: {
37
+ description: 'Large image with a counter and a thumbnail strip for browsing a set of photos.',
38
+ props: {
39
+ images: { type: 'string[]', required: true, default: [], description: 'Image URLs. The first one is shown initially; clicking a thumbnail switches the main image.' },
40
+ alt: { type: 'string', default: '', description: 'Accessible description of the main image.' },
41
+ index: { type: 'number', description: 'Controlled active image index. When set, the gallery only changes image when you update it from onChange; when omitted it keeps its own state starting at 0.' },
42
+ labels: { type: '{ photo?: string }', default: "{ photo: 'Photo' }", description: 'Override the thumbnail aria-label prefix.' },
43
+ size: size('md'),
44
+ },
45
+ events: { change: 'Called with the new image index when a thumbnail is clicked.' },
46
+ notes: ['An empty or missing images array renders an empty frame (no counter, no thumbnails). Invalid index values are clamped to the available range.'],
47
+ },
48
+
49
+ Carousel: {
50
+ description: 'Slideshow of image slides with captions, arrows and dot pagination.',
51
+ props: {
52
+ items: { type: '{ image?: string; title: string; sub?: string }[]', required: true, default: [], description: 'Slides. title and sub form the caption over the image.' },
53
+ index: { type: 'number', description: 'Controlled active slide index. When set, the carousel only changes slide when you update it from onChange; when omitted it keeps its own state starting at 0.' },
54
+ autoplay: { type: 'number | boolean', description: 'Advance automatically every N milliseconds (true = 5000). Starts only in the browser, pauses while the pointer is over or focus is inside the carousel and while the tab is hidden, and is disabled for prefers-reduced-motion and data-motion="off". Needs at least 2 slides.' },
55
+ label: { type: 'string', description: 'Accessible name of the carousel region.' },
56
+ labels: { type: '{ prev?: string; next?: string; slide?: string }', default: "{ prev: 'Previous', next: 'Next', slide: 'Slide' }", description: 'Override the aria-labels of the arrows and dots.' },
57
+ size: size('md', 'Slide aspect ratio and caption size: xs 16/6, sm 16/7, md 16/8, lg 16/9, xl 16/10.'),
58
+ },
59
+ events: { change: 'Called with the new slide index on arrow, dot, keyboard (left and right arrows) and autoplay changes.' },
60
+ notes: ['Arrows wrap around at both ends and are hidden with fewer than 2 slides; an empty items array renders an empty frame.', 'Autoplay has no visible pause button; hover or focus pauses it. Prefer a manual carousel for essential content.'],
61
+ },
62
+
63
+ Map: {
64
+ description: 'Interactive raster map with panning, zoom buttons and clickable markers, using OpenStreetMap data via CARTO tiles.',
65
+ props: {
66
+ lat: { type: 'number', default: 21.4225, description: 'Initial center latitude.' },
67
+ lon: { type: 'number', default: 39.8262, description: 'Initial center longitude.' },
68
+ zoom: { type: 'number', default: 14, description: 'Initial zoom level; the zoom buttons clamp it to 3..18.' },
69
+ markers: { type: "{ lat: number; lon: number; label?: string; icon?: string; tone?: 'brand' | 'sky' | 'success' | 'warning' | 'danger' | 'neutral' }[]", default: [], description: 'Pins. label is the tooltip, icon defaults to "map-pin".' },
70
+ width: { type: 'number', default: 640, description: 'Pixel width used to compute which tiles to load; the map element itself fills its container.' },
71
+ height: { type: 'number', default: 360, description: 'Height in px.' },
72
+ theme: { type: "'light' | 'dark'", values: ['light', 'dark'], description: 'Tile theme. If omitted, dark is used when the page is inside a dark data-theme.' },
73
+ style: { type: 'string', description: 'CARTO tile style path, for example "light_all" or "dark_all". Overrides theme.' },
74
+ label: { type: 'string', default: 'Map', description: 'aria-label of the map region.' },
75
+ labels: { type: '{ zoomIn?: string; zoomOut?: string }', default: "{ zoomIn: 'Zoom in', zoomOut: 'Zoom out' }", description: 'Accessible names of the zoom buttons.' },
76
+ },
77
+ events: { select: 'Called with the marker object when a marker is clicked.' },
78
+ notes: ['The center and zoom props are initial values; user panning and zooming is kept in internal state.', 'Tiles come from a third-party CDN, so it needs network access.'],
79
+ },
80
+
81
+ ProfileHeader: {
82
+ description: 'Profile card with a cover image, large avatar, name and a row of meta items.',
83
+ props: {
84
+ name: { type: 'string', required: true, description: 'Person or organization name.' },
85
+ src: { type: 'string', description: 'Avatar image URL; initials are shown without it.' },
86
+ cover: { type: 'string', description: 'Cover image URL.' },
87
+ meta: { type: '{ icon: string; label: string }[]', default: [], description: 'Facts under the name such as location or join date. label must be unique.' },
88
+ },
89
+ slots: { default: 'Extra content on the right of the name row, usually action buttons.' },
90
+ },
91
+
92
+ ChatAi: {
93
+ description: 'Chat panel with message bubbles, a typing indicator and an input, for AI assistant UIs.',
94
+ props: {
95
+ messages: { type: "{ role: 'user' | 'ai'; text: string }[]", required: true, default: [], description: 'Conversation in order. Any role other than "user" renders as an assistant bubble.' },
96
+ loading: { type: 'boolean', default: false, description: 'Shows the typing dots and disables sending.' },
97
+ title: { type: 'string', default: 'Assistant', description: 'Header title; also the aria-label. The header renders only when set.' },
98
+ subtitle: { type: 'string', description: 'Muted text next to the title.' },
99
+ placeholder: { type: 'string', default: 'Ask anything…', description: 'Input placeholder.' },
100
+ },
101
+ events: { send: 'Called with the trimmed message text when the user presses Enter or the send button. The component does not append it to messages.' },
102
+ notes: ['Append the user message and the reply to messages yourself; set loading while waiting.'],
103
+ },
104
+ };
@@ -0,0 +1,99 @@
1
+ // API metadata for the navigation group. Format: see src/meta/README.md
2
+ const SIZE = "'xs' | 'sm' | 'md' | 'lg' | 'xl'";
3
+ const SIZES = ['xs', 'sm', 'md', 'lg', 'xl'];
4
+ const size = (what) => ({ type: SIZE, values: SIZES, default: 'md', description: `Scale of ${what}.` });
5
+ const current = { type: 'string', description: 'Value of the active item (aria-current="page").' };
6
+
7
+ export default {
8
+ TopNav: {
9
+ description: 'Horizontal app header with a brand area, navigation items and an actions area.',
10
+ props: {
11
+ brand: { type: 'string | VNode', description: 'Logo or product name on the left.' },
12
+ items: { type: '{ value: string; label: string; icon?: string; count?: string | number }[]', default: '[]', description: 'Navigation entries. count renders a small counter after the label.' },
13
+ value: current,
14
+ size: size('header padding, item height and text'),
15
+ },
16
+ slots: { actions: 'Right side of the header (buttons, avatar). React also accepts an actions prop.' },
17
+ events: { change: 'Called with the value of the clicked item.' },
18
+ notes: ['Controlled: update value yourself in the change handler.', 'Items use count; SubNav uses badge for the same purpose.'],
19
+ },
20
+
21
+ SideNav: {
22
+ description: 'Vertical sidebar with a brand, grouped navigation items, a footer and an optional collapse toggle.',
23
+ props: {
24
+ brand: { type: 'string | VNode', description: 'Logo or product name at the top.' },
25
+ items: { type: '({ value: string; label: string; icon?: string; count?: string | number } | { group: string })[]', default: '[]', description: 'Entries. An object with group renders a section heading (hidden when collapsed); others are links with an optional icon and count.' },
26
+ value: current,
27
+ collapsed: { type: 'boolean', default: false, description: 'Icon-only mode; labels move to the title tooltip.' },
28
+ collapseLabel: { type: 'string', default: 'Collapse', description: 'Label of the collapse button.' },
29
+ size: size('width (216 to 336 px), padding and item height'),
30
+ },
31
+ slots: { footer: 'Bottom area above the collapse button, e.g. user card. React also accepts a footer prop.' },
32
+ events: { change: 'Called with the value of the clicked item.', toggle: 'Called when the collapse button is clicked. The button is rendered only when this handler is set.' },
33
+ notes: ['Collapsing is controlled: flip collapsed in the toggle handler.', 'Group entries are keyed by their title, so keep group names unique.'],
34
+ },
35
+
36
+ SubNav: {
37
+ description: 'Compact vertical list of sections with an optional title; use for settings and secondary page navigation.',
38
+ props: {
39
+ title: { type: 'string', description: 'Small heading above the list; also the aria-label.' },
40
+ items: { type: '{ value: string; label: string; icon?: string; badge?: string | number }[]', default: '[]', description: 'Entries. badge renders a counter on the right.' },
41
+ value: current,
42
+ size: size('item height and text'),
43
+ },
44
+ events: { change: 'Called with the value of the clicked item.' },
45
+ },
46
+
47
+ Tabs: {
48
+ description: 'Underlined tab strip for switching between views of the same page.',
49
+ props: {
50
+ modelValue: { type: 'string', model: true, description: 'Value of the selected tab. Vue v-model, React modelValue + onChange.' },
51
+ items: { type: '{ value: string; label: string }[]', default: '[]', description: 'Tabs.' },
52
+ size: size('tab height and text'),
53
+ },
54
+ events: { change: 'Called with the value of the clicked tab.' },
55
+ notes: ['Renders only the tab buttons (role="tablist"); you show the matching panel yourself.'],
56
+ },
57
+
58
+ PillTabs: {
59
+ description: 'Pill-style tab buttons with optional icons, horizontal or vertical; use for settings and profile sections.',
60
+ props: {
61
+ items: { type: '{ value: string; label: string; icon?: string }[]', default: '[]', description: 'Tabs.' },
62
+ value: { type: 'string', description: 'Value of the selected tab. Changed via the change event.' },
63
+ vertical: { type: 'boolean', default: false, description: 'Stacks the tabs in a column.' },
64
+ size: size('button height, gap and text'),
65
+ },
66
+ events: { change: 'Called with the value of the clicked tab.' },
67
+ notes: ['Renders only the tab buttons; you show the matching panel yourself.'],
68
+ },
69
+
70
+ Breadcrumbs: {
71
+ description: 'Path trail with chevron separators; the last item is the current page.',
72
+ props: {
73
+ items: { type: '{ label: string; href?: string }[]', default: '[]', description: 'Trail from root to current. All but the last render as links (href defaults to "#"); the last is bold with aria-current="page".' },
74
+ size: size('text and separator size'),
75
+ },
76
+ },
77
+
78
+ Pagination: {
79
+ description: 'Page navigation with numbered buttons, ellipsis windowing and previous/next; use under tables and lists.',
80
+ props: {
81
+ page: { type: 'number', description: 'Current page, 1-based. Changed via the change event.' },
82
+ pages: { type: 'number', default: 1, description: 'Total number of pages.' },
83
+ siblings: { type: 'number', default: 1, description: 'Page buttons shown on each side of the current page. First and last page are always shown; gaps become an ellipsis.' },
84
+ label: { type: 'string', default: 'Pagination', description: 'Accessible name of the navigation landmark.' },
85
+ size: size('button size and text'),
86
+ },
87
+ events: { change: 'Called with the clicked page number (also from the previous and next buttons).' },
88
+ notes: ['Shows first, last and current plus or minus siblings with an ellipsis for gaps, so it scales to hundreds of pages. A gap of a single page shows that page instead of an ellipsis.', 'Previous and next buttons carry aria-label "Previous page" and "Next page" and are disabled at the ends; the current page has aria-current="page".'],
89
+ },
90
+
91
+ Accordion: {
92
+ description: 'Vertical list of collapsible sections where one is open at a time; use for FAQs and grouped details.',
93
+ props: {
94
+ items: { type: '{ id: string; title: string; body: string | VNode }[]', default: '[]', description: 'Sections. id must be unique; body is shown when the section is open.' },
95
+ size: size('header padding and text'),
96
+ },
97
+ notes: ['Open state is internal and single: opening one closes the others, all start closed.'],
98
+ },
99
+ };