@hawiyah/vue 0.0.0-stage → 0.2.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 (81) hide show
  1. package/LICENSE +73 -0
  2. package/NOTICE +19 -0
  3. package/PORTAL.md +70 -0
  4. package/README.md +388 -2
  5. package/dist/calendar.d.ts +183 -0
  6. package/dist/calendar.d.ts.map +1 -0
  7. package/dist/calendar.js +448 -0
  8. package/dist/calendar.js.map +1 -0
  9. package/dist/content-navigation.d.ts +61 -0
  10. package/dist/content-navigation.d.ts.map +1 -0
  11. package/dist/content-navigation.js +31 -0
  12. package/dist/content-navigation.js.map +1 -0
  13. package/dist/content.d.ts +392 -0
  14. package/dist/content.d.ts.map +1 -0
  15. package/dist/content.js +305 -0
  16. package/dist/content.js.map +1 -0
  17. package/dist/controls.d.ts +657 -0
  18. package/dist/controls.d.ts.map +1 -0
  19. package/dist/controls.js +679 -0
  20. package/dist/controls.js.map +1 -0
  21. package/dist/display.d.ts +387 -0
  22. package/dist/display.d.ts.map +1 -0
  23. package/dist/display.js +293 -0
  24. package/dist/display.js.map +1 -0
  25. package/dist/expansion-interactive.d.ts +476 -0
  26. package/dist/expansion-interactive.d.ts.map +1 -0
  27. package/dist/expansion-interactive.js +613 -0
  28. package/dist/expansion-interactive.js.map +1 -0
  29. package/dist/file-upload-list.d.ts +54 -0
  30. package/dist/file-upload-list.d.ts.map +1 -0
  31. package/dist/file-upload-list.js +21 -0
  32. package/dist/file-upload-list.js.map +1 -0
  33. package/dist/forms.d.ts +388 -0
  34. package/dist/forms.d.ts.map +1 -0
  35. package/dist/forms.js +156 -0
  36. package/dist/forms.js.map +1 -0
  37. package/dist/index.d.ts +26 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +16 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/interactive.d.ts +240 -0
  42. package/dist/interactive.d.ts.map +1 -0
  43. package/dist/interactive.js +341 -0
  44. package/dist/interactive.js.map +1 -0
  45. package/dist/layout.d.ts +755 -0
  46. package/dist/layout.d.ts.map +1 -0
  47. package/dist/layout.js +101 -0
  48. package/dist/layout.js.map +1 -0
  49. package/dist/mobile-top-bar.d.ts +47 -0
  50. package/dist/mobile-top-bar.d.ts.map +1 -0
  51. package/dist/mobile-top-bar.js +18 -0
  52. package/dist/mobile-top-bar.js.map +1 -0
  53. package/dist/native-attributes.d.ts +38 -0
  54. package/dist/native-attributes.d.ts.map +1 -0
  55. package/dist/native-attributes.js +27 -0
  56. package/dist/native-attributes.js.map +1 -0
  57. package/dist/navigation-drawer.d.ts +87 -0
  58. package/dist/navigation-drawer.d.ts.map +1 -0
  59. package/dist/navigation-drawer.js +56 -0
  60. package/dist/navigation-drawer.js.map +1 -0
  61. package/dist/patterns.d.ts +455 -0
  62. package/dist/patterns.d.ts.map +1 -0
  63. package/dist/patterns.js +355 -0
  64. package/dist/patterns.js.map +1 -0
  65. package/dist/portal.d.ts +174 -0
  66. package/dist/portal.d.ts.map +1 -0
  67. package/dist/portal.js +36 -0
  68. package/dist/portal.js.map +1 -0
  69. package/dist/search-field.d.ts +87 -0
  70. package/dist/search-field.d.ts.map +1 -0
  71. package/dist/search-field.js +123 -0
  72. package/dist/search-field.js.map +1 -0
  73. package/dist/shared.d.ts +46 -0
  74. package/dist/shared.d.ts.map +1 -0
  75. package/dist/shared.js +40 -0
  76. package/dist/shared.js.map +1 -0
  77. package/dist/site-verification.d.ts +63 -0
  78. package/dist/site-verification.d.ts.map +1 -0
  79. package/dist/site-verification.js +34 -0
  80. package/dist/site-verification.js.map +1 -0
  81. package/package.json +53 -3
