@cocoar/vue-ui 3.1.0 → 3.2.0-beta.6
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/dist/components/checkbox/CoarCheckbox.vue.d.ts +1 -1
- package/dist/components/checkbox/CoarCheckbox.vue.d.ts.map +1 -1
- package/dist/components/code-block/CoarCodeBlock.vue.d.ts +1 -1
- package/dist/components/code-block/CoarCodeBlock.vue.d.ts.map +1 -1
- package/dist/components/data-list/CoarDataList.vue.d.ts +158 -0
- package/dist/components/data-list/CoarDataList.vue.d.ts.map +1 -0
- package/dist/components/data-list/CoarDataListToolbar.vue.d.ts +30 -0
- package/dist/components/data-list/CoarDataListToolbar.vue.d.ts.map +1 -0
- package/dist/components/data-list/data-list-builder.d.ts +288 -0
- package/dist/components/data-list/data-list-builder.d.ts.map +1 -0
- package/dist/components/data-list/index.d.ts +12 -0
- package/dist/components/data-list/index.d.ts.map +1 -0
- package/dist/components/data-list/internal/compare.d.ts +14 -0
- package/dist/components/data-list/internal/compare.d.ts.map +1 -0
- package/dist/components/data-list/internal/reorder-core.d.ts +46 -0
- package/dist/components/data-list/internal/reorder-core.d.ts.map +1 -0
- package/dist/components/data-list/internal/search.d.ts +16 -0
- package/dist/components/data-list/internal/search.d.ts.map +1 -0
- package/dist/components/data-list/internal/useDataListLines.d.ts +92 -0
- package/dist/components/data-list/internal/useDataListLines.d.ts.map +1 -0
- package/dist/components/data-list/internal/useDataListReorder.d.ts +68 -0
- package/dist/components/data-list/internal/useDataListReorder.d.ts.map +1 -0
- package/dist/components/data-list/internal/useSearchHighlight.d.ts +19 -0
- package/dist/components/data-list/internal/useSearchHighlight.d.ts.map +1 -0
- package/dist/components/data-list/types.d.ts +185 -0
- package/dist/components/data-list/types.d.ts.map +1 -0
- package/dist/components/data-list/useDataListModel.d.ts +79 -0
- package/dist/components/data-list/useDataListModel.d.ts.map +1 -0
- package/dist/components/date-time/_shared/maskito-config.d.ts +7 -2
- package/dist/components/date-time/_shared/maskito-config.d.ts.map +1 -1
- package/dist/components/date-time/_shared/time-helpers.d.ts +11 -4
- package/dist/components/date-time/_shared/time-helpers.d.ts.map +1 -1
- package/dist/components/date-time/_shared/use-date-picker-base.d.ts +1 -1
- package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts +1 -1
- package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts.map +1 -1
- package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts +1 -1
- package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts.map +1 -1
- package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts +1 -1
- package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts.map +1 -1
- package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts +3 -0
- package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts.map +1 -1
- package/dist/components/form-field/CoarFormField.vue.d.ts +1 -1
- package/dist/components/form-field/CoarFormField.vue.d.ts.map +1 -1
- package/dist/components/icon/CoarIcon.vue.d.ts +2 -2
- package/dist/components/icon/CoarIcon.vue.d.ts.map +1 -1
- package/dist/components/listbox/CoarListbox.vue.d.ts +7 -0
- package/dist/components/listbox/CoarListbox.vue.d.ts.map +1 -1
- package/dist/components/number-input/CoarNumberInput.vue.d.ts +1 -1
- package/dist/components/number-input/CoarNumberInput.vue.d.ts.map +1 -1
- package/dist/components/otp-input/CoarOtpInput.vue.d.ts +1 -1
- package/dist/components/otp-input/CoarOtpInput.vue.d.ts.map +1 -1
- package/dist/components/password-input/CoarPasswordInput.vue.d.ts +1 -1
- package/dist/components/password-input/CoarPasswordInput.vue.d.ts.map +1 -1
- package/dist/components/switch/CoarSwitch.vue.d.ts +1 -1
- package/dist/components/switch/CoarSwitch.vue.d.ts.map +1 -1
- package/dist/components/text-input/CoarTextInput.vue.d.ts +1 -1
- package/dist/components/text-input/CoarTextInput.vue.d.ts.map +1 -1
- package/dist/composables/dragRegistry.d.ts +12 -0
- package/dist/composables/dragRegistry.d.ts.map +1 -1
- package/dist/composables/index.d.ts +2 -2
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useDragDrop.d.ts +62 -6
- package/dist/composables/useDragDrop.d.ts.map +1 -1
- package/dist/composables/useVirtualList.d.ts +21 -1
- package/dist/composables/useVirtualList.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4508 -2039
- package/package.json +5 -3
- package/skills/cocoar-vue-ui/SKILL.md +244 -0
- package/skills/cocoar-vue-ui/references/components/avatar.md +178 -0
- package/skills/cocoar-vue-ui/references/components/badge.md +290 -0
- package/skills/cocoar-vue-ui/references/components/breadcrumb.md +238 -0
- package/skills/cocoar-vue-ui/references/components/button.md +252 -0
- package/skills/cocoar-vue-ui/references/components/calendar/agenda-view.md +353 -0
- package/skills/cocoar-vue-ui/references/components/calendar/coar-calendar.md +1645 -0
- package/skills/cocoar-vue-ui/references/components/calendar/day-view.md +361 -0
- package/skills/cocoar-vue-ui/references/components/calendar/index.md +312 -0
- package/skills/cocoar-vue-ui/references/components/calendar/month-view.md +475 -0
- package/skills/cocoar-vue-ui/references/components/calendar/performance.md +186 -0
- package/skills/cocoar-vue-ui/references/components/calendar/timeline-view.md +301 -0
- package/skills/cocoar-vue-ui/references/components/calendar/week-view.md +382 -0
- package/skills/cocoar-vue-ui/references/components/calendar/work-week-view.md +145 -0
- package/skills/cocoar-vue-ui/references/components/calendar/year-view.md +63 -0
- package/skills/cocoar-vue-ui/references/components/card.md +151 -0
- package/skills/cocoar-vue-ui/references/components/checkbox-group.md +103 -0
- package/skills/cocoar-vue-ui/references/components/checkbox.md +279 -0
- package/skills/cocoar-vue-ui/references/components/code-block.md +241 -0
- package/skills/cocoar-vue-ui/references/components/context-menu.md +355 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/checkbox.md +213 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/date-columns.md +272 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/editing.md +236 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/multi-select.md +193 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/number.md +147 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/select.md +202 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/text.md +127 -0
- package/skills/cocoar-vue-ui/references/components/data-grid.md +1225 -0
- package/skills/cocoar-vue-ui/references/components/data-list.md +2121 -0
- package/skills/cocoar-vue-ui/references/components/date-or-time-picker.md +200 -0
- package/skills/cocoar-vue-ui/references/components/date-picker.md +203 -0
- package/skills/cocoar-vue-ui/references/components/date-time-picker.md +154 -0
- package/skills/cocoar-vue-ui/references/components/date-views.md +212 -0
- package/skills/cocoar-vue-ui/references/components/dialog.md +148 -0
- package/skills/cocoar-vue-ui/references/components/divider.md +157 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/annotations.md +400 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/coar-document-viewer.md +327 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/index.md +234 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/toolbar.md +221 -0
- package/skills/cocoar-vue-ui/references/components/drag-drop.md +267 -0
- package/skills/cocoar-vue-ui/references/components/dual-listbox.md +449 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/asset-store.md +327 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/in-memory-store.md +176 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/index.md +311 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/use-file-explorer.md +300 -0
- package/skills/cocoar-vue-ui/references/components/form-field.md +1025 -0
- package/skills/cocoar-vue-ui/references/components/fragment-parser.md +298 -0
- package/skills/cocoar-vue-ui/references/components/link.md +235 -0
- package/skills/cocoar-vue-ui/references/components/listbox.md +575 -0
- package/skills/cocoar-vue-ui/references/components/map/editor.md +448 -0
- package/skills/cocoar-vue-ui/references/components/map/index.md +351 -0
- package/skills/cocoar-vue-ui/references/components/markdown-diagrams.md +210 -0
- package/skills/cocoar-vue-ui/references/components/markdown-editor.md +1478 -0
- package/skills/cocoar-vue-ui/references/components/markdown-embeds.md +387 -0
- package/skills/cocoar-vue-ui/references/components/markdown-form.md +447 -0
- package/skills/cocoar-vue-ui/references/components/markdown.md +276 -0
- package/skills/cocoar-vue-ui/references/components/menu.md +380 -0
- package/skills/cocoar-vue-ui/references/components/mermaid.md +172 -0
- package/skills/cocoar-vue-ui/references/components/navbar.md +147 -0
- package/skills/cocoar-vue-ui/references/components/note.md +96 -0
- package/skills/cocoar-vue-ui/references/components/notice.md +131 -0
- package/skills/cocoar-vue-ui/references/components/number-input.md +215 -0
- package/skills/cocoar-vue-ui/references/components/otp-input.md +324 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/authoring-contract.md +271 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-builder.md +476 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-renderer.md +677 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/custom-elements.md +441 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/idp-integration.md +122 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/index.md +573 -0
- package/skills/cocoar-vue-ui/references/components/pagination.md +150 -0
- package/skills/cocoar-vue-ui/references/components/panel-layout.md +372 -0
- package/skills/cocoar-vue-ui/references/components/password-input.md +175 -0
- package/skills/cocoar-vue-ui/references/components/popconfirm.md +186 -0
- package/skills/cocoar-vue-ui/references/components/popover.md +173 -0
- package/skills/cocoar-vue-ui/references/components/progress-bar.md +178 -0
- package/skills/cocoar-vue-ui/references/components/radio-group.md +225 -0
- package/skills/cocoar-vue-ui/references/components/script-editor.md +1271 -0
- package/skills/cocoar-vue-ui/references/components/segmented-control.md +238 -0
- package/skills/cocoar-vue-ui/references/components/select.md +463 -0
- package/skills/cocoar-vue-ui/references/components/sidebar.md +421 -0
- package/skills/cocoar-vue-ui/references/components/spinner.md +132 -0
- package/skills/cocoar-vue-ui/references/components/switch.md +195 -0
- package/skills/cocoar-vue-ui/references/components/table.md +170 -0
- package/skills/cocoar-vue-ui/references/components/tabs.md +382 -0
- package/skills/cocoar-vue-ui/references/components/tag.md +178 -0
- package/skills/cocoar-vue-ui/references/components/text-input.md +256 -0
- package/skills/cocoar-vue-ui/references/components/toast.md +160 -0
- package/skills/cocoar-vue-ui/references/components/tooltip.md +121 -0
- package/skills/cocoar-vue-ui/references/components/transitions.md +193 -0
- package/skills/cocoar-vue-ui/references/components/tree.md +2388 -0
- package/skills/cocoar-vue-ui/references/components/virtual-list.md +212 -0
- package/skills/cocoar-vue-ui/references/components/wizard.md +251 -0
- package/skills/cocoar-vue-ui/references/components/zoned-date-time-picker.md +177 -0
- package/skills/cocoar-vue-ui/references/foundations/colors.md +708 -0
- package/skills/cocoar-vue-ui/references/foundations/design-principles.md +115 -0
- package/skills/cocoar-vue-ui/references/foundations/icons.md +381 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/formatting.md +530 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/setup.md +86 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/timezones.md +182 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/translations.md +305 -0
- package/skills/cocoar-vue-ui/references/foundations/motion.md +549 -0
- package/skills/cocoar-vue-ui/references/foundations/spacing.md +330 -0
- package/skills/cocoar-vue-ui/references/foundations/theming.md +140 -0
- package/skills/cocoar-vue-ui/references/foundations/typography.md +206 -0
- package/skills/cocoar-vue-ui/references/guide/error-handling.md +162 -0
- package/skills/cocoar-vue-ui/references/guide/getting-started.md +119 -0
- package/skills/cocoar-vue-ui/references/guide/migration-page-builder-3.md +207 -0
- package/skills/cocoar-vue-ui/references/guide/migration.md +140 -0
- package/skills/cocoar-vue-ui/references/guide/theming.md +98 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
<!-- Generated from apps/docs/components/pagination.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
|
|
2
|
+
|
|
3
|
+
# Pagination
|
|
4
|
+
|
|
5
|
+
Split large collections into digestible pages and let users navigate between them. Pagination automatically calculates page count from `totalItems` and `pageSize`, and intelligently truncates the page range with ellipses when there are too many pages to display at once.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { CoarPagination } from '@cocoar/vue-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Basic Usage
|
|
12
|
+
|
|
13
|
+
Bind a reactive page number with `v-model`. This example paginates 100 items at 10 per page, yielding 10 pages.
|
|
14
|
+
|
|
15
|
+
**Demo — `pagination/demos/PaginationBasic.vue`**
|
|
16
|
+
|
|
17
|
+
```vue
|
|
18
|
+
<template>
|
|
19
|
+
<div>
|
|
20
|
+
<CoarPagination v-model="page" :total-items="100" :page-size="10" />
|
|
21
|
+
<p style="margin-top: 8px; font-size: 13px; color: var(--coar-text-neutral-secondary);">Current page: {{ page }}</p>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup lang="ts">
|
|
26
|
+
import { ref } from 'vue';
|
|
27
|
+
import { CoarPagination } from '@cocoar/vue-ui';
|
|
28
|
+
|
|
29
|
+
const page = ref(1);
|
|
30
|
+
</script>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Different Page Size
|
|
34
|
+
|
|
35
|
+
Adjust `totalItems` and `pageSize` to match your data. Here, 500 items at 25 per page produces 20 pages.
|
|
36
|
+
|
|
37
|
+
**Demo — `pagination/demos/PaginationPageSize.vue`**
|
|
38
|
+
|
|
39
|
+
```vue
|
|
40
|
+
<template>
|
|
41
|
+
<div>
|
|
42
|
+
<CoarPagination v-model="page" :total-items="500" :page-size="25" />
|
|
43
|
+
<p style="margin-top: 8px; font-size: 13px; color: var(--coar-text-neutral-secondary);">Current page: {{ page }} / 20</p>
|
|
44
|
+
</div>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script setup lang="ts">
|
|
48
|
+
import { ref } from 'vue';
|
|
49
|
+
import { CoarPagination } from '@cocoar/vue-ui';
|
|
50
|
+
|
|
51
|
+
const page = ref(3);
|
|
52
|
+
</script>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Large Dataset
|
|
56
|
+
|
|
57
|
+
With 10,000 items, the page count reaches 1,000. The component automatically shows ellipsis markers so the control stays compact no matter how many pages exist.
|
|
58
|
+
|
|
59
|
+
**Demo — `pagination/demos/PaginationLarge.vue`**
|
|
60
|
+
|
|
61
|
+
```vue
|
|
62
|
+
<template>
|
|
63
|
+
<div>
|
|
64
|
+
<CoarPagination v-model="page" :total-items="10000" :page-size="10" />
|
|
65
|
+
<p style="margin-top: 8px; font-size: 13px; color: var(--coar-text-neutral-secondary);">Current page: {{ page }} / 1000</p>
|
|
66
|
+
</div>
|
|
67
|
+
</template>
|
|
68
|
+
|
|
69
|
+
<script setup lang="ts">
|
|
70
|
+
import { ref } from 'vue';
|
|
71
|
+
import { CoarPagination } from '@cocoar/vue-ui';
|
|
72
|
+
|
|
73
|
+
const page = ref(1);
|
|
74
|
+
</script>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## With Table
|
|
78
|
+
|
|
79
|
+
The most common real-world pattern: pair pagination with a data table. A summary line shows the visible row range, and page controls sit alongside it in the table footer.
|
|
80
|
+
|
|
81
|
+
**Demo — `pagination/demos/PaginationTable.vue`**
|
|
82
|
+
|
|
83
|
+
```vue
|
|
84
|
+
<template>
|
|
85
|
+
<div style="display: flex; flex-direction: column; gap: 16px;">
|
|
86
|
+
<table style="width: 100%; border-collapse: collapse; font-size: 13px;">
|
|
87
|
+
<thead>
|
|
88
|
+
<tr>
|
|
89
|
+
<th style="padding: 6px 12px; text-align: left; border-bottom: 1px solid var(--coar-border-neutral-secondary); font-weight: 600; color: var(--coar-text-neutral-secondary); background: var(--coar-background-neutral-secondary);">Name</th>
|
|
90
|
+
<th style="padding: 6px 12px; text-align: left; border-bottom: 1px solid var(--coar-border-neutral-secondary); font-weight: 600; color: var(--coar-text-neutral-secondary); background: var(--coar-background-neutral-secondary);">Email</th>
|
|
91
|
+
<th style="padding: 6px 12px; text-align: left; border-bottom: 1px solid var(--coar-border-neutral-secondary); font-weight: 600; color: var(--coar-text-neutral-secondary); background: var(--coar-background-neutral-secondary);">Role</th>
|
|
92
|
+
</tr>
|
|
93
|
+
</thead>
|
|
94
|
+
<tbody>
|
|
95
|
+
<tr v-for="i in 5" :key="i">
|
|
96
|
+
<td style="padding: 6px 12px; border-bottom: 1px solid var(--coar-border-neutral-secondary);">User {{ (page - 1) * 5 + i }}</td>
|
|
97
|
+
<td style="padding: 6px 12px; border-bottom: 1px solid var(--coar-border-neutral-secondary);">user{{ (page - 1) * 5 + i }}@example.com</td>
|
|
98
|
+
<td style="padding: 6px 12px; border-bottom: 1px solid var(--coar-border-neutral-secondary);">{{ i % 2 === 0 ? 'Admin' : 'User' }}</td>
|
|
99
|
+
</tr>
|
|
100
|
+
</tbody>
|
|
101
|
+
</table>
|
|
102
|
+
<div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px;">
|
|
103
|
+
<span style="font-size: 13px; color: var(--coar-text-neutral-secondary);">
|
|
104
|
+
Showing {{ (page - 1) * 5 + 1 }}–{{ Math.min(page * 5, 100) }} of 100
|
|
105
|
+
</span>
|
|
106
|
+
<CoarPagination v-model="page" :total-items="100" :page-size="5" />
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</template>
|
|
110
|
+
|
|
111
|
+
<script setup lang="ts">
|
|
112
|
+
import { ref } from 'vue';
|
|
113
|
+
import { CoarPagination } from '@cocoar/vue-ui';
|
|
114
|
+
|
|
115
|
+
const page = ref(1);
|
|
116
|
+
</script>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## API
|
|
120
|
+
|
|
121
|
+
### Props
|
|
122
|
+
|
|
123
|
+
| Prop | Type | Default | Description |
|
|
124
|
+
|------|------|---------|-------------|
|
|
125
|
+
| `v-model` | `number` | `1` | Current active page (1-indexed) |
|
|
126
|
+
| `totalItems` | `number` | — | Total number of items (**required**) |
|
|
127
|
+
| `pageSize` | `number` | `10` | Items per page |
|
|
128
|
+
| `maxVisiblePages` | `number` | `5` | Maximum visible page buttons |
|
|
129
|
+
| `showFirstLast` | `boolean` | `true` | Show first/last page nav buttons |
|
|
130
|
+
| `disabled` | `boolean` | `false` | Disable the pagination control |
|
|
131
|
+
|
|
132
|
+
### Events
|
|
133
|
+
|
|
134
|
+
| Event | Payload | Description |
|
|
135
|
+
|-------|---------|-------------|
|
|
136
|
+
| `pageChanged` | `number` | Emitted when the page changes |
|
|
137
|
+
|
|
138
|
+
## i18n Keys
|
|
139
|
+
|
|
140
|
+
These keys can be translated via [`@cocoar/vue-localization`](../foundations/localization/translations.md).
|
|
141
|
+
|
|
142
|
+
| Key | Default (English) | Used as |
|
|
143
|
+
|-----|-------------------|---------|
|
|
144
|
+
| `coar.ui.pagination.firstPage` | `'Go to first page'` | First page button `aria-label` |
|
|
145
|
+
| `coar.ui.pagination.previousPage` | `'Go to previous page'` | Previous page button `aria-label` |
|
|
146
|
+
| `coar.ui.pagination.nextPage` | `'Go to next page'` | Next page button `aria-label` |
|
|
147
|
+
| `coar.ui.pagination.lastPage` | `'Go to last page'` | Last page button `aria-label` |
|
|
148
|
+
| `coar.ui.pagination.morePage` | `'More pages'` | Ellipsis separator `aria-label` |
|
|
149
|
+
| `coar.ui.pagination.goToPage` | `'Go to page {page}'` | Page number button `aria-label` — `{page}` is replaced with the page number |
|
|
150
|
+
| `coar.ui.pagination.nav` | `'Pagination'` | Nav element `aria-label` |
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
<!-- Generated from apps/docs/components/panel-layout.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
|
|
2
|
+
|
|
3
|
+
# Panel Layout (Preview)
|
|
4
|
+
|
|
5
|
+
A VS-Code-style workbench: named, resizable regions you fill with whatever you like. Two pieces:
|
|
6
|
+
|
|
7
|
+
- **`CoarSplitPane`** — the primitive: two panes with a draggable divider. Nestable.
|
|
8
|
+
- **`CoarPanelLayout`** — a ready-made shell with `top / left / content / right / bottom / status` regions, built on `CoarSplitPane` so every divider feels identical.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { CoarSplitPane, CoarPanelLayout } from '@cocoar/vue-ui';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The layout owns the arrangement and the drag-to-resize; **you own the contents** of every slot. Resizing works by pointer drag or arrow keys (the dividers are real `role="separator"` widgets).
|
|
15
|
+
|
|
16
|
+
## Split pane
|
|
17
|
+
|
|
18
|
+
`CoarSplitPane` lays out two slots — `#first` and `#second` — with a divider between them. The `side` pane carries a controlled px `size` (`v-model:size`); the other flexes. `direction="row"` gives a vertical divider (left/right); `direction="column"` gives a horizontal one (top/bottom).
|
|
19
|
+
|
|
20
|
+
**Demo — `panel-layout/demos/SplitPaneBasic.vue`**
|
|
21
|
+
|
|
22
|
+
```vue
|
|
23
|
+
<template>
|
|
24
|
+
<div class="sp-frame">
|
|
25
|
+
<CoarSplitPane v-model:size="size" :min="120" :max="420">
|
|
26
|
+
<template #first>
|
|
27
|
+
<div class="sp-pane sp-pane--a">
|
|
28
|
+
<strong>Sidebar</strong>
|
|
29
|
+
<span>{{ size }}px — drag the divider →</span>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
<template #second>
|
|
33
|
+
<div class="sp-pane sp-pane--b">
|
|
34
|
+
<strong>Content</strong>
|
|
35
|
+
<span>flexes to fill the rest</span>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
</CoarSplitPane>
|
|
39
|
+
</div>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<script setup lang="ts">
|
|
43
|
+
import { ref } from 'vue';
|
|
44
|
+
import { CoarSplitPane } from '@cocoar/vue-ui';
|
|
45
|
+
|
|
46
|
+
const size = ref(200);
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<style scoped>
|
|
50
|
+
.sp-frame {
|
|
51
|
+
height: 220px;
|
|
52
|
+
border: 1px solid var(--vp-c-divider);
|
|
53
|
+
border-radius: 8px;
|
|
54
|
+
overflow: hidden;
|
|
55
|
+
}
|
|
56
|
+
.sp-pane {
|
|
57
|
+
height: 100%;
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
gap: 4px;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
font-size: 13px;
|
|
64
|
+
}
|
|
65
|
+
.sp-pane span {
|
|
66
|
+
font-size: 12px;
|
|
67
|
+
color: var(--coar-text-neutral-tertiary, #6b7280);
|
|
68
|
+
}
|
|
69
|
+
.sp-pane--a {
|
|
70
|
+
background: var(--vp-c-bg-soft);
|
|
71
|
+
}
|
|
72
|
+
.sp-pane--b {
|
|
73
|
+
background: var(--vp-c-bg);
|
|
74
|
+
}
|
|
75
|
+
</style>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```vue
|
|
79
|
+
<CoarSplitPane v-model:size="width" :min="120" :max="420">
|
|
80
|
+
<template #first>…sidebar…</template>
|
|
81
|
+
<template #second>…content…</template>
|
|
82
|
+
</CoarSplitPane>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
| Prop | Default | Notes |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `direction` | `'row'` | `'row'` = side by side (vertical divider); `'column'` = stacked (horizontal divider). |
|
|
88
|
+
| `side` | `'first'` | Which pane carries the controlled `size`. Use `'second'` for a right sidebar or bottom panel. |
|
|
89
|
+
| `size` (`v-model`) | — | Controlled px size of the `side` pane. Omit for uncontrolled (seeded by `defaultSize`). |
|
|
90
|
+
| `defaultSize` | `240` | Initial size when uncontrolled. |
|
|
91
|
+
| `min` / `max` | `0` / `∞` | Clamp for the `side` pane (also clamped to the container). |
|
|
92
|
+
| `resizable` | `true` | `false` hides the divider and fixes the split. |
|
|
93
|
+
|
|
94
|
+
Panes clip overflow (`overflow: hidden`) — put a scroll container inside if the content can exceed the pane.
|
|
95
|
+
|
|
96
|
+
## Workbench
|
|
97
|
+
|
|
98
|
+
`CoarPanelLayout` arranges the canonical regions. An **empty slot renders no region** (and no divider), so you take only what you need; `#default` (content) always fills the centre. Sidebars and the bottom panel are resizable and collapsible (`:left-open` …). Toggle the buttons and drag the dividers:
|
|
99
|
+
|
|
100
|
+
**Demo — `panel-layout/demos/PanelLayoutWorkbench.vue`**
|
|
101
|
+
|
|
102
|
+
```vue
|
|
103
|
+
<template>
|
|
104
|
+
<div class="wb-frame">
|
|
105
|
+
<CoarPanelLayout
|
|
106
|
+
v-model:left-width="leftW"
|
|
107
|
+
v-model:right-width="rightW"
|
|
108
|
+
v-model:bottom-height="botH"
|
|
109
|
+
:left-open="leftOpen"
|
|
110
|
+
:right-open="rightOpen"
|
|
111
|
+
:bottom-open="botOpen"
|
|
112
|
+
:left-min="160"
|
|
113
|
+
:left-max="360"
|
|
114
|
+
:right-min="140"
|
|
115
|
+
:right-max="320"
|
|
116
|
+
:bottom-min="60"
|
|
117
|
+
:bottom-max="220"
|
|
118
|
+
>
|
|
119
|
+
<template #top>
|
|
120
|
+
<div class="wb-bar">
|
|
121
|
+
<strong>Workbench</strong>
|
|
122
|
+
<span class="wb-spacer" />
|
|
123
|
+
<button class="wb-btn" :class="{ 'wb-btn--on': leftOpen }" @click="leftOpen = !leftOpen">Left</button>
|
|
124
|
+
<button class="wb-btn" :class="{ 'wb-btn--on': botOpen }" @click="botOpen = !botOpen">Bottom</button>
|
|
125
|
+
<button class="wb-btn" :class="{ 'wb-btn--on': rightOpen }" @click="rightOpen = !rightOpen">Right</button>
|
|
126
|
+
</div>
|
|
127
|
+
</template>
|
|
128
|
+
|
|
129
|
+
<template #left>
|
|
130
|
+
<!-- left sidebar = vertical split: tree (top) + details (bottom) -->
|
|
131
|
+
<CoarSplitPane direction="column" side="second" v-model:size="detailsH" :min="64" :max="220">
|
|
132
|
+
<template #first>
|
|
133
|
+
<section class="wb-pane">
|
|
134
|
+
<header class="wb-title">Explorer</header>
|
|
135
|
+
<div class="wb-body wb-scroll">
|
|
136
|
+
<CoarTree
|
|
137
|
+
:nodes="nodes"
|
|
138
|
+
:get-id="(n) => n.id"
|
|
139
|
+
:get-children="(n) => n.children"
|
|
140
|
+
:get-label="(n) => n.name"
|
|
141
|
+
:is-expandable="(n) => !!n.children"
|
|
142
|
+
v-model:expanded="expanded"
|
|
143
|
+
v-model:selected="selected"
|
|
144
|
+
>
|
|
145
|
+
<template #default="{ node }">
|
|
146
|
+
<CoarTreeNodeLabel :label="node.name" />
|
|
147
|
+
</template>
|
|
148
|
+
</CoarTree>
|
|
149
|
+
</div>
|
|
150
|
+
</section>
|
|
151
|
+
</template>
|
|
152
|
+
<template #second>
|
|
153
|
+
<section class="wb-pane">
|
|
154
|
+
<header class="wb-title">Details</header>
|
|
155
|
+
<div class="wb-body">
|
|
156
|
+
<p class="wb-muted">Selected: <strong>{{ selectedName }}</strong></p>
|
|
157
|
+
</div>
|
|
158
|
+
</section>
|
|
159
|
+
</template>
|
|
160
|
+
</CoarSplitPane>
|
|
161
|
+
</template>
|
|
162
|
+
|
|
163
|
+
<template #default>
|
|
164
|
+
<div class="wb-content">Content / editor area</div>
|
|
165
|
+
</template>
|
|
166
|
+
|
|
167
|
+
<template #right>
|
|
168
|
+
<section class="wb-pane">
|
|
169
|
+
<header class="wb-title">Outline</header>
|
|
170
|
+
<div class="wb-body"><p class="wb-muted">Any panel you like.</p></div>
|
|
171
|
+
</section>
|
|
172
|
+
</template>
|
|
173
|
+
|
|
174
|
+
<template #bottom>
|
|
175
|
+
<section class="wb-pane">
|
|
176
|
+
<header class="wb-title">Problems</header>
|
|
177
|
+
<div class="wb-body"><p class="wb-muted">No problems detected.</p></div>
|
|
178
|
+
</section>
|
|
179
|
+
</template>
|
|
180
|
+
|
|
181
|
+
<template #status>
|
|
182
|
+
<div class="wb-status"><span>Ready</span><span class="wb-spacer" /><span>UTF-8</span></div>
|
|
183
|
+
</template>
|
|
184
|
+
</CoarPanelLayout>
|
|
185
|
+
</div>
|
|
186
|
+
</template>
|
|
187
|
+
|
|
188
|
+
<script setup lang="ts">
|
|
189
|
+
import { computed, ref } from 'vue';
|
|
190
|
+
import { CoarPanelLayout, CoarSplitPane, CoarTree, CoarTreeNodeLabel } from '@cocoar/vue-ui';
|
|
191
|
+
|
|
192
|
+
interface Node {
|
|
193
|
+
id: string;
|
|
194
|
+
name: string;
|
|
195
|
+
children?: Node[];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const nodes: Node[] = [
|
|
199
|
+
{
|
|
200
|
+
id: 'src',
|
|
201
|
+
name: 'src',
|
|
202
|
+
children: [
|
|
203
|
+
{ id: 'app', name: 'App.vue' },
|
|
204
|
+
{ id: 'main', name: 'main.ts' },
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
{ id: 'readme', name: 'README.md' },
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
const expanded = ref(new Set<string>(['src']));
|
|
211
|
+
const selected = ref<string | null>('app');
|
|
212
|
+
const selectedName = computed(() => {
|
|
213
|
+
const find = (list: Node[]): Node | undefined =>
|
|
214
|
+
list.flatMap((n) => [n, ...(n.children ?? [])]).find((n) => n.id === selected.value);
|
|
215
|
+
return find(nodes)?.name ?? 'nothing';
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Layout state — consumer-owned.
|
|
219
|
+
const leftW = ref(220);
|
|
220
|
+
const rightW = ref(180);
|
|
221
|
+
const botH = ref(110);
|
|
222
|
+
const detailsH = ref(96);
|
|
223
|
+
const leftOpen = ref(true);
|
|
224
|
+
const rightOpen = ref(true);
|
|
225
|
+
const botOpen = ref(true);
|
|
226
|
+
</script>
|
|
227
|
+
|
|
228
|
+
<style scoped>
|
|
229
|
+
.wb-frame {
|
|
230
|
+
height: 420px;
|
|
231
|
+
border: 1px solid var(--vp-c-divider);
|
|
232
|
+
border-radius: 8px;
|
|
233
|
+
overflow: hidden;
|
|
234
|
+
font-size: 13px;
|
|
235
|
+
}
|
|
236
|
+
.wb-bar,
|
|
237
|
+
.wb-status {
|
|
238
|
+
display: flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
gap: 8px;
|
|
241
|
+
padding: 0 12px;
|
|
242
|
+
background: var(--vp-c-bg-soft);
|
|
243
|
+
}
|
|
244
|
+
.wb-bar {
|
|
245
|
+
height: 40px;
|
|
246
|
+
border-bottom: 1px solid var(--vp-c-divider);
|
|
247
|
+
}
|
|
248
|
+
.wb-status {
|
|
249
|
+
height: 26px;
|
|
250
|
+
border-top: 1px solid var(--vp-c-divider);
|
|
251
|
+
font-size: 11px;
|
|
252
|
+
color: var(--coar-text-neutral-tertiary, #6b7280);
|
|
253
|
+
}
|
|
254
|
+
.wb-spacer {
|
|
255
|
+
flex: 1;
|
|
256
|
+
}
|
|
257
|
+
.wb-btn {
|
|
258
|
+
height: 24px;
|
|
259
|
+
padding: 0 10px;
|
|
260
|
+
border: 1px solid var(--vp-c-divider);
|
|
261
|
+
border-radius: 4px;
|
|
262
|
+
background: var(--vp-c-bg);
|
|
263
|
+
color: var(--coar-text-neutral-secondary, #475569);
|
|
264
|
+
font-size: 12px;
|
|
265
|
+
cursor: pointer;
|
|
266
|
+
}
|
|
267
|
+
.wb-btn--on {
|
|
268
|
+
background: var(--coar-color-accent, #3b82f6);
|
|
269
|
+
border-color: var(--coar-color-accent, #3b82f6);
|
|
270
|
+
color: #fff;
|
|
271
|
+
}
|
|
272
|
+
.wb-pane {
|
|
273
|
+
display: flex;
|
|
274
|
+
flex-direction: column;
|
|
275
|
+
height: 100%;
|
|
276
|
+
min-height: 0;
|
|
277
|
+
background: var(--vp-c-bg-soft);
|
|
278
|
+
}
|
|
279
|
+
.wb-title {
|
|
280
|
+
flex: 0 0 auto;
|
|
281
|
+
height: 26px;
|
|
282
|
+
display: flex;
|
|
283
|
+
align-items: center;
|
|
284
|
+
padding: 0 12px;
|
|
285
|
+
font-size: 11px;
|
|
286
|
+
font-weight: 700;
|
|
287
|
+
letter-spacing: 0.05em;
|
|
288
|
+
text-transform: uppercase;
|
|
289
|
+
color: var(--coar-text-neutral-tertiary, #6b7280);
|
|
290
|
+
}
|
|
291
|
+
.wb-body {
|
|
292
|
+
flex: 1 1 0;
|
|
293
|
+
min-height: 0;
|
|
294
|
+
padding: 4px 0;
|
|
295
|
+
}
|
|
296
|
+
.wb-scroll {
|
|
297
|
+
overflow: auto;
|
|
298
|
+
}
|
|
299
|
+
.wb-muted {
|
|
300
|
+
margin: 0;
|
|
301
|
+
padding: 4px 12px;
|
|
302
|
+
color: var(--coar-text-neutral-tertiary, #6b7280);
|
|
303
|
+
}
|
|
304
|
+
.wb-content {
|
|
305
|
+
height: 100%;
|
|
306
|
+
display: flex;
|
|
307
|
+
align-items: center;
|
|
308
|
+
justify-content: center;
|
|
309
|
+
background: var(--vp-c-bg);
|
|
310
|
+
color: var(--coar-text-neutral-tertiary, #6b7280);
|
|
311
|
+
}
|
|
312
|
+
</style>
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
```vue
|
|
316
|
+
<CoarPanelLayout v-model:left-width="leftW" :left-min="160" :left-max="360">
|
|
317
|
+
<template #top>…toolbar…</template>
|
|
318
|
+
<template #left>…sidebar…</template>
|
|
319
|
+
<template #default>…content…</template>
|
|
320
|
+
<template #right>…inspector…</template>
|
|
321
|
+
<template #bottom>…panel…</template>
|
|
322
|
+
<template #status>…status bar…</template>
|
|
323
|
+
</CoarPanelLayout>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
┌───────────────────────────────────────┐
|
|
328
|
+
│ #top (fixed, full width) │
|
|
329
|
+
├────────┬────────────────────┬──────────┤
|
|
330
|
+
│ #left │ #default │ #right │
|
|
331
|
+
│ ├────────────────────┤ │
|
|
332
|
+
│ │ #bottom │ │
|
|
333
|
+
├────────┴────────────────────┴──────────┤
|
|
334
|
+
│ #status (fixed, full width) │
|
|
335
|
+
└───────────────────────────────────────┘
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
| Prop | Default | Notes |
|
|
339
|
+
|---|---|---|
|
|
340
|
+
| `leftWidth` / `rightWidth` / `bottomHeight` (`v-model`) | `240` / `280` / `200` | Controlled px sizes. Two-way, so persisting them (localStorage, user prefs) is a one-liner. |
|
|
341
|
+
| `leftOpen` / `rightOpen` / `bottomOpen` | `true` | Collapse a region without unmounting the rest. Render your own toggle button. |
|
|
342
|
+
| `leftMin/Max`, `rightMin/Max`, `bottomMin/Max` | `0` / `∞` | Per-region clamps. |
|
|
343
|
+
| `leftResizable` / `rightResizable` / `bottomResizable` | `true` | `false` fixes that region (no divider). |
|
|
344
|
+
| `contentMinWidth` / `contentMinHeight` | `120` / `80` | Floor the content region keeps — sidebars / the bottom panel can never be dragged (or a shrinking window squeeze them) past it, so **content is never crushed to 0**. Set to `0` to opt out. |
|
|
345
|
+
|
|
346
|
+
The `#bottom` panel sits **under the content, between the sidebars** (VS-Code default). `#top` and `#status` are fixed-height, full-width, and not resizable. Content is **protected by default**: a divider stops before the content region would drop below `contentMinWidth` / `contentMinHeight`, and that floor also holds as the window shrinks (the over-constrained edge clips rather than collapsing the content).
|
|
347
|
+
|
|
348
|
+
## Nesting — a panel within a region
|
|
349
|
+
|
|
350
|
+
Need a split *inside* a region — say a file tree above a details panel in the left sidebar? Nest a `CoarSplitPane`. Same divider, same behaviour:
|
|
351
|
+
|
|
352
|
+
```vue
|
|
353
|
+
<CoarPanelLayout :left-width="leftW">
|
|
354
|
+
<template #left>
|
|
355
|
+
<CoarSplitPane direction="column" side="second" v-model:size="detailsHeight" :min="80">
|
|
356
|
+
<template #first><CoarTree … /></template> <!-- tree fills -->
|
|
357
|
+
<template #second>…details panel…</template> <!-- sized, draggable -->
|
|
358
|
+
</CoarSplitPane>
|
|
359
|
+
</template>
|
|
360
|
+
<template #default>…editor…</template>
|
|
361
|
+
</CoarPanelLayout>
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
This pairs naturally with the [file explorer](./file-explorer/index.md)'s `selectedAsset` + `describeAsset` for a tree-over-details sidebar.
|
|
365
|
+
|
|
366
|
+
## Accessibility
|
|
367
|
+
|
|
368
|
+
Each divider is a `role="separator"` with `aria-orientation`, `aria-valuenow/valuemin/valuemax`, and an `aria-label`. It's in the tab order — focus it and use **Arrow keys** to resize, **Home/End** to jump to min/max. Clicking a divider focuses it too.
|
|
369
|
+
|
|
370
|
+
## Scope
|
|
371
|
+
|
|
372
|
+
Resize and collapse are in. Drag-to-rearrange panels, tab docking, and floating panels are intentionally **out** — compose those on top if you need them.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
<!-- Generated from apps/docs/components/password-input.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
|
|
2
|
+
|
|
3
|
+
# Password Input
|
|
4
|
+
|
|
5
|
+
A text input tailored for passwords. It masks characters by default and provides a built-in eye toggle so users can temporarily reveal what they typed -- reducing frustration without sacrificing security.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { CoarPasswordInput } from '@cocoar/vue-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Basic Usage
|
|
12
|
+
|
|
13
|
+
Works just like a text input, but the value is masked. Click the eye icon to peek at the password.
|
|
14
|
+
|
|
15
|
+
**Demo — `password-input/demos/BasicPasswordInput.vue`**
|
|
16
|
+
|
|
17
|
+
```vue
|
|
18
|
+
<template>
|
|
19
|
+
<div style="display: flex; flex-direction: column; gap: 12px; max-width: 320px;">
|
|
20
|
+
<CoarFormField label="Password">
|
|
21
|
+
<CoarPasswordInput
|
|
22
|
+
v-model="password"
|
|
23
|
+
placeholder="Enter your password"
|
|
24
|
+
/>
|
|
25
|
+
</CoarFormField>
|
|
26
|
+
<span style="font-size: 13px; color: #64748b;">Length: {{ password.length }} characters</span>
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<script setup lang="ts">
|
|
31
|
+
import { ref } from 'vue';
|
|
32
|
+
import { CoarPasswordInput, CoarFormField } from '@cocoar/vue-ui';
|
|
33
|
+
|
|
34
|
+
const password = ref('');
|
|
35
|
+
</script>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## With Validation
|
|
39
|
+
|
|
40
|
+
Wrap in `CoarFormField` to add labels, hints, and error messages. The component reads the error state from the field context automatically.
|
|
41
|
+
|
|
42
|
+
**Demo — `password-input/demos/PasswordInputValidation.vue`**
|
|
43
|
+
|
|
44
|
+
```vue
|
|
45
|
+
<template>
|
|
46
|
+
<div style="display: flex; flex-direction: column; gap: 12px; max-width: 320px;">
|
|
47
|
+
<CoarFormField
|
|
48
|
+
label="New Password"
|
|
49
|
+
hint="Use a mix of letters, numbers, and symbols"
|
|
50
|
+
:error="password.length > 0 && password.length < 8 ? 'Password must be at least 8 characters' : ''"
|
|
51
|
+
required
|
|
52
|
+
>
|
|
53
|
+
<CoarPasswordInput
|
|
54
|
+
v-model="password"
|
|
55
|
+
placeholder="Min 8 characters"
|
|
56
|
+
:required="true"
|
|
57
|
+
/>
|
|
58
|
+
</CoarFormField>
|
|
59
|
+
<CoarFormField
|
|
60
|
+
label="Confirm Password"
|
|
61
|
+
:error="confirm.length > 0 && confirm !== password ? 'Passwords do not match' : ''"
|
|
62
|
+
>
|
|
63
|
+
<CoarPasswordInput
|
|
64
|
+
v-model="confirm"
|
|
65
|
+
placeholder="Repeat your password"
|
|
66
|
+
/>
|
|
67
|
+
</CoarFormField>
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<script setup lang="ts">
|
|
72
|
+
import { ref } from 'vue';
|
|
73
|
+
import { CoarPasswordInput, CoarFormField } from '@cocoar/vue-ui';
|
|
74
|
+
|
|
75
|
+
const password = ref('');
|
|
76
|
+
const confirm = ref('');
|
|
77
|
+
</script>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## States
|
|
81
|
+
|
|
82
|
+
Use `disabled` for locked accounts or `readonly` when displaying a masked placeholder that the user cannot modify.
|
|
83
|
+
|
|
84
|
+
**Demo — `password-input/demos/PasswordInputStates.vue`**
|
|
85
|
+
|
|
86
|
+
```vue
|
|
87
|
+
<template>
|
|
88
|
+
<div style="display: flex; flex-direction: column; gap: 12px; max-width: 320px;">
|
|
89
|
+
<CoarFormField label="Disabled">
|
|
90
|
+
<CoarPasswordInput :model-value="'secret123'" :disabled="true" />
|
|
91
|
+
</CoarFormField>
|
|
92
|
+
<CoarFormField label="Readonly">
|
|
93
|
+
<CoarPasswordInput :model-value="'readonly-pass'" :readonly="true" />
|
|
94
|
+
</CoarFormField>
|
|
95
|
+
</div>
|
|
96
|
+
</template>
|
|
97
|
+
|
|
98
|
+
<script setup lang="ts">
|
|
99
|
+
import { CoarPasswordInput, CoarFormField } from '@cocoar/vue-ui';
|
|
100
|
+
</script>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Sizes
|
|
104
|
+
|
|
105
|
+
Four sizes to keep password fields visually consistent with the rest of your form.
|
|
106
|
+
|
|
107
|
+
**Demo — `password-input/demos/PasswordInputSizes.vue`**
|
|
108
|
+
|
|
109
|
+
```vue
|
|
110
|
+
<template>
|
|
111
|
+
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px;">
|
|
112
|
+
<CoarFormField label="Extra Small">
|
|
113
|
+
<CoarPasswordInput size="xs" placeholder="xs" />
|
|
114
|
+
</CoarFormField>
|
|
115
|
+
<CoarFormField label="Small">
|
|
116
|
+
<CoarPasswordInput size="s" placeholder="s" />
|
|
117
|
+
</CoarFormField>
|
|
118
|
+
<CoarFormField label="Medium">
|
|
119
|
+
<CoarPasswordInput size="m" placeholder="m" />
|
|
120
|
+
</CoarFormField>
|
|
121
|
+
<CoarFormField label="Large">
|
|
122
|
+
<CoarPasswordInput size="l" placeholder="l" />
|
|
123
|
+
</CoarFormField>
|
|
124
|
+
</div>
|
|
125
|
+
</template>
|
|
126
|
+
|
|
127
|
+
<script setup lang="ts">
|
|
128
|
+
import { CoarPasswordInput, CoarFormField } from '@cocoar/vue-ui';
|
|
129
|
+
</script>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Accessibility
|
|
133
|
+
|
|
134
|
+
### Keyboard Navigation
|
|
135
|
+
|
|
136
|
+
| Key | Action |
|
|
137
|
+
|-----|--------|
|
|
138
|
+
| `Tab` | Move focus to input / toggle button |
|
|
139
|
+
| `Enter` / `Space` | Toggle password visibility (on eye icon) |
|
|
140
|
+
|
|
141
|
+
> **Info**
|
|
142
|
+
>
|
|
143
|
+
> The visibility toggle button has an accessible label that updates based on the current state ("Show password" / "Hide password").
|
|
144
|
+
|
|
145
|
+
### Screen Reader Support
|
|
146
|
+
|
|
147
|
+
- Label text announces on focus
|
|
148
|
+
- Password visibility toggle state is communicated
|
|
149
|
+
- Error messages linked via `aria-describedby`
|
|
150
|
+
- Input type switches between `password` and `text`
|
|
151
|
+
|
|
152
|
+
## API
|
|
153
|
+
|
|
154
|
+
### Props
|
|
155
|
+
|
|
156
|
+
| Prop | Type | Default | Description |
|
|
157
|
+
|------|------|---------|-------------|
|
|
158
|
+
| `v-model` | `string` | `''` | Password value |
|
|
159
|
+
| `placeholder` | `string` | `''` | Placeholder text |
|
|
160
|
+
| `error` | `boolean` | `false` | Error state (auto-injected from `CoarFormField`) |
|
|
161
|
+
| `size` | `'xs' \| 's' \| 'm' \| 'l'` | `'m'` | Input size |
|
|
162
|
+
| `disabled` | `boolean` | `false` | Disable the input |
|
|
163
|
+
| `readonly` | `boolean` | `false` | Make read-only |
|
|
164
|
+
| `required` | `boolean` | `false` | Mark as required |
|
|
165
|
+
| `clearable` | `boolean` | `false` | Show clear button when input has value |
|
|
166
|
+
|
|
167
|
+
## i18n Keys
|
|
168
|
+
|
|
169
|
+
These keys can be translated via [`@cocoar/vue-localization`](../foundations/localization/translations.md).
|
|
170
|
+
|
|
171
|
+
| Key | Default (English) | Used as |
|
|
172
|
+
|-----|-------------------|---------|
|
|
173
|
+
| `coar.ui.passwordInput.showPassword` | `'Show password'` | Toggle visibility button `aria-label` |
|
|
174
|
+
| `coar.ui.passwordInput.hidePassword` | `'Hide password'` | Toggle visibility button `aria-label` (when visible) |
|
|
175
|
+
| `coar.ui.passwordInput.clear` | `'Clear'` | Clear button `aria-label` |
|