@fastkit/vui 0.19.56 → 0.20.1

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 (3) hide show
  1. package/README.md +932 -1
  2. package/dist/vui.d.ts +45 -0
  3. package/package.json +18 -18
package/README.md CHANGED
@@ -1,4 +1,935 @@
1
+
1
2
  # @fastkit/vui
2
3
 
4
+ 🌐 English | [日本語](https://github.com/dadajam4/fastkit/blob/main/packages/vui/README-ja.md)
5
+
6
+ A simple and extensible UI component library for Vue.js 3 applications. Focused on full TypeScript support, color theme system, accessibility, and tight integration with @fastkit/vue-form-control.
7
+
8
+ ## Features
9
+
10
+ - **45+ UI Components**: Comprehensive component set including buttons, forms, navigation, data display
11
+ - **Integrated Ecosystem**: 20+ @fastkit packages with unified API
12
+ - **Full TypeScript Support**: Type-safe props, events, and slot definitions
13
+ - **Composition API Design**: Full adoption of Vue 3 modern patterns
14
+ - **Color Theme System**: Flexible theming with CSS Variables foundation
15
+ - **Form Integration**: Seamless integration with @fastkit/vue-form-control
16
+ - **Accessibility**: WAI-ARIA compliant, keyboard navigation support
17
+ - **Responsive Design**: Mobile-first grid system
18
+ - **Internationalization**: Multi-language font settings, RTL language support
19
+ - **Programmatic UI**: Imperative operations for dialogs, notifications, etc.
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @fastkit/vui
25
+ # or
26
+ pnpm add @fastkit/vui
27
+ ```
28
+
29
+ ## Basic Usage
30
+
31
+ ### Plugin Setup
32
+
33
+ ```typescript
34
+ import { createApp } from 'vue';
35
+ import { VuiPlugin } from '@fastkit/vui';
36
+ import { createRouter } from 'vue-router';
37
+
38
+ const app = createApp(App);
39
+ const router = createRouter(/* routes */);
40
+
41
+ // Install VUI plugin
42
+ app.use(VuiPlugin, {
43
+ router,
44
+ colorScheme: {
45
+ // Color theme settings
46
+ primary: '#1976d2',
47
+ secondary: '#424242'
48
+ },
49
+ uiSettings: {
50
+ primaryScope: 'primary',
51
+ buttonDefault: {
52
+ color: 'primary',
53
+ variant: 'contained'
54
+ }
55
+ }
56
+ });
57
+ ```
58
+
59
+ ### Application Setup
60
+
61
+ ```vue
62
+ <template>
63
+ <VApp>
64
+ <!-- Application content -->
65
+ <router-view />
66
+ </VApp>
67
+ </template>
68
+
69
+ <script setup lang="ts">
70
+ import { VApp } from '@fastkit/vui';
71
+ </script>
72
+ ```
73
+
74
+ ## Component List
75
+
76
+ ### Layout & Structure
77
+
78
+ - **VApp** - Application root container
79
+ - **VGrid** (`VGridContainer`, `VGridItem`) - Responsive grid system
80
+ - **VPaper** - Material Design-style paper container
81
+ - **VCard** (`VCardContent`, `VCardActions`) - Card layout
82
+ - **VToolbar** (`VToolbarTitle`, `VToolbarMenu`, `VToolbarEdge`) - Toolbar
83
+
84
+ ### Navigation
85
+
86
+ - **VNavigation**, **VNavigationItem** - Side navigation
87
+ - **VBreadcrumbs** - Breadcrumb navigation
88
+ - **VTabs**, **VTab** - Tab interface
89
+ - **VPagination** - Pagination
90
+
91
+ ### Form Controls
92
+
93
+ - **VButton**, **VButtonGroup** - Button and button group
94
+ - **VTextField** - Text input field
95
+ - **VTextarea** - Multi-line text input
96
+ - **VNumberField** - Number input field
97
+ - **VSelect** - Select dropdown
98
+ - **VCheckbox**, **VCheckboxGroup** - Checkbox
99
+ - **VRadio**, **VRadioGroup** - Radio button
100
+ - **VSwitch**, **VSwitchGroup** - Switch toggle
101
+ - **VOption**, **VOptionGroup** - Option element
102
+
103
+ ### Data Display
104
+
105
+ - **VDataTable** - Data table
106
+ - **VListTile** - List item
107
+ - **VAvatar** - User avatar
108
+ - **VChip** - Chip/tag element
109
+ - **VIcon** - Icon display
110
+
111
+ ### Feedback & Interaction
112
+
113
+ - **VDialog** - Modal dialog
114
+ - **VSnackbar** - Notification snackbar
115
+ - **VTooltip** - Tooltip
116
+ - **VMenu** - Context menu
117
+ - **VSheetModal** - Sheet modal
118
+
119
+ ### Others
120
+
121
+ - **VSkeltonLoader** - Skeleton loading
122
+ - **VBusyImage** - Lazy loading image
123
+ - **VHero** - Hero section
124
+ - **VContentSwitcher** - Content switcher
125
+
126
+ ## Usage Examples
127
+
128
+ ### Button Components
129
+
130
+ ```vue
131
+ <template>
132
+ <div>
133
+ <!-- Basic button -->
134
+ <VButton @click="handleClick">Click</VButton>
135
+
136
+ <!-- Primary button -->
137
+ <VButton color="primary" variant="contained">
138
+ Save
139
+ </VButton>
140
+
141
+ <!-- Button with icon -->
142
+ <VButton
143
+ startIcon="mdi-search"
144
+ color="primary"
145
+ variant="outlined"
146
+ :loading="searching"
147
+ @click="handleSearch"
148
+ >
149
+ Search
150
+ </VButton>
151
+
152
+ <!-- Button group -->
153
+ <VButtonGroup>
154
+ <VButton>Left</VButton>
155
+ <VButton>Center</VButton>
156
+ <VButton>Right</VButton>
157
+ </VButtonGroup>
158
+ </div>
159
+ </template>
160
+
161
+ <script setup lang="ts">
162
+ import { ref } from 'vue';
163
+ import { VButton, VButtonGroup } from '@fastkit/vui';
164
+
165
+ const searching = ref(false);
166
+
167
+ const handleClick = () => {
168
+ console.log('Button clicked');
169
+ };
170
+
171
+ const handleSearch = async () => {
172
+ searching.value = true;
173
+ try {
174
+ // Search processing
175
+ await performSearch();
176
+ } finally {
177
+ searching.value = false;
178
+ }
179
+ };
180
+ </script>
181
+ ```
182
+
183
+ ### Form Components
184
+
185
+ ```vue
186
+ <template>
187
+ <VCard>
188
+ <VCardContent>
189
+ <h2>User Registration</h2>
190
+
191
+ <!-- Text input -->
192
+ <VTextField
193
+ v-model="form.name.value"
194
+ label="Name"
195
+ required
196
+ :rules="[required(), minLength(2)]"
197
+ :invalid="form.name.invalid"
198
+ :error-message="form.name.errorMessage"
199
+ />
200
+
201
+ <!-- Email input -->
202
+ <VTextField
203
+ v-model="form.email.value"
204
+ label="Email Address"
205
+ type="email"
206
+ required
207
+ :rules="[required(), email()]"
208
+ :invalid="form.email.invalid"
209
+ :error-message="form.email.errorMessage"
210
+ />
211
+
212
+ <!-- Select -->
213
+ <VSelect
214
+ v-model="form.category.value"
215
+ label="Category"
216
+ :items="categories"
217
+ placeholder="Select category"
218
+ required
219
+ />
220
+
221
+ <!-- Checkbox -->
222
+ <VCheckbox
223
+ v-model="form.agreement.value"
224
+ :rules="[required()]"
225
+ >
226
+ I agree to the terms of use
227
+ </VCheckbox>
228
+ </VCardContent>
229
+
230
+ <VCardActions>
231
+ <VButton
232
+ color="primary"
233
+ variant="contained"
234
+ :disabled="form.invalid"
235
+ @click="handleSubmit"
236
+ >
237
+ Register
238
+ </VButton>
239
+ <VButton variant="text" @click="handleCancel">
240
+ Cancel
241
+ </VButton>
242
+ </VCardActions>
243
+ </VCard>
244
+ </template>
245
+
246
+ <script setup lang="ts">
247
+ import {
248
+ VCard, VCardContent, VCardActions,
249
+ VTextField, VSelect, VCheckbox, VButton
250
+ } from '@fastkit/vui';
251
+ import { useForm } from '@fastkit/vue-form-control';
252
+ import { required, email, minLength } from '@fastkit/rules';
253
+
254
+ const categories = [
255
+ { value: 'personal', label: 'Personal' },
256
+ { value: 'business', label: 'Business' },
257
+ { value: 'education', label: 'Education' }
258
+ ];
259
+
260
+ const form = useForm({
261
+ name: {
262
+ value: '',
263
+ rules: [required(), minLength(2)]
264
+ },
265
+ email: {
266
+ value: '',
267
+ rules: [required(), email()]
268
+ },
269
+ category: {
270
+ value: '',
271
+ rules: [required()]
272
+ },
273
+ agreement: {
274
+ value: false,
275
+ rules: [required()]
276
+ }
277
+ }, {
278
+ onSubmit: async (values) => {
279
+ console.log('Form submitted:', values);
280
+ await api.register(values);
281
+ }
282
+ });
283
+
284
+ const handleSubmit = () => {
285
+ form.submit();
286
+ };
287
+
288
+ const handleCancel = () => {
289
+ form.reset();
290
+ };
291
+ </script>
292
+ ```
293
+
294
+ ### Data Table
295
+
296
+ ```vue
297
+ <template>
298
+ <VDataTable
299
+ :items="users"
300
+ :headers="headers"
301
+ :loading="loading"
302
+ item-key="id"
303
+ selectable
304
+ @select="handleSelect"
305
+ >
306
+ <!-- Custom column -->
307
+ <template #item.status="{ item }">
308
+ <VChip
309
+ :color="item.status === 'active' ? 'success' : 'warning'"
310
+ size="sm"
311
+ >
312
+ {{ item.status === 'active' ? 'Active' : 'Inactive' }}
313
+ </VChip>
314
+ </template>
315
+
316
+ <template #item.actions="{ item }">
317
+ <VButton size="sm" variant="text" @click="editUser(item)">
318
+ Edit
319
+ </VButton>
320
+ <VButton
321
+ size="sm"
322
+ variant="text"
323
+ color="error"
324
+ @click="deleteUser(item)"
325
+ >
326
+ Delete
327
+ </VButton>
328
+ </template>
329
+ </VDataTable>
330
+ </template>
331
+
332
+ <script setup lang="ts">
333
+ import { ref } from 'vue';
334
+ import { VDataTable, VChip, VButton } from '@fastkit/vui';
335
+
336
+ const loading = ref(false);
337
+ const users = ref([
338
+ { id: 1, name: 'John Tanaka', email: 'tanaka@example.com', status: 'active' },
339
+ { id: 2, name: 'Hanako Sato', email: 'sato@example.com', status: 'inactive' }
340
+ ]);
341
+
342
+ const headers = [
343
+ { key: 'name', title: 'Name', sortable: true },
344
+ { key: 'email', title: 'Email', sortable: true },
345
+ { key: 'status', title: 'Status' },
346
+ { key: 'actions', title: 'Actions', width: 120 }
347
+ ];
348
+
349
+ const handleSelect = (selectedItems: any[]) => {
350
+ console.log('Selected items:', selectedItems);
351
+ };
352
+
353
+ const editUser = (user: any) => {
354
+ console.log('Edit:', user);
355
+ };
356
+
357
+ const deleteUser = (user: any) => {
358
+ console.log('Delete:', user);
359
+ };
360
+ </script>
361
+ ```
362
+
363
+ ### Layout System
364
+
365
+ ```vue
366
+ <template>
367
+ <VApp>
368
+ <!-- Toolbar -->
369
+ <VToolbar color="primary" variant="flat">
370
+ <VToolbarTitle>My App</VToolbarTitle>
371
+ <VToolbarMenu>
372
+ <VButton variant="text" color="on-primary">
373
+ Menu
374
+ </VButton>
375
+ </VToolbarMenu>
376
+ </VToolbar>
377
+
378
+ <!-- Main content -->
379
+ <VGridContainer>
380
+ <VGridItem cols="12" md="3">
381
+ <!-- Side navigation -->
382
+ <VNavigation>
383
+ <VNavigationItem to="/dashboard" icon="mdi-dashboard">
384
+ Dashboard
385
+ </VNavigationItem>
386
+ <VNavigationItem to="/users" icon="mdi-account-group">
387
+ Users
388
+ </VNavigationItem>
389
+ <VNavigationItem to="/settings" icon="mdi-cog">
390
+ Settings
391
+ </VNavigationItem>
392
+ </VNavigation>
393
+ </VGridItem>
394
+
395
+ <VGridItem cols="12" md="9">
396
+ <!-- Page content -->
397
+ <VPaper class="pa-4">
398
+ <router-view />
399
+ </VPaper>
400
+ </VGridItem>
401
+ </VGridContainer>
402
+ </VApp>
403
+ </template>
404
+
405
+ <script setup lang="ts">
406
+ import {
407
+ VApp, VToolbar, VToolbarTitle, VToolbarMenu,
408
+ VGridContainer, VGridItem, VNavigation, VNavigationItem,
409
+ VPaper, VButton
410
+ } from '@fastkit/vui';
411
+ </script>
412
+ ```
413
+
414
+ ## Programmatic UI Operations
415
+
416
+ You can control UI elements from JavaScript using VUI services.
417
+
418
+ ```typescript
419
+ import { useVui } from '@fastkit/vui';
420
+
421
+ const vui = useVui();
422
+
423
+ // Alert dialog
424
+ await vui.alert('Processing completed');
425
+
426
+ // Confirmation dialog
427
+ const confirmed = await vui.confirm({
428
+ title: 'Confirmation',
429
+ message: 'Do you want to execute this operation?',
430
+ okText: 'Execute',
431
+ cancelText: 'Cancel'
432
+ });
433
+
434
+ if (confirmed) {
435
+ // Process when confirmed
436
+ }
437
+
438
+ // Prompt dialog
439
+ const result = await vui.prompt({
440
+ title: 'Enter name',
441
+ message: 'Please enter a new name',
442
+ defaultValue: 'Default value'
443
+ });
444
+
445
+ // Form prompt
446
+ const formResult = await vui.formPrompt(
447
+ {
448
+ state: { name: '', description: '' },
449
+ title: 'Create New'
450
+ },
451
+ (state) => (
452
+ <>
453
+ <VTextField
454
+ label="Name"
455
+ v-model={state.name}
456
+ required
457
+ />
458
+ <VTextarea
459
+ label="Description"
460
+ v-model={state.description}
461
+ rows={3}
462
+ />
463
+ </>
464
+ )
465
+ );
466
+
467
+ // Snackbar notification
468
+ vui.snackbar.show({
469
+ message: 'Saved',
470
+ color: 'success',
471
+ timeout: 3000
472
+ });
473
+
474
+ // Custom dialog
475
+ const dialog = await vui.dialog.show({
476
+ component: MyCustomDialog,
477
+ props: {
478
+ data: someData
479
+ }
480
+ });
481
+ ```
482
+
483
+ ## Theme Customization
484
+
485
+ ### CSS Variables
486
+
487
+ ```css
488
+ :root {
489
+ /* Color palette */
490
+ --color-primary: #1976d2;
491
+ --color-secondary: #424242;
492
+ --color-success: #4caf50;
493
+ --color-warning: #ff9800;
494
+ --color-error: #f44336;
495
+
496
+ /* Size settings */
497
+ --control-field-rem-sm: 0.875rem;
498
+ --control-field-rem-md: 1rem;
499
+ --control-field-rem-lg: 1.125rem;
500
+
501
+ /* Spacing */
502
+ --root-spacing: 8px;
503
+
504
+ /* Font */
505
+ --typo-base-font: Roboto, 'Noto Sans JP', sans-serif;
506
+
507
+ /* Shadow */
508
+ --shadow-1: 0px 2px 1px -1px rgba(0, 0, 0, 0.2);
509
+ --shadow-4: 0px 2px 4px -1px rgba(0, 0, 0, 0.2);
510
+
511
+ /* Transition */
512
+ --transition-primary: cubic-bezier(0.25, 0.8, 0.5, 1);
513
+ }
514
+ ```
515
+
516
+ ### Custom Color Scheme
517
+
518
+ ```typescript
519
+ // Define custom colors
520
+ app.use(VuiPlugin, {
521
+ colorScheme: {
522
+ primary: {
523
+ main: '#1976d2',
524
+ light: '#42a5f5',
525
+ dark: '#1565c0',
526
+ contrast: '#ffffff'
527
+ },
528
+ secondary: {
529
+ main: '#dc004e',
530
+ light: '#ff5983',
531
+ dark: '#9a0036',
532
+ contrast: '#ffffff'
533
+ }
534
+ }
535
+ });
536
+ ```
537
+
538
+ ### Dark Theme Support
539
+
540
+ ```vue
541
+ <template>
542
+ <VApp :theme="currentTheme">
543
+ <VButton @click="toggleTheme">
544
+ {{ currentTheme === 'dark' ? 'Light' : 'Dark' }} Theme
545
+ </VButton>
546
+ <!-- App content -->
547
+ </VApp>
548
+ </template>
549
+
550
+ <script setup lang="ts">
551
+ import { ref } from 'vue';
552
+ import { useColorScheme } from '@fastkit/vui';
553
+
554
+ const { theme: currentTheme, toggle: toggleTheme } = useColorScheme();
555
+ </script>
556
+ ```
557
+
558
+ ## Advanced Usage Examples
559
+
560
+ ### Custom Component Creation
561
+
562
+ ```vue
563
+ <template>
564
+ <VCard class="user-profile">
565
+ <VCardContent>
566
+ <div class="user-profile__header">
567
+ <VAvatar :src="user.avatar" size="lg" />
568
+ <div class="user-profile__info">
569
+ <h3>{{ user.name }}</h3>
570
+ <p>{{ user.role }}</p>
571
+ </div>
572
+ </div>
573
+
574
+ <VTabs v-model="activeTab">
575
+ <VTab value="profile">Profile</VTab>
576
+ <VTab value="settings">Settings</VTab>
577
+ <VTab value="activity">Activity</VTab>
578
+ </VTabs>
579
+
580
+ <VContentSwitcher :value="activeTab">
581
+ <template #profile>
582
+ <UserProfileTab :user="user" />
583
+ </template>
584
+ <template #settings>
585
+ <UserSettingsTab :user="user" />
586
+ </template>
587
+ <template #activity>
588
+ <UserActivityTab :user="user" />
589
+ </template>
590
+ </VContentSwitcher>
591
+ </VCardContent>
592
+ </VCard>
593
+ </template>
594
+
595
+ <script setup lang="ts">
596
+ import { ref } from 'vue';
597
+ import {
598
+ VCard, VCardContent, VAvatar, VTabs, VTab, VContentSwitcher
599
+ } from '@fastkit/vui';
600
+
601
+ interface User {
602
+ name: string;
603
+ role: string;
604
+ avatar: string;
605
+ }
606
+
607
+ const props = defineProps<{
608
+ user: User;
609
+ }>();
610
+
611
+ const activeTab = ref('profile');
612
+ </script>
613
+
614
+ <style scoped>
615
+ .user-profile__header {
616
+ display: flex;
617
+ align-items: center;
618
+ gap: 1rem;
619
+ margin-bottom: 1.5rem;
620
+ }
621
+
622
+ .user-profile__info h3 {
623
+ margin: 0;
624
+ font-size: 1.25rem;
625
+ font-weight: 500;
626
+ }
627
+
628
+ .user-profile__info p {
629
+ margin: 0.25rem 0 0;
630
+ color: var(--color-text-secondary);
631
+ }
632
+ </style>
633
+ ```
634
+
635
+ ### Complex Form Wizard
636
+
637
+ ```vue
638
+ <template>
639
+ <VCard class="form-wizard">
640
+ <VCardContent>
641
+ <VToolbar variant="flat" class="mb-4">
642
+ <VToolbarTitle>Registration Wizard</VToolbarTitle>
643
+ <VToolbarEdge>
644
+ Step {{ currentStep + 1 }} / {{ steps.length }}
645
+ </VToolbarEdge>
646
+ </VToolbar>
647
+
648
+ <!-- Step indicator -->
649
+ <div class="step-indicator">
650
+ <div
651
+ v-for="(step, index) in steps"
652
+ :key="step.name"
653
+ class="step-indicator__item"
654
+ :class="{
655
+ 'step-indicator__item--active': index === currentStep,
656
+ 'step-indicator__item--completed': index < currentStep
657
+ }"
658
+ >
659
+ <VIcon
660
+ :name="index < currentStep ? 'mdi-check' : step.icon"
661
+ size="sm"
662
+ />
663
+ <span>{{ step.title }}</span>
664
+ </div>
665
+ </div>
666
+
667
+ <!-- Step content -->
668
+ <VContentSwitcher :value="currentStep">
669
+ <template #0>
670
+ <PersonalInfoStep v-model="formData.personal" />
671
+ </template>
672
+ <template #1>
673
+ <ContactInfoStep v-model="formData.contact" />
674
+ </template>
675
+ <template #2>
676
+ <PreferencesStep v-model="formData.preferences" />
677
+ </template>
678
+ <template #3>
679
+ <ConfirmationStep :data="formData" />
680
+ </template>
681
+ </VContentSwitcher>
682
+ </VCardContent>
683
+
684
+ <VCardActions>
685
+ <VButton
686
+ variant="text"
687
+ :disabled="currentStep === 0"
688
+ @click="previousStep"
689
+ >
690
+ Back
691
+ </VButton>
692
+
693
+ <div class="flex-grow" />
694
+
695
+ <VButton
696
+ v-if="currentStep < steps.length - 1"
697
+ color="primary"
698
+ variant="contained"
699
+ :disabled="!canProceed"
700
+ @click="nextStep"
701
+ >
702
+ Next
703
+ </VButton>
704
+
705
+ <VButton
706
+ v-else
707
+ color="primary"
708
+ variant="contained"
709
+ :loading="submitting"
710
+ @click="submitForm"
711
+ >
712
+ Complete
713
+ </VButton>
714
+ </VCardActions>
715
+ </VCard>
716
+ </template>
717
+
718
+ <script setup lang="ts">
719
+ import { ref, computed } from 'vue';
720
+ import {
721
+ VCard, VCardContent, VCardActions,
722
+ VToolbar, VToolbarTitle, VToolbarEdge,
723
+ VContentSwitcher, VButton, VIcon
724
+ } from '@fastkit/vui';
725
+
726
+ const currentStep = ref(0);
727
+ const submitting = ref(false);
728
+
729
+ const steps = [
730
+ { name: 'personal', title: 'Personal Info', icon: 'mdi-account' },
731
+ { name: 'contact', title: 'Contact Info', icon: 'mdi-email' },
732
+ { name: 'preferences', title: 'Preferences', icon: 'mdi-cog' },
733
+ { name: 'confirm', title: 'Confirmation', icon: 'mdi-check-circle' }
734
+ ];
735
+
736
+ const formData = ref({
737
+ personal: { name: '', birthday: '' },
738
+ contact: { email: '', phone: '' },
739
+ preferences: { newsletter: false, theme: 'light' }
740
+ });
741
+
742
+ const canProceed = computed(() => {
743
+ // Validation for each step
744
+ switch (currentStep.value) {
745
+ case 0:
746
+ return formData.value.personal.name && formData.value.personal.birthday;
747
+ case 1:
748
+ return formData.value.contact.email;
749
+ case 2:
750
+ return true;
751
+ default:
752
+ return false;
753
+ }
754
+ });
755
+
756
+ const nextStep = () => {
757
+ if (currentStep.value < steps.length - 1) {
758
+ currentStep.value++;
759
+ }
760
+ };
761
+
762
+ const previousStep = () => {
763
+ if (currentStep.value > 0) {
764
+ currentStep.value--;
765
+ }
766
+ };
767
+
768
+ const submitForm = async () => {
769
+ submitting.value = true;
770
+ try {
771
+ await api.submitRegistration(formData.value);
772
+ // Success processing
773
+ } finally {
774
+ submitting.value = false;
775
+ }
776
+ };
777
+ </script>
778
+
779
+ <style scoped>
780
+ .step-indicator {
781
+ display: flex;
782
+ justify-content: space-between;
783
+ margin-bottom: 2rem;
784
+ }
785
+
786
+ .step-indicator__item {
787
+ display: flex;
788
+ flex-direction: column;
789
+ align-items: center;
790
+ gap: 0.5rem;
791
+ opacity: 0.6;
792
+ transition: opacity 0.2s;
793
+ }
794
+
795
+ .step-indicator__item--active,
796
+ .step-indicator__item--completed {
797
+ opacity: 1;
798
+ }
799
+
800
+ .flex-grow {
801
+ flex-grow: 1;
802
+ }
803
+ </style>
804
+ ```
805
+
806
+ ## API
807
+
808
+ ### Composables
809
+
810
+ #### useVui()
811
+
812
+ Access VUI service instance.
813
+
814
+ ```typescript
815
+ const vui = useVui();
816
+
817
+ // Dialog operations
818
+ vui.alert(message)
819
+ vui.confirm(options)
820
+ vui.prompt(options)
821
+ vui.formPrompt(state, renderer)
822
+
823
+ // Notifications
824
+ vui.snackbar.show(options)
825
+ vui.snackbar.hide()
826
+
827
+ // Navigation
828
+ vui.router.push(location)
829
+ vui.location.assign(url)
830
+ ```
831
+
832
+ #### useControl()
833
+
834
+ Provides common functionality for control elements.
835
+
836
+ ```typescript
837
+ const control = useControl(props, options);
838
+
839
+ // Properties
840
+ control.size // 'sm' | 'md' | 'lg'
841
+ control.classes // Computed CSS classes
842
+ control.isDisabled // Disabled state
843
+ ```
844
+
845
+ #### useColorScheme()
846
+
847
+ Provides color theme control.
848
+
849
+ ```typescript
850
+ const { theme, toggle, setTheme } = useColorScheme();
851
+
852
+ theme.value // Current theme
853
+ toggle() // Toggle theme
854
+ setTheme('dark') // Set specific theme
855
+ ```
856
+
857
+ ### Plugin Options
858
+
859
+ ```typescript
860
+ interface VuiPluginOptions {
861
+ router: Router;
862
+ colorScheme?: VueColorSchemePluginSettings;
863
+ uiSettings?: {
864
+ primaryScope: ScopeName;
865
+ buttonDefault: {
866
+ color: ScopeName;
867
+ variant: ColorVariant;
868
+ };
869
+ dialogOk?: {
870
+ color?: ScopeName;
871
+ variant?: ColorVariant;
872
+ };
873
+ };
874
+ icons?: {
875
+ menuDown: IconName;
876
+ navigationExpand: RawIconProp;
877
+ // Other icon settings
878
+ };
879
+ stack?: VueStackPluginOptions;
880
+ form?: VueFormServiceOptions;
881
+ }
882
+ ```
883
+
884
+ ## Accessibility
885
+
886
+ ### Keyboard Navigation
887
+
888
+ - **Tab/Shift+Tab**: Focus movement
889
+ - **Enter/Space**: Button/checkbox operation
890
+ - **Arrow Keys**: Radio button/tab/menu navigation
891
+ - **Escape**: Close dialog/menu
892
+ - **Home/End**: Move to beginning/end of list/table
893
+
894
+ ### ARIA Support
895
+
896
+ ```html
897
+ <!-- Automatically applied ARIA attributes -->
898
+ <button aria-disabled="true" aria-label="Save button">
899
+ <input aria-invalid="true" aria-describedby="error-message">
900
+ <div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
901
+ ```
902
+
903
+ ### Screen Reader Support
904
+
905
+ - Voice reading of form errors
906
+ - State change notifications
907
+ - Proper placement of landmark elements
908
+
909
+ ## Dependencies
910
+
911
+ ```json
912
+ {
913
+ "dependencies": {
914
+ "@fastkit/vue-form-control": "Form functionality",
915
+ "@fastkit/vue-color-scheme": "Color theme",
916
+ "@fastkit/vue-stack": "Stack management",
917
+ "@fastkit/vue-action": "Action functionality",
918
+ "@fastkit/vue-app-layout": "Layout",
919
+ "@fastkit/rules": "Validation",
920
+ "@fastkit/helpers": "Utilities"
921
+ },
922
+ "peerDependencies": {
923
+ "vue": "^3.4.0",
924
+ "vue-router": "^4.0.0"
925
+ }
926
+ }
927
+ ```
928
+
3
929
  ## Documentation
4
- https://dadajam4.github.io/fastkit/vui/
930
+
931
+ For detailed documentation, please visit [here](https://dadajam4.github.io/fastkit/vui/).
932
+
933
+ ## License
934
+
935
+ MIT
package/dist/vui.d.ts CHANGED
@@ -141,6 +141,10 @@ declare function defineFormSelectorComponent(opts: DefineFormSelectorComponentOp
141
141
  type: PropType<_fastkit_vue_form_control.RawFormSelectorItems>;
142
142
  default: () => never[];
143
143
  };
144
+ preserveOrder: {
145
+ type: BooleanConstructor;
146
+ default: boolean;
147
+ };
144
148
  onClickItem: PropType<_fastkit_vue_form_control.FormSelectorGuard>;
145
149
  name: StringConstructor;
146
150
  tag: StringConstructor;
@@ -218,6 +222,10 @@ declare function defineFormSelectorComponent(opts: DefineFormSelectorComponentOp
218
222
  type: PropType<_fastkit_vue_form_control.RawFormSelectorItems>;
219
223
  default: () => never[];
220
224
  };
225
+ preserveOrder: {
226
+ type: BooleanConstructor;
227
+ default: boolean;
228
+ };
221
229
  onClickItem: PropType<_fastkit_vue_form_control.FormSelectorGuard>;
222
230
  name: StringConstructor;
223
231
  tag: StringConstructor;
@@ -274,6 +282,7 @@ declare function defineFormSelectorComponent(opts: DefineFormSelectorComponentOp
274
282
  multiple: boolean;
275
283
  stacked: boolean;
276
284
  items: _fastkit_vue_form_control.RawFormSelectorItems;
285
+ preserveOrder: boolean;
277
286
  }, _fastkit_vue_utils.DefinedSlots<{
278
287
  [x: `error:${string}`]: (error: _fastkit_vue_form_control.FormNodeError) => vue.VNode<vue.RendererNode, vue.RendererElement, {
279
288
  [key: string]: any;
@@ -4627,6 +4636,10 @@ declare const VCheckboxGroup: vue.DefineComponent<{
4627
4636
  type: vue.PropType<_fastkit_vue_form_control.RawFormSelectorItems>;
4628
4637
  default: () => never[];
4629
4638
  };
4639
+ preserveOrder: {
4640
+ type: BooleanConstructor;
4641
+ default: boolean;
4642
+ };
4630
4643
  onClickItem: vue.PropType<_fastkit_vue_form_control.FormSelectorGuard>;
4631
4644
  name: StringConstructor;
4632
4645
  tag: StringConstructor;
@@ -4704,6 +4717,10 @@ declare const VCheckboxGroup: vue.DefineComponent<{
4704
4717
  type: vue.PropType<_fastkit_vue_form_control.RawFormSelectorItems>;
4705
4718
  default: () => never[];
4706
4719
  };
4720
+ preserveOrder: {
4721
+ type: BooleanConstructor;
4722
+ default: boolean;
4723
+ };
4707
4724
  onClickItem: vue.PropType<_fastkit_vue_form_control.FormSelectorGuard>;
4708
4725
  name: StringConstructor;
4709
4726
  tag: StringConstructor;
@@ -4760,6 +4777,7 @@ declare const VCheckboxGroup: vue.DefineComponent<{
4760
4777
  multiple: boolean;
4761
4778
  stacked: boolean;
4762
4779
  items: _fastkit_vue_form_control.RawFormSelectorItems;
4780
+ preserveOrder: boolean;
4763
4781
  }, _fastkit_vue_utils.DefinedSlots<{
4764
4782
  [x: `error:${string}`]: (error: _fastkit_vue_form_control.FormNodeError) => vue.VNode<vue.RendererNode, vue.RendererElement, {
4765
4783
  [key: string]: any;
@@ -4926,6 +4944,10 @@ declare const VRadioGroup: vue.DefineComponent<{
4926
4944
  type: vue.PropType<_fastkit_vue_form_control.RawFormSelectorItems>;
4927
4945
  default: () => never[];
4928
4946
  };
4947
+ preserveOrder: {
4948
+ type: BooleanConstructor;
4949
+ default: boolean;
4950
+ };
4929
4951
  onClickItem: vue.PropType<_fastkit_vue_form_control.FormSelectorGuard>;
4930
4952
  name: StringConstructor;
4931
4953
  tag: StringConstructor;
@@ -5003,6 +5025,10 @@ declare const VRadioGroup: vue.DefineComponent<{
5003
5025
  type: vue.PropType<_fastkit_vue_form_control.RawFormSelectorItems>;
5004
5026
  default: () => never[];
5005
5027
  };
5028
+ preserveOrder: {
5029
+ type: BooleanConstructor;
5030
+ default: boolean;
5031
+ };
5006
5032
  onClickItem: vue.PropType<_fastkit_vue_form_control.FormSelectorGuard>;
5007
5033
  name: StringConstructor;
5008
5034
  tag: StringConstructor;
@@ -5059,6 +5085,7 @@ declare const VRadioGroup: vue.DefineComponent<{
5059
5085
  multiple: boolean;
5060
5086
  stacked: boolean;
5061
5087
  items: _fastkit_vue_form_control.RawFormSelectorItems;
5088
+ preserveOrder: boolean;
5062
5089
  }, _fastkit_vue_utils.DefinedSlots<{
5063
5090
  [x: `error:${string}`]: (error: _fastkit_vue_form_control.FormNodeError) => vue.VNode<vue.RendererNode, vue.RendererElement, {
5064
5091
  [key: string]: any;
@@ -5225,6 +5252,10 @@ declare const VSwitchGroup: vue.DefineComponent<{
5225
5252
  type: vue.PropType<_fastkit_vue_form_control.RawFormSelectorItems>;
5226
5253
  default: () => never[];
5227
5254
  };
5255
+ preserveOrder: {
5256
+ type: BooleanConstructor;
5257
+ default: boolean;
5258
+ };
5228
5259
  onClickItem: vue.PropType<_fastkit_vue_form_control.FormSelectorGuard>;
5229
5260
  name: StringConstructor;
5230
5261
  tag: StringConstructor;
@@ -5302,6 +5333,10 @@ declare const VSwitchGroup: vue.DefineComponent<{
5302
5333
  type: vue.PropType<_fastkit_vue_form_control.RawFormSelectorItems>;
5303
5334
  default: () => never[];
5304
5335
  };
5336
+ preserveOrder: {
5337
+ type: BooleanConstructor;
5338
+ default: boolean;
5339
+ };
5305
5340
  onClickItem: vue.PropType<_fastkit_vue_form_control.FormSelectorGuard>;
5306
5341
  name: StringConstructor;
5307
5342
  tag: StringConstructor;
@@ -5358,6 +5393,7 @@ declare const VSwitchGroup: vue.DefineComponent<{
5358
5393
  multiple: boolean;
5359
5394
  stacked: boolean;
5360
5395
  items: _fastkit_vue_form_control.RawFormSelectorItems;
5396
+ preserveOrder: boolean;
5361
5397
  }, _fastkit_vue_utils.DefinedSlots<{
5362
5398
  [x: `error:${string}`]: (error: _fastkit_vue_form_control.FormNodeError) => vue.VNode<vue.RendererNode, vue.RendererElement, {
5363
5399
  [key: string]: any;
@@ -5570,6 +5606,10 @@ declare const VSelect: vue.DefineComponent<{
5570
5606
  type: PropType<_fastkit_vue_form_control.RawFormSelectorItems>;
5571
5607
  default: () => never[];
5572
5608
  };
5609
+ preserveOrder: {
5610
+ type: BooleanConstructor;
5611
+ default: boolean;
5612
+ };
5573
5613
  onClickItem: PropType<_fastkit_vue_form_control.FormSelectorGuard>;
5574
5614
  name: StringConstructor;
5575
5615
  tag: StringConstructor;
@@ -5657,6 +5697,10 @@ declare const VSelect: vue.DefineComponent<{
5657
5697
  type: PropType<_fastkit_vue_form_control.RawFormSelectorItems>;
5658
5698
  default: () => never[];
5659
5699
  };
5700
+ preserveOrder: {
5701
+ type: BooleanConstructor;
5702
+ default: boolean;
5703
+ };
5660
5704
  onClickItem: PropType<_fastkit_vue_form_control.FormSelectorGuard>;
5661
5705
  name: StringConstructor;
5662
5706
  tag: StringConstructor;
@@ -5709,6 +5753,7 @@ declare const VSelect: vue.DefineComponent<{
5709
5753
  hiddenInfo: boolean;
5710
5754
  multiple: boolean;
5711
5755
  items: _fastkit_vue_form_control.RawFormSelectorItems;
5756
+ preserveOrder: boolean;
5712
5757
  }, _fastkit_vue_utils.DefinedSlots<{
5713
5758
  [x: `error:${string}`]: (error: _fastkit_vue_form_control.FormNodeError) => vue.VNode<vue.RendererNode, vue.RendererElement, {
5714
5759
  [key: string]: any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastkit/vui",
3
- "version": "0.19.56",
3
+ "version": "0.20.1",
4
4
  "description": "A simple, extensible UI kit for Vue applications.",
5
5
  "keywords": [
6
6
  "fastkit",
@@ -44,31 +44,31 @@
44
44
  "src"
45
45
  ],
46
46
  "dependencies": {
47
- "@fastkit/color-scheme": "^2.0.10",
48
47
  "@fastkit/dom": "^0.2.6",
48
+ "@fastkit/color-scheme-gen": "^0.14.13",
49
+ "@fastkit/color-scheme": "^2.0.10",
50
+ "@fastkit/icon-font": "^2.0.7",
49
51
  "@fastkit/helpers": "^0.14.5",
52
+ "@fastkit/tiny-logger": "^0.14.5",
53
+ "@fastkit/media-match": "^2.0.7",
54
+ "@fastkit/media-match-gen": "^0.14.13",
50
55
  "@fastkit/rules": "^0.14.7",
51
- "@fastkit/vue-action": "^0.3.19",
52
- "@fastkit/vue-app-layout": "^0.16.2",
53
- "@fastkit/vue-body-scroll-lock": "^0.2.13",
54
56
  "@fastkit/vue-click-outside": "^0.2.12",
55
- "@fastkit/vue-form-control": "^0.20.28",
56
- "@fastkit/vue-color-scheme": "^0.15.15",
57
- "@fastkit/vue-keyboard": "^0.2.5",
57
+ "@fastkit/stylebase": "^0.13.1",
58
+ "@fastkit/vue-form-control": "^0.21.0",
59
+ "@fastkit/vue-loading": "^0.15.15",
58
60
  "@fastkit/vue-location": "^0.4.2",
61
+ "@fastkit/vue-keyboard": "^0.2.5",
62
+ "@fastkit/vue-resize": "^0.2.12",
63
+ "@fastkit/vue-action": "^0.3.19",
64
+ "@fastkit/vue-stack": "^0.16.20",
65
+ "@fastkit/vue-body-scroll-lock": "^0.2.13",
59
66
  "@fastkit/vue-media-match": "^0.14.10",
60
67
  "@fastkit/vue-scroller": "^0.15.12",
61
- "@fastkit/vue-resize": "^0.2.12",
62
- "@fastkit/vue-stack": "^0.16.19",
63
- "@fastkit/vue-transitions": "^0.2.13",
64
- "@fastkit/tiny-logger": "^0.14.5",
68
+ "@fastkit/vue-color-scheme": "^0.15.15",
69
+ "@fastkit/vue-app-layout": "^0.16.2",
65
70
  "@fastkit/vue-utils": "^0.15.12",
66
- "@fastkit/icon-font": "^2.0.7",
67
- "@fastkit/stylebase": "^0.13.1",
68
- "@fastkit/media-match": "^2.0.7",
69
- "@fastkit/vue-loading": "^0.15.15",
70
- "@fastkit/color-scheme-gen": "^0.14.13",
71
- "@fastkit/media-match-gen": "^0.14.13"
71
+ "@fastkit/vue-transitions": "^0.2.13"
72
72
  },
73
73
  "peerDependencies": {
74
74
  "vue": "^3.4.0",