package/LICENSE ADDED
@@ -0,0 +1,73 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
10
+
11
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
12
+
13
+ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
14
+
15
+ "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
16
+
17
+ "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
18
+
19
+ "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
20
+
21
+ "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
22
+
23
+ "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
24
+
25
+ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
26
+
27
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
28
+
29
+ 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
30
+
31
+ 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
32
+
33
+ 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
34
+
35
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
36
+
37
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
38
+
39
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
40
+
41
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
42
+
43
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
44
+
45
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
46
+
47
+ 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
48
+
49
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
50
+
51
+ 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
52
+
53
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
54
+
55
+ END OF TERMS AND CONDITIONS
56
+
57
+ APPENDIX: How to apply the Apache License to your work.
58
+
59
+ To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
60
+
61
+ Copyright [yyyy] [name of copyright owner]
62
+
63
+ Licensed under the Apache License, Version 2.0 (the "License");
64
+ you may not use this file except in compliance with the License.
65
+ You may obtain a copy of the License at
66
+
67
+ http://www.apache.org/licenses/LICENSE-2.0
68
+
69
+ Unless required by applicable law or agreed to in writing, software
70
+ distributed under the License is distributed on an "AS IS" BASIS,
71
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
72
+ See the License for the specific language governing permissions and
73
+ limitations under the License.
package/NOTICE ADDED
@@ -0,0 +1,19 @@
1
+ Hawiyah — هوية
2
+
3
+ تم التطوير بواسطة فريق التطوير في قطاع المنصات الوطنية - مركز المعلومات الوطني
4
+ Developed by the Development Team in the National Platforms Sector — National Information Center.
5
+
6
+ Copyright 2026 Mohamed Embarak
7
+
8
+ Licensed under the Apache License, Version 2.0. See LICENSE.
9
+
10
+ This distribution includes an Umm al-Qura calendar table derived from
11
+ HijriDate 2.3.0, Copyright (c) 2018 Mohammed Alshehri, licensed under MIT.
12
+ The full notice and provenance are in packages/core/third-party in the
13
+ source repository and third-party in the core package.
14
+
15
+ Brand source fonts are licensed separately under the SIL Open Font License
16
+ 1.1. The unmodified notices accompany their files in brand/third-party.
17
+ Font software and third-party dependencies retain their own licenses.
18
+ The Hawiyah name and identity do not imply endorsement by Saudi Arabia's
19
+ Digital Government Authority. Apache 2.0 does not grant trademark rights.
package/PORTAL.md ADDED
@@ -0,0 +1,70 @@
1
+ # Portal layout
2
+
3
+ `PortalLayout` composes `PortalHeader`, optional `SecondaryNav`, `Breadcrumb`,
4
+ a focusable main container and `PortalFooter`. It defaults to `lang="ar" dir="rtl"`.
5
+
6
+ ```vue
7
+ <script setup lang="ts">
8
+ import { PortalLayout } from '@hawiyah/vue';
9
+ import '@hawiyah/styles/styles.css';
10
+ const links = [{ label: 'الخدمات', href: '/services', current: true }];
11
+ </script>
12
+ <template>
13
+ <PortalLayout brand="هوية" :links="links" :secondary-navigation="links"
14
+ :breadcrumbs="[{ label: 'الرئيسية', href: '/' }, { label: 'الخدمات', current: true }]"
15
+ :footer-links="[{ label: 'الخصوصية', href: '/privacy' }]">
16
+ <h1>الخدمات</h1>
17
+ </PortalLayout>
18
+ </template>
19
+ ```
20
+
21
+ Props: `brand` (required), `homeHref`, `links`, `secondaryNavigation`, `footerLinks`,
22
+ `breadcrumbs`, `copyright`, `mainId`, `lang`, `dir`. Accessible labels:
23
+ `skipLabel`, `menuLabel`, `navigationLabel`, `secondaryLabel`, `breadcrumbLabel`,
24
+ `footerLabel`. `mainId` uses Vue `useId` by default. Slots: `logo`, `actions`,
25
+ `footer`, default (page content). Supply actual artwork through `logo`.
26
+
27
+ For English, pass `lang="en" dir="ltr"` and translate all content and labels.
28
+ Direction and language remain independent. Existing standalone `PortalHeader`
29
+ keeps `brand`, `homeHref`, `links`, `navLabel` and its slots, adding `menuLabel`,
30
+ `navigationId`, `mainId` and `skipLabel`. Escape returns focus to the toggle;
31
+ selection, outside press and focus leaving close the mobile disclosure. Tab is
32
+ native. Desktop links remain visible. `SecondaryNav` uses `items`, `label` and
33
+ ordinary page links with `aria-current`, not menu/tablist semantics.
34
+
35
+ The 48rem mobile breakpoint is Hawiyah policy. Shared tokens preserve DGA, Hawiyah
36
+ and custom themes. The shell introduces no animations. Source-informed
37
+ composition does not establish pixel parity or DGA certification.
38
+
39
+ ## Mobile navigation bar
40
+
41
+ Use `variant="mobile"` on `SecondaryNav` (React/Vue) or `hw-secondary-nav`
42
+ (Angular). Items accept an optional decorative `icon` name from the bundled icon
43
+ stylesheet, such as `home-01`, `search-01` or `menu-01`; labels remain required.
44
+ Vanilla uses `data-variant="mobile"` and the same semantic HTML:
45
+
46
+ ```html
47
+ <nav class="hw-secondary-nav" data-variant="mobile" aria-label="تنقل الجوال">
48
+ <ul>
49
+ <li><a href="/" aria-current="page"><span class="hw-icon" data-icon="home-01" aria-hidden="true"></span><span>الرئيسية</span></a></li>
50
+ <li><a href="/services"><span class="hw-icon" data-icon="search-01" aria-hidden="true"></span><span>الخدمات</span></a></li>
51
+ </ul>
52
+ </nav>
53
+ ```
54
+
55
+ The bar uses equal-width items, 24 px icons, a 70 px minimum height, wrapped labels
56
+ and a top current-page indicator. It is in normal flow and does not cover page
57
+ content. Safe-area bottom padding accommodates mobile devices; the source OS
58
+ home indicator is not reproduced. The application owns route/current updates.
59
+ These are page links, not content tabs: preserve native Tab/Enter navigation.
60
+
61
+ DGA/custom color transitions use `--hw-duration-tab-bar: 300ms` and
62
+ `--hw-ease-tab-bar: cubic-bezier(0, 0, 0.58, 1)`. Hawiyah remains immediate by
63
+ default. Reduced motion removes transitions; forced colors retains the selected
64
+ top border and keyboard focus. Hover does not mark a route selected.
65
+
66
+ Source Mobile `8332:24202` → `8332:24242` → `8332:24282` supports color changes,
67
+ not a sliding indicator or tab-panel animation. Semantic colors adapt to theme;
68
+ icon color follows the label, since imported icon override endpoints were not
69
+ resolved. Arabic width differs from English in the source, so no fixed item
70
+ width is imposed. This is a source-informed composition, not certified parity.
package/README.md CHANGED
@@ -1,3 +1,389 @@
1
- # Temporary Holding Version
1
+ # @hawiyah/vue
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Native Vue 3 components for Hawiyah — هوية, a UI library built to comply with the
4
+ DGA Platforms Code.
5
+ Install with `npm install @hawiyah/styles @hawiyah/vue`. This is a pre-1.0 release,
6
+ so a minor version may still change its API. Import the shared CSS once:
7
+
8
+ ```vue
9
+ <script setup lang="ts">
10
+ import { ref } from "vue";
11
+ import { Button, Combobox, Dialog, TextField } from "@hawiyah/vue";
12
+ import "@hawiyah/styles/styles.css";
13
+ // Optional for a new application: import '@hawiyah/styles/reset.css';
14
+ const name = ref("");
15
+ const city = ref("");
16
+ const reviewing = ref(false);
17
+ const cities = [{ value: "riyadh", label: "الرياض" }];
18
+ </script>
19
+
20
+ <template>
21
+ <form dir="rtl" lang="ar" @submit.prevent="reviewing = true">
22
+ <TextField v-model="name" label="الاسم" name="name" required />
23
+ <Combobox
24
+ v-model="city"
25
+ :options="cities"
26
+ label="المدينة"
27
+ name="city"
28
+ required
29
+ empty-message="لا توجد نتائج"
30
+ required-message="اختر مدينة من القائمة."
31
+ :results-message="(count) => `${count} نتائج متاحة`"
32
+ />
33
+ <Button type="submit">مراجعة</Button>
34
+ </form>
35
+ <Dialog v-model:open="reviewing" title="مراجعة الطلب" close-label="إغلاق">
36
+ <p>{{ name }}</p>
37
+ <template #footer="{ close }"><Button @click="close">تم</Button></template>
38
+ </Dialog>
39
+ </template>
40
+ ```
41
+
42
+ ## Conventions
43
+
44
+ Components use native elements and Vue rendering. They share core keyboard and
45
+ combobox transitions without importing another adapter. Vue `useId()` supplies
46
+ server/client stable accessible IDs; importing or server-rendering the package
47
+ does not access browser globals. Vue 3.5+ and native `<dialog>` support are required.
48
+ A server-rendered open dialog becomes modal after mounting.
49
+
50
+ Native attributes and event listeners fall through to the primary control. Classes
51
+ are merged, and internal event handlers preserve consumer listeners. Field controls
52
+ expose `element` and `focus()` through a Vue template ref; `Button` does too.
53
+ `Dialog` exposes `element`, `show()` and `close()`. `Combobox` adds `close()`.
54
+
55
+ Text inputs, select, combobox, tabs and pagination use `v-model`; omitting it
56
+ uses internal state initialized by `defaultValue`. Checkbox/switch values are
57
+ booleans; radio values are strings matched against the radio's `value` prop.
58
+ `Dialog` uses `v-model:open` / `defaultOpen`. Form reset emits model defaults so
59
+ both controlled and uncontrolled controls can return to their initial state.
60
+ Use `defaultValue` alongside `v-model` if a nonempty reset value is desired.
61
+
62
+ For radios sharing a form and `name`, set `defaultValue` to the option that
63
+ should be checked after a reset. The group emits that value once. A group
64
+ without a checked default resets to an empty string.
65
+
66
+ Locale, direction, theme, mode and density are independent application choices.
67
+ Set `lang`, `dir`, `data-hw-theme="hawiyah|dga|custom"`,
68
+ `data-hw-mode="light|dark"` and `data-hw-density="comfortable|compact|mobile"` on an
69
+ ancestor. Provide translated labels, status messages, pagination labels and
70
+ close labels; the library never infers text language from layout direction.
71
+
72
+ ## Catalog
73
+
74
+ | Export | Main contract |
75
+ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
76
+ | `Button` | `variant`, `size`, `loading`; defaults to `type="button"` |
77
+ | `Field` | `label`, `hint`, `error`, `required`, `controlId`; provides association to a child field control; scoped default slot exposes IDs for native controls |
78
+ | `TextField`, `Textarea` | String `v-model`, `defaultValue`, field decoration; standard input/textarea attributes |
79
+ | `Select` | String `v-model`, `options: Option[]`; native option children also accepted |
80
+ | `Checkbox`, `Switch` | Boolean `v-model`, `defaultValue`, label/hint/error; checkbox supports `indeterminate` |
81
+ | `Radio` | String `v-model`, `value`, shared native `name`; group inside a fieldset with a legend |
82
+ | `Combobox` | String `v-model`, `options`, `disabled`, `readonly`, `emptyMessage`, `resultsMessage(count)`, `requiredMessage`; emits `update:query`, `update:open` |
83
+ | `Tabs` | `items`, required `label`, string `v-model`, `orientation`, `activationMode`, `overflow: menu/scroll` (default menu), configurable `overflowLabel`; item content or named value slots, optional `tab:value` label slots. Compact horizontal containers move excess labels into a keyboard-accessible disclosure; vertical tabs remain visible. |
84
+ | `Dialog` | `title`, `description`, `closeLabel`, `closeOnBackdrop`; default/footer slots receive `{close}`, trigger slot receives `{open}` |
85
+ | `Accordion` | `items: {value,title,content?,open?}[]`, `exclusive`; native details, named value content slots; emits `toggle(value,open)` |
86
+ | `Card` | `header`, default and `footer` slots |
87
+ | `Badge` | Static label; `tone="neutral\|success\|warning\|danger\|info"`, `variant="soft\|outline\|dot"`; [source and adaptations](../../docs/nic-badges.md). |
88
+ | `Alert` | `tone: info | success | warning | danger`, `title`; defaults to status, or alert for danger; native role can override |
89
+ | `Table` | `columns`, `rows`, `rowKey`, `caption`; column `{key,header,cell?,rowHeader?}`; named `cell:key` slots receive `{row,index}`; omit columns to compose native children |
90
+ | `Pagination` | Numeric `v-model`/`defaultValue`, `totalPages`, Arabic default `label`, `previousLabel`, `nextLabel`, `pageLabel(page)`, `formatPage(page)`, `siblingCount=1`, `disabled`, `hideOnSinglePage=false`. Ellipsis disclosures use `gapLabel(start,end)`, `jumpLabel`, `goLabel`; gaps above 100 pages use bounded integer entry. Parent fetches/slices data; no navigation or scrolling is imposed. |
91
+ | `Breadcrumb` | `items: {label,href?,current?}[]`, `label` |
92
+ | `Progress` | Required `label`, optional value for determinate mode, `max=100` |
93
+ | `Skeleton` | Decorative hidden span, size using native style/class |
94
+ | `Spinner` | Status with localized `label` |
95
+ | `Avatar` | Required `name`; optional `src`, `initials`, `size="40\|64\|80\|120\|sm\|md\|lg\|xl"`, `variant="circle\|square"`. Failed images fall back to initials; changing `src` retries. [Source notes](../../docs/nic-avatars.md). |
96
+ | `Divider` | Native `hr` |
97
+ | `Tooltip` | Plain `content`, one focusable trigger in default slot; description IDs merge with the trigger |
98
+ | `FileUpload` | Native input attributes plus field decoration; emits `update:files(File[])`; optional default slot |
99
+ | `Stepper` | `items: {value,label,description?,status?}[]`, `current`, `label` |
100
+ | `EmptyState` | `title`, `description`; `icon`, default, `action` slots |
101
+
102
+ `Field` associates one control; use a native fieldset and legend for groups.
103
+ Combobox is local, editable, single selection: editing clears the submitted value,
104
+ arrow keys skip disabled options, Enter commits explicitly, Escape/Tab/blur/outside
105
+ close without committing. `name` is attached to a hidden input containing the
106
+ selected option value. Required validation checks selection, not merely typed text.
107
+ Readonly comboboxes never open or change selection. Tabs use roving focus and
108
+ RTL-aware horizontal arrows; manual mode selects through native Enter/Space.
109
+ Dialog uses `showModal()` / `close()` for browser modality and focus containment,
110
+ then returns focus to its connected opener. Tooltips must not contain interactive
111
+ content; use a dialog for interactive help.
112
+
113
+ The example in `examples/vue` includes independent locale/direction/theme controls,
114
+ a conditional service form, validation, native modal review, local request filtering
115
+ and pagination, and the remaining presentation components. Unit tests exercise
116
+ behavior and server rendering; browser checks validate the application workflow.
117
+
118
+ ## Expanded controls
119
+
120
+ The package now exports **77 components**: the original 27 and these 50 additions.
121
+ All use native Vue rendering, shared CSS, and the same attrs/slots conventions.
122
+
123
+ | Component | Props, models and events |
124
+ | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
125
+ | `InputGroup` | `prefix`, default and `suffix` slots; the consuming field supplies its label |
126
+ | `PasswordField` | String `v-model`, `defaultValue`, field decoration, `disabled`, `readonly`; `showLabel`, `hideLabel` localize the reveal button; input and button form one input group |
127
+ | `SearchField` | String `v-model`, `defaultValue`, field decoration, `clearLabel`; optional `suggestions`, `maxSuggestions` (8), `resultsMessage(count)`, `showSearchButton` (false), `searchLabel`; emits `search(query)`. Input attributes and exposed `element`/`focus()` remain supported. |
128
+ | `NumberField` | `v-model: number \| ''`; native `min`, `max`, `step` and `required`; clearing preserves an empty value instead of inventing zero |
129
+ | `Slider` | Numeric `v-model`, `defaultValue`, `min=0`, `max=100`, `step=1`, field decoration, `disabled`, `readonly`; native range keyboard behavior |
130
+ | `RangeSlider` | Tuple `v-model: readonly [number,number]`, `defaultValue`, `min`, `max`, `step`; `label`, `lowerLabel`, `upperLabel`, `lowerName`, `upperName`; two native ranges enforce lower ≤ upper |
131
+ | `Rating` | Numeric `v-model`, `defaultValue`, `max=5` (1–20), native `name`, `required`, `label`, `hint`, `error`, `disabled`, `readonly`; `optionLabel(value,max)` localizes each native radio |
132
+ | `RatingDisplay` | Required `label`, numeric `value=0`, `max=5` (1–20), `showValue=true`, `valueLabel(value,max)`; noninteractive fractional aggregate score with one accessible image name. |
133
+ | `PinInput` | String `v-model`, `length=6`, field decoration; one input with `one-time-code`, numeric keyboard, native length/pattern; normalizes Arabic-Indic/Eastern digits; emits `complete(value)` |
134
+ | `TagInput` | String-array `v-model`, `defaultValue`, `name`, `required`, field decoration; Enter/comma adds unique trimmed tags; Backspace on an empty draft removes the last tag; `removeLabel(value)`, `requiredMessage` |
135
+ | `MultiSelect` | String-array `v-model`, `defaultValue`, `options`, `label`, `searchLabel`, `emptyMessage`, `requiredMessage`; normalized search and native checkboxes, repeated hidden values for `name`; no combobox role |
136
+ | `Calendar` | ISO-string `v-model`, `defaultValue`, `month`, `min`, `max`, `locale`, `calendar`, `weekStartsOn`, `isDateDisabled(value)`; `label`, `previousLabel`, `nextLabel`, `unsupportedMessage`; emits `update:month` when browsing |
137
+ | `DatePicker` | Calendar props plus field decoration and string `v-model`; custom ISO text input and modal Calendar; `v-model:calendar` switches `gregory` / `islamic-umalqura` while preserving ISO value; `calendarSwitchable=true`, `calendarSystemLabel`, `gregoryLabel`, `hijriLabel`, `calendarLabel`, `openLabel`, `closeLabel`, `invalidMessage` |
138
+ | `FileDropzone` | Field decoration, native `name`/`form`, `accept`, `multiple`, `maxSize` in bytes, `disabled`, `readonly`, `dropLabel`; emits `update:files(File[])` and `reject(File[])`; valid drops populate the native file input, invalid batches clear it |
139
+
140
+ Value controls support form reset. Arrays reset to `defaultValue`; native file
141
+ controls reset to no files. `TagInput` and `MultiSelect` validate committed
142
+ selection rather than draft/search text. Supply localized validity messages.
143
+ Native attributes such as `name`, `form`, `autocomplete` and `inputmode` pass to
144
+ the control, except that compound controls explicitly manage successful values.
145
+
146
+ `TextField` remains available for standard HTML input types: `text`, `email`,
147
+ `url`, `tel`, `password`, `search`, `date`, `time`, `month`, `week`,
148
+ `datetime-local`, `color`, and others supported by the browser. It uses a string
149
+ model and preserves native input semantics. Use `NumberField` for numeric/empty
150
+ values, `FileUpload`/`FileDropzone` for File objects, and the native choice/range
151
+ components for their specialized models. Native date/time values are machine
152
+ values, independent of localized browser display; `datetime-local` contains no
153
+ time-zone information.
154
+
155
+ ### Switching date systems
156
+
157
+ ```vue
158
+ <script setup lang="ts">
159
+ import { ref } from "vue";
160
+ import { DatePicker } from "@hawiyah/vue";
161
+ import type { CalendarSystem } from "@hawiyah/core";
162
+ const date = ref("2024-03-11");
163
+ const system = ref<CalendarSystem>("islamic-umalqura");
164
+ </script>
165
+
166
+ <template>
167
+ <DatePicker
168
+ v-model="date"
169
+ v-model:calendar="system"
170
+ name="appointment"
171
+ label="تاريخ الموعد"
172
+ locale="ar-SA"
173
+ dir="rtl"
174
+ required
175
+ calendar-label="اختر تاريخ الموعد"
176
+ calendar-system-label="نوع التقويم"
177
+ gregory-label="ميلادي"
178
+ hijri-label="أم القرى"
179
+ open-label="فتح التقويم"
180
+ close-label="إغلاق"
181
+ previous-label="الشهر السابق"
182
+ next-label="الشهر التالي"
183
+ invalid-message="اختر تاريخاً متاحاً."
184
+ unsupported-message="التاريخ خارج نطاق بيانات تقويم أم القرى المدعومة."
185
+ />
186
+ </template>
187
+ ```
188
+
189
+ The default Arabic locale supplies calendar navigation, picker control labels and validation messages. Set `locale="en"` for English or override individual labels through props.
190
+
191
+ The stored/submitted value stays Gregorian `YYYY-MM-DD` when switching display
192
+ calendars. Reopening shows the selected date's month. Calendar supports arrows
193
+ (horizontal direction follows RTL/LTR), Home/End, PageUp/PageDown, and
194
+ Shift+PageUp/PageDown for years. Selection uses native Enter/Space button behavior.
195
+ Disabled dates are skipped, min/max are enforced, and readonly calendars allow
196
+ browsing without changing the selected value.
197
+
198
+ Saudi Umm al-Qura conversion and arithmetic come from the core's pinned Saudi
199
+ reference table, using `@internationalized/date` arithmetic. The supported range
200
+ is **AH 1343–1500, Gregorian 1924-08-01 through 2077-11-16**. Numeric day/year
201
+ values and accessible date names use the same provider through
202
+ `formatCalendarDate`; browser ICU supplies localized names and formatting, not
203
+ an alternative numeric conversion. Navigation stops at supported boundaries.
204
+ Switching an existing out-of-range Gregorian value does not rewrite it: the
205
+ picker displays the caller-localized unsupported message and native validation
206
+ rejects it until the user chooses a supported date or switches back. See the
207
+ core calendar documentation for provenance and coverage; this is not an official
208
+ endorsement or a claim about religious moon-sighting determinations.
209
+
210
+ ## Expanded interactions
211
+
212
+ | Component | Contract |
213
+ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
214
+ | `ToggleGroup` | `options`, `label`, `variant: segmented\|outline`; string `v-model` uses native radio selection; `multiple` uses string-array `v-model` and pressed buttons; `defaultValue`, native `name`/`form`, `disabled`, `readonly` |
215
+ | `Popover` | `label`, `triggerLabel`, `v-model:open` / `defaultOpen`, `disabled`; optional trigger content slot; default slot `{close}`; labelled region, ordinary Tab navigation, outside press and Escape dismissal |
216
+ | `Menu` | `label`, `items: {value,label,disabled?,href?}[]`; optional trigger content slot; emits `select(value)`; Home/End/ArrowUp/ArrowDown skip disabled items, Escape returns focus, Tab exits normally |
217
+ | `Drawer` | Dialog API with `side: start\|end\|bottom`; modal native dialog and preserved trigger focus; default/footer/trigger scoped slots inherited |
218
+ | `ToastRegion` | `messages: {id,title?,message,tone?,urgent?,dismissible?}[]`, `label`, `dismissLabel(message)`, `@dismiss="remove"`. Set `variant="banner"` and optionally `position="inline"` or a corner. A dismissal listener enables the close action. Defaults are Arabic. |
219
+ | `Collapsible` | `label`, boolean `v-model`, `defaultValue`, `disabled`; native details/summary with default content slot |
220
+ | `TreeView` | `items: {id,label,disabled?,children?}[]`, `label`, selected-id `v-model`, `defaultValue`, `v-model:expanded`, `defaultExpanded`, `disabled`; localized `expandLabel(node)` / `collapseLabel(node)`; flattened visible tree with level/position, roving focus and RTL expansion arrows |
221
+ | `Carousel` | Required label/slides; `v-model/defaultValue`, `wrap=true`, `slides` and named slide-ID slots, one-based `slideMessage(index,total)`. `showIndicators=true`, localized control/indicator/description/empty labels, inert inactive slides, explicit navigation and no autoplay. [Source and motion notes](../../docs/nic-carousel.md). |
222
+
223
+ Popover is a disclosure, not a focus trap. Menu is for commands/navigation;
224
+ TreeView does not claim drag-and-drop. Toast and notification dismissal is
225
+ caller-owned: update the supplied message array in response to the emitted ID.
226
+
227
+ ## Expanded content and platform patterns
228
+
229
+ | Component | Contract |
230
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
231
+ | `Link` | Native anchor attrs, `href`, `disabled`, `external`; external links use `noopener noreferrer` |
232
+ | `IconButton` | Required accessible `label`, `variant`, `size`, `disabled`, `loading`; default decorative icon slot |
233
+ | `ButtonGroup` | Required `label`; default buttons slot; native `data-variant` / `data-orientation` for shared CSS variants |
234
+ | `Chip` | `label` or default slot, `tone`, `removable`, `removeLabel`, `disabled`; emits `remove` |
235
+ | `DescriptionList` | `items: {term,description}[]`, `variant: stacked\|horizontal\|compact`; `item:index` slots receive `{item}` |
236
+ | `Stat` | `label`, string/number `value`, `locale`, `description`, `trend`; numeric values format with Intl |
237
+ | `Timeline` | `label`, `items: {id,title,description?,date?,dateLabel?,status?}[]`; named item-ID slots; machine-readable native time, optional current step |
238
+ | `Meter` | Required `label`, `value`; native `min`, `max`, `low`, `high`, `optimum` semantics |
239
+ | `AvatarGroup` | `label`, `items: {name,src?,initials?}[]`, `max`, `remainingLabel(count)` |
240
+ | `DataTable` | `caption`, `rows: Record<string,unknown>[]`, `columns: {key,label,sortable?,rowHeader?}[]`, `rowKey`, `locale`; `v-model:sort-key`, `v-model:sort-direction`, initial `defaultSortKey` / `defaultSortDirection`; `sortLabel(column,nextDirection)`; `cell:key` slots receive `{row,index}` |
241
+ | `ServiceCard` | `title`, `description`, `href`, `actionLabel`, `badge`, `metadata`; `icon`, default and `action` slots |
242
+ | `ServiceDetails` | `title`, `description`, DescriptionList `items`; default and `action` slots |
243
+ | `PortalHeader` | `brand`, `homeHref`, `links: {label,href,current?}[]`, `navLabel`; `logo`, `actions`, default slots Optional top-level `groups` contain a label and child links. See [grouped navigation](../../docs/nic-portal-navigation.md). |
244
+ | `PortalFooter` | `brand`, `copyright`, `links`, `navLabel`; default content slot |
245
+ | `Feedback` | `label`, selected answer string `v-model` (`yes`/`no`), `defaultValue`, `yesLabel`, `noLabel`, `commentLabel`, `submitLabel`, `thanks`, `disabled`, `readonly`; emits `submit({value,comment})` |
246
+ | `CookieConsent` | `v-model:open`, `defaultOpen=true`, `label`, required `description`, `acceptLabel`, `rejectLabel`, `preferencesLabel`; emits `accept`, `reject`, `preferences`; no hidden storage or third-party initialization |
247
+ | `NotificationList` | `label`, `items: {id,title,description?,date?,dateLabel?,read?,href?}[]`, `emptyMessage`, `readLabel(item)`, `dismissLabel(item)`; emits `read(id)`, `dismiss(id)`; named ID slots |
248
+
249
+ DataTable sorting is immutable, stable and localized. It remains a native table;
250
+ use the existing Table component for unrestricted table composition. Platform
251
+ patterns render content supplied by the application: no government seals, trust
252
+ verification, identity verification, analytics or backend service are implied.
253
+ Feedback emits data for the consuming application to persist; CookieConsent
254
+ emits decisions for the application to enforce and persist.
255
+
256
+ ## Layout primitives
257
+
258
+ All layout primitives forward native HTML attributes and accept `as` (default
259
+ `div`, or `section` for Section). Shared `LayoutProps` includes:
260
+
261
+ - `gap` and `padding`: token numbers 0–16 or CSS length strings, optionally
262
+ responsive `{base,sm,md,lg,xl}` objects.
263
+ - `align: start|center|end|stretch|baseline`,
264
+ `justify: start|center|end|between|around|evenly`, and `wrap`.
265
+ - `columns`: a positive number or responsive object; `minItemWidth` opts into
266
+ adaptive auto-fit columns instead.
267
+ - `side: start|end`, `sidebarWidth`, `collapseAt: sm|md|lg`, `maxWidth`, `ratio`.
268
+
269
+ | Component | Composition |
270
+ | ------------- | --------------------------------------------------------------------------------------------------------------------- |
271
+ | `Container` | Constrained centered content, optional `maxWidth` |
272
+ | `Stack` | Vertical flex layout |
273
+ | `Inline` | Horizontal flex layout with optional wrapping |
274
+ | `Grid` | Responsive explicit or adaptive grid |
275
+ | `Columns` | Same grid contract with a column-composition semantic name |
276
+ | `Sidebar` | First child is sidebar, second child is main content; `side` changes visual placement, `collapseAt` controls stacking |
277
+ | `Center` | Centered flex content |
278
+ | `Spacer` | Flexible empty space; keep decorative |
279
+ | `AspectRatio` | Media frame with positive `ratio`, e.g. `16 / 9` |
280
+ | `Section` | Semantic section; name it with native `aria-label` or a heading |
281
+ | `AppShell` | Use `header`, `sidebar`, default main content and `footer` slots. The Arabic skip link focuses main. Set `mainId` to choose its target ID and `skipLabel` to localize the link. |
282
+ | `PageHeader` | `title`, `description`, heading `level=1`; `breadcrumb`, `actions`, default slots; outer element is always header |
283
+
284
+ ```vue
285
+ <Container max-width="72rem">
286
+ <Stack :gap="{ base: 4, lg: 8 }">
287
+ <PageHeader title="خدماتك" description="اختر الخدمة المناسبة">
288
+ <template #actions><Button>طلب جديد</Button></template>
289
+ </PageHeader>
290
+ <Grid :columns="{ base: 1, md: 2, lg: 3 }" :gap="4">
291
+ <ServiceCard title="تجديد طلب" description="راجع التفاصيل ثم قدّم الطلب."
292
+ href="/services/renew" action-label="بدء الخدمة" />
293
+ <ServiceCard title="متابعة طلب" href="/requests" action-label="عرض الطلبات" />
294
+ </Grid>
295
+ </Stack>
296
+ </Container>
297
+ ```
298
+
299
+ Breakpoints are CSS-only: sm 40rem, md 48rem, lg 64rem, xl 80rem. Server and
300
+ client emit identical custom properties through shared `layoutStyles`; no
301
+ viewport measurements or hydration-dependent layout branches are used.
302
+
303
+ Maps, rich text, charting, code editors, PDF viewers, CAPTCHA and payments are
304
+ integration boundaries. Mount the chosen provider in a named Section/Card or
305
+ AspectRatio with its own accessible fallback and documented data contract. Hawiyah
306
+ does not simulate these engines or handle provider credentials or transactions.
307
+
308
+ ## Portal shell
309
+
310
+ [`PortalLayout`, `PortalHeader` and `SecondaryNav`](PORTAL.md) provide a complete
311
+ Arabic-default portal shell with responsive navigation, breadcrumbs and footer.
312
+ Run the Vue example with `?preview=portal` to use the native composed shell.
313
+
314
+ ## NIC card composition
315
+
316
+ See [cards](../../docs/nic-cards.md) for source-mapped spacing, variants, whole-card links, disabled semantics and content classes. Native framework consumers include `?preview=nic`; vanilla uses the documented semantic HTML recipe.
317
+
318
+ See [NIC button variants](../../docs/nic-buttons.md) for all ten appearances,
319
+ interaction states, dark-surface use and density adaptations.
320
+
321
+ [Ready component examples](../../docs/ready-component-examples.md) cover all five
322
+ alert/notification tones, header/footer composition and guide-only navigation containment.
323
+ # Native choice read-only state
324
+
325
+ `Checkbox`, `Radio` and `Switch` accept `readOnly` (`read-only` in templates).
326
+ The control remains keyboard-focusable and submits its value, while pointer and
327
+ keyboard activation leave its value and radio peers unchanged. Disabled remains
328
+ a separate native state. See [NIC states](../../docs/nic-choices.md).
329
+
330
+
331
+ [Site verification](../../docs/site-verification.md) composes a native disclosure above the header with deployment-owned domain and certificate information. [Grouped portal footers](../../docs/nic-footer.md) add navigation groups, support actions, legal links and identity slots.
332
+
333
+ [Link and breadcrumb states](../../docs/nic-links.md) document neutral/standalone variants, disabled destinations, source contrast substitutions and native adapter APIs.
334
+
335
+
336
+ ### Table presentation and empty results
337
+
338
+ `Table` and `DataTable` expose `variant="default|striped|bordered"`,
339
+ `size="sm|md|lg"` and `emptyMessage`. DGA cells have a 48px default minimum;
340
+ 40px/56px alternate sizes and striped/bordered variants are Hawiyah extensions.
341
+ Captions name keyboard-focusable horizontal scroll containers; row-header
342
+ columns render native `th scope="row"`. Empty data spans the column count and
343
+ uses an Arabic message by default. Sorting does not mutate caller records.
344
+ React/Vue native `data-variant` and `data-size` attributes remain supported;
345
+ typed props take precedence. `wrapperProps` customizes the scroll container.
346
+ The default sort-action label is Arabic; pass `sortLabel` for another language.
347
+ See [table evidence and adaptations](../../docs/nic-tables.md).
348
+
349
+ Rating inputs select whole numbers; finite prefilled values are clamped to the normalized maximum and floored. Fractional averages belong in `RatingDisplay`. Nonfinite maxima fall back to 5 and nonfinite values to 0. Arabic labels and Latin digits are the defaults.
350
+
351
+
352
+ ### Search suggestions
353
+
354
+ SearchField accepts local string suggestions and remains a free-text input.
355
+ With suggestions supplied it exposes an editable combobox/listbox, highlighted
356
+ matches and a polite result count. Arrow keys wrap through matches; Enter on an
357
+ active suggestion selects it and prevents form submission. Ordinary Enter with
358
+ no active suggestion emits `search` with a trimmed, nonempty query and keeps
359
+ native form submission available. Choose either the search callback or the
360
+ form's submit handler for the application request; wiring both duplicates it.
361
+ The optional icon search action is a non-submit button and uses the callback.
362
+ Arabic clear/search labels are defaults and can be localized.
363
+
364
+ Escape dismisses an open list before reaching an enclosing dialog; another
365
+ Escape can dismiss the dialog. Composition/modifier keys do not commit matches.
366
+ Readonly inputs remain searchable but cannot edit/clear/select suggestions;
367
+ disabled inputs cannot search. Blur, outside press, reset and changed suggestion
368
+ configuration clear active-option state. No remote search service is called.
369
+
370
+ ### Content navigation
371
+
372
+ `ContentNavigation` follows application-owned section IDs using nested
373
+ `items: {id, label, disabled?, children?}[]`. Options are `label` (Arabic default),
374
+ `showHeading` (true), `sticky` (false), `scrollRoot` (null/window) and `offset`
375
+ (80px). `@current-change="onCurrentChange"` reports the current section ID. Ordinary
376
+ activation scrolls without changing the URL; keyboard activation also focuses
377
+ the target. Modified anchor clicks retain browser behavior. Disabled entries
378
+ remain noninteractive text links; no section content is created or hidden.
379
+
380
+ ```vue
381
+ <script setup>
382
+ import {ContentNavigation} from '@hawiyah/vue';
383
+ const sections = [{id: 'requirements', label: 'المتطلبات'}];
384
+ </script>
385
+ <template>
386
+ <ContentNavigation :items="sections" />
387
+ <section id="requirements"><h2>المتطلبات</h2></section>
388
+ </template>
389
+ ```