@wpic/ui 0.3.16 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ ## 0.5.0 — 2026-09-08
4
+
5
+ - Add opt-in `WThemeProvider`, UI5 light/dark tokens, typed color overrides, `themeVariables`, and chart palettes. Existing consumers keep their default appearance.
6
+ - Add `WPageHeader` with actions, metadata and unframed filters.
7
+ - Apply the validated controls, monochrome primary/neutral secondary buttons, subtle cards and white-thumb switches inside the provider.
8
+ - Carry themes into all shared popovers, including calendars, date ranges and editable tags. Add nested Escape handling and safe outside-listener cleanup.
9
+ - Make date clear actions keyboard accessible; preserve date bounds, emitted values and legacy props. Fix local date serialization in date-range calendars.
10
+ - Ensure packaged CSS always includes the public token declarations, even when compiled utilities already reference their names.
11
+ - Add packaged-browser regression coverage for light/dark scopes, customization, nested panels, switches, dates and backwards-compatible semantic components.
12
+
13
+
14
+ ## 0.4.0 — 2026-09-08
15
+
16
+ ### Banners, tags and badges
17
+
18
+ - Shared Tailwind palettes reproduce the approved Analyze light/dark laboratory: blue information, yellow attention, emerald success and rose error.
19
+ - `WBanner` adds title, lead, actions slot, optional dismissal and an explicit theme override while retaining slot-only usage.
20
+ - `WTag` adds controlled checkable tags, configurable selected color, normal/muted unselected appearance and a true disabled state. The selection indicator is a 14px circle with the approved larger check. Borders use 5% opacity.
21
+ - `WBadge` uses stronger light fills, semibold light labels and the approved bottom padding. Adds a compact `count` option and explicit theme selection.
22
+ - `theme="auto"` follows `.dark`; explicit `light`/`dark` values support previews without inheriting the host theme.
23
+ - Editable and removable tags remain supported; combined interactions no longer create nested buttons. `WAlert` now delegates to `WBanner` and retains its props, slot and close event.
24
+ - Published documentation defines when to use each component. Browser coverage checks distributed CSS without host Tailwind, themes, selection, keyboard interaction, disabled controls, popover editing, removal and dismissal.
25
+
26
+ ### Adoption notes
27
+
28
+ This minor release intentionally updates appearance. The default tag and badge label size is now `text-xs`; badges use slightly more bottom padding. `WBadge.bordered` remains opt-in for compatibility (enable it for the laboratory treatment). Existing `WTag` variants, editable string model, remove event and compact size remain available. `primary` badges retain their inverted neutral appearance. Muted tags are selectable, not disabled.
29
+
30
+ Import `@wpic/ui/style.css` alongside the components. No host Tailwind source scanning is required. See `COMPONENTS.md` for examples and API details.
31
+
32
+ ### Verification
33
+
34
+ Run `npm run build` followed by `npm run test:semantic`. The browser tests use Google Chrome (`npx playwright install chrome` if needed). Open `/test-ui/semantic.html` with `npm run test:ui` for the standalone package showcase.
package/COMPONENTS.md ADDED
@@ -0,0 +1,680 @@
1
+ # COMPONENTS.md — wpic-ui component reference
2
+ Use this as reference when writing Vue components. Updated for 0.5.0.
3
+
4
+
5
+ ## Theme provider and customization (0.5)
6
+
7
+ The UI5 preset is **opt-in**. Existing consumers without `WThemeProvider` keep their current styling and existing component props/events. Import the packaged stylesheet; no host Tailwind scanning is required.
8
+
9
+ ```vue
10
+ <script setup lang="ts">
11
+ import { WThemeProvider, WButton, type WThemeOverrides } from '@wpic/ui'
12
+ import '@wpic/ui/style.css'
13
+ const colors: WThemeOverrides = {
14
+ light: { accent: '#85C531', primary: '#171717', onPrimary: '#FFFFFF' },
15
+ dark: { surface: '#151515', popover: '#1F201D', raised: '#2A2A28', primary: '#FFFFFF', onPrimary: '#171717' },
16
+ }
17
+ </script>
18
+ <template>
19
+ <WThemeProvider mode="auto" :colors="colors">
20
+ <WButton>Save</WButton>
21
+ </WThemeProvider>
22
+ </template>
23
+ ```
24
+
25
+ `mode` accepts `auto` (inherits a parent provider or the document `.dark` class), `light`, or `dark`. `preset` currently accepts `ui5`. `colors` is a partial map per mode; unspecified entries use the preset. A nested provider can preview another theme without changing the document or saved application preference.
26
+
27
+ Color keys (`WThemeColors`): `canvas`, `surface`, `input`, `raised`, `popover`, `border`, `borderHover`, `text`, `muted`, `dim`, `accent`, `onAccent`, `primary`, `primaryHover`, `onPrimary`, `secondary`, `secondaryHover`, `onSecondary`, `secondaryShadow`, `switchOn`, `switchOff`, `switchDisabled`, `switchThumb`, `focusRing`, `positive`, `negative`. Secondary and switch backgrounds accept CSS gradients. `focusRing` defaults to the accent at 18% opacity. Change both foreground and background when customizing contrast.
28
+
29
+ The provider supplies prefixed CSS variables and the existing surface/text aliases. `--wpic-primary` remains an accent alias for legacy components; monochrome action buttons use `--wpic-button-primary`. `themeVariables(mode, overrides)` exposes the same mapping for document-level integration. Use the provider's **colors prop** for scoped overrides that must follow teleports. Direct DOM CSS overrides are not copied to teleported panels.
30
+
31
+ `WPopover`, date calendars, ranges, month pickers, selects and editable-tag option panels receive the provider context directly. Panels inherit the selected palette even outside the provider's DOM subtree. Nested popovers keep their parent open when selecting a child option; Escape closes the innermost visible panel and returns focus to its anchor.
32
+
33
+ UI5 uses 36px default controls (explicit small/large sizes and plain selects retain their sizing), standard button radii, subtle card contours, uniform input fills, and a 1px accent focus border plus a 3px translucent ring. Disabled and validation states remain distinct. `WTable` structure and behavior are unchanged.
34
+
35
+ ### WPageHeader
36
+
37
+ Props: `title: string`, `description?: string`, `headingLevel?: 1 | 2` (default 1). Slots: `actions`, `metadata`, `filters`. Titles wrap, actions can move to the next line, filters have no border or internal padding. The consumer supplies outer page spacing. Existing application headers can forward their slots through this component.
38
+
39
+ ### Chart palettes
40
+
41
+ `chartPalettes.light` and `.dark` contain six stable series colors: blue, teal, violet, amber, rose, slate. WPIC green is reserved for UI accents. Choose by theme and keep series-to-color assignments stable; retain labels/legends when displaying multiple series. Consumers can supply their own palette arrays.
42
+
43
+ ### Date and switch additions
44
+
45
+ - `WDatePicker`: a selected, clearable date shows one trailing clear button instead of a chevron. `clearLabel` localizes its accessible name (default `Clear date`). Clear emits the existing empty-string model/change values and closes the panel. Date bounds and output format remain unchanged.
46
+ - `WDateRangePicker`: retains `DateRange | null`, presets and custom ranges; adds `disabled`, `ariaLabel`, and `clearLabel` (default `Clear date range`). Clear emits `null`. Calendar dates use local year/month/day, avoiding UTC day shifts in positive time zones.
47
+ - `WToggle`: retains boolean v-model, default label slot and `disabled`; adds `ariaLabel`. The actual button has `role="switch"` and `aria-checked`. The UI5 disabled track is dark in dark mode, with a white thumb.
48
+
49
+ ## Banners, tags and badges: usage
50
+
51
+ | Component | Purpose | Examples |
52
+ |---|---|---|
53
+ | `WBanner` | Explain a contextual message, optionally with an action | Import failed; data updated |
54
+ | `WTag` | Categorize an item or select a category filter | Tmall, Japan, Advertising |
55
+ | `WBadge` | Indicate a read-only status or count | Completed, Needs review, 3 |
56
+
57
+ Tags use neutral or blue categories by default. Checkable tags may use a configurable selection color; that color indicates selection, not severity. `uncheckedAppearance="muted"` only changes appearance: the tag remains clickable and keyboard accessible. Use `disabled` to actually prevent interaction. Several tags may describe the same item.
58
+
59
+ Badges use neutral for no severity, blue for information, yellow for attention, emerald for success and rose for error. Keep a visible label or accessible count context; color alone is insufficient. Status badges belong next to the item they describe. They are not buttons.
60
+
61
+ All three components accept `theme="auto"` (default, follows an ancestor `.dark`), `theme="light"` or `theme="dark"`. Explicit themes stay independent of the application theme, including a light preview within a dark page. Import `@wpic/ui/style.css`; the package contains the required Tailwind utilities, so host source scanning is unnecessary.
62
+
63
+ ```vue
64
+ <WBanner variant="warning" title="Attention needed" lead="Review this before continuing."
65
+ dismissible @close="showBanner = false" v-if="showBanner">
66
+ Check the source data before publishing.
67
+ <template #actions><WButton @click="review">Review data</WButton></template>
68
+ </WBanner>
69
+ <WTag variant="info">Advertising</WTag>
70
+ <WTag checkable v-model:checked="selected" checked-variant="success" unchecked-appearance="muted">Tmall</WTag>
71
+ <WBadge variant="success" :icon="CircleCheck" bordered>Completed</WBadge>
72
+ <WBadge variant="warning" count bordered aria-label="3 reports need review">3</WBadge>
73
+ ```
74
+
75
+ `WBanner` emits `close`; the parent owns dismissal and restoration. `WAlert` is retained as a compatibility wrapper around `WBanner`. Prefer `WBanner` for new contextual messages.
76
+
77
+ ## WAlert
78
+
79
+ **Import:** `import { WAlert } from '@wpic/ui'`
80
+
81
+ **Props:**
82
+ ```ts
83
+ variant?: 'info' | 'success' | 'warning' | 'error'
84
+ title?: string
85
+ dismissible?: boolean
86
+ ```
87
+
88
+ **Emits:**
89
+ ```ts
90
+ close: []
91
+ ```
92
+
93
+ ---
94
+
95
+ ## WAvatar
96
+
97
+ **Import:** `import { WAvatar } from '@wpic/ui'`
98
+
99
+ **Props:**
100
+ ```ts
101
+ name: string
102
+ src?: string
103
+ size?: 'sm' | 'md' | 'lg'
104
+ ```
105
+
106
+ ---
107
+
108
+ ## WBadge
109
+
110
+ **Import:** `import { WBadge } from '@wpic/ui'`
111
+
112
+ ```ts
113
+ variant?: 'default' | 'neutral' | 'primary' | 'success' | 'warning' | 'error' | 'info'
114
+ size?: 'sm' | 'md' // default md; sm retains the compact 10px label
115
+ icon?: Component
116
+ dot?: boolean
117
+ bordered?: boolean // default false, preserving existing callers
118
+ count?: boolean // centered compact counter
119
+ theme?: 'auto' | 'light' | 'dark'
120
+ ```
121
+
122
+ Light badges have stronger fills and semibold labels. Default badges use `text-xs`, top padding `0.125rem` and bottom padding `2.5px`. `primary` retains its inverted neutral treatment.
123
+
124
+ ---
125
+
126
+ ## WBanner
127
+
128
+ **Import:** `import { WBanner } from '@wpic/ui'`
129
+
130
+ ```ts
131
+ variant?: 'info' | 'warning' | 'error' | 'success' // default info
132
+ icon?: Component
133
+ title?: string
134
+ lead?: string
135
+ dismissible?: boolean // default false
136
+ dismissLabel?: string // default "Dismiss"; localize in the caller
137
+ theme?: 'auto' | 'light' | 'dark'
138
+ ```
139
+
140
+ Slots: default (body or standalone message), `actions`. Emits `close`. Warnings and errors use `role="alert"`; information and success use `role="status"`. Existing slot-only content is supported.
141
+
142
+ ---
143
+
144
+ ## WButton
145
+
146
+ **Import:** `import { WButton } from '@wpic/ui'`
147
+
148
+ **Props:**
149
+ ```ts
150
+ variant?: 'primary' | 'secondary' | 'destructive' | 'ghost' | 'outline'
151
+ size?: 'xs' | 'sm' | 'md' | 'lg'
152
+ loading?: boolean
153
+ disabled?: boolean
154
+ icon?: object | Function
155
+ href?: string
156
+ ```
157
+
158
+ **Emits:**
159
+ ```ts
160
+ click: [e: MouseEvent]
161
+ ```
162
+
163
+ ---
164
+
165
+ ## WCard
166
+
167
+ **Import:** `import { WCard } from '@wpic/ui'`
168
+
169
+ **Props:**
170
+ ```ts
171
+ title?: string
172
+ empty?: boolean
173
+ noPadding?: boolean
174
+ bodyClass?: string
175
+ borderless?: boolean
176
+ headerDivider?: boolean // defaults to true
177
+ padding?: 'sm' | 'md' | 'lg' // shared header/body/footer spacing; defaults to md
178
+ ```
179
+
180
+ ---
181
+
182
+ ## WCheckbox
183
+
184
+ **Import:** `import { WCheckbox } from '@wpic/ui'`
185
+
186
+ **Props:**
187
+ ```ts
188
+ disabled?: boolean
189
+ ```
190
+
191
+ ---
192
+
193
+ ## WEmptyState
194
+
195
+ **Import:** `import { WEmptyState } from '@wpic/ui'`
196
+
197
+ **Props:**
198
+ ```ts
199
+ icon?: Component
200
+ title: string
201
+ description?: string
202
+ ```
203
+
204
+ ---
205
+
206
+ ## WFileList
207
+
208
+ **Import:** `import { WFileList } from '@wpic/ui'`
209
+
210
+ **Props:**
211
+ ```ts
212
+ files: any[]
213
+ nameKey?: string
214
+ idKey?: string
215
+ statusKey?: string
216
+ selectedId?: number | string | null
217
+ removable?: boolean
218
+ ```
219
+
220
+ **Emits:**
221
+ ```ts
222
+ select: [item: any]
223
+ remove: [item: any]
224
+ ```
225
+
226
+ ---
227
+
228
+ ## WFilter
229
+
230
+ **Import:** `import { WFilter } from '@wpic/ui'`
231
+
232
+ **Emits:**
233
+ ```ts
234
+ 'update:modelValue': [value: T]
235
+ ```
236
+
237
+ ---
238
+
239
+ ## WFormLabel
240
+
241
+ **Import:** `import { WFormLabel } from '@wpic/ui'`
242
+
243
+ **Props:**
244
+ ```ts
245
+ required?: boolean
246
+ ```
247
+
248
+ ---
249
+
250
+ ## WImageTile
251
+
252
+ **Import:** `import { WImageTile } from '@wpic/ui'`
253
+
254
+ **Props:**
255
+ ```ts
256
+ src: string
257
+ selected?: boolean
258
+ alt?: string
259
+ ```
260
+
261
+ **Emits:**
262
+ ```ts
263
+ click: []
264
+ ```
265
+
266
+ ---
267
+
268
+ ## WInput
269
+
270
+ **Import:** `import { WInput } from '@wpic/ui'`
271
+
272
+ **Props:**
273
+ ```ts
274
+ icon?: object | Function
275
+ label?: string
276
+ placeholder?: string
277
+ required?: boolean
278
+ type?: string
279
+ disabled?: boolean
280
+ size?: 'sm' | 'md' | 'lg'
281
+ clearable?: boolean
282
+ ```
283
+
284
+ ---
285
+
286
+ ## WModal
287
+
288
+ **Import:** `import { WModal } from '@wpic/ui'`
289
+
290
+ **Props:**
291
+ ```ts
292
+ title?: string
293
+ subtitle?: string
294
+ size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'
295
+ zIndex?: string
296
+ hideClose?: boolean
297
+ noPadding?: boolean
298
+ ```
299
+
300
+ **Emits:**
301
+ ```ts
302
+ close: []
303
+ ```
304
+
305
+ ---
306
+
307
+ ## WOptionPopover
308
+
309
+ **Import:** `import { WOptionPopover } from '@wpic/ui'`
310
+
311
+ **Props:**
312
+ ```ts
313
+ modelValue: string | null
314
+ options: PopoverOption[]
315
+ anchor: HTMLElement | null
316
+ ```
317
+
318
+ **Emits:**
319
+ ```ts
320
+ 'update:modelValue': [value: string]
321
+ 'close': []
322
+ ```
323
+
324
+ ---
325
+
326
+ ## WPagination
327
+
328
+ **Import:** `import { WPagination } from '@wpic/ui'`
329
+
330
+ **Props:**
331
+ ```ts
332
+ modelValue: number
333
+ total: number
334
+ limit?: number
335
+ syncUrl?: boolean // default: true; automatically false inside WModal
336
+ queryParam?: string // default: 'page'
337
+ ```
338
+
339
+ **Emits:**
340
+ ```ts
341
+ 'update:modelValue': [p: number]
342
+ ```
343
+
344
+ ---
345
+
346
+ ## WPillGroup
347
+
348
+ **Import:** `import { WPillGroup } from '@wpic/ui'`
349
+
350
+ **Props:**
351
+ ```ts
352
+ options: Option[]
353
+ multiple?: boolean
354
+ size?: 'sm' | 'md'
355
+ disabled?: boolean
356
+ ```
357
+
358
+ ---
359
+
360
+ ## WPopover
361
+
362
+ **Import:** `import { WPopover } from '@wpic/ui'`
363
+
364
+ **Props:**
365
+ ```ts
366
+ open: boolean
367
+ anchor: HTMLElement | null
368
+ placement?: 'top' | 'bottom' | 'left' | 'right' | 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end'
369
+ width?: string
370
+ minWidth?: string
371
+ maxHeight?: string
372
+ offset?: number
373
+ ```
374
+
375
+ **Emits:**
376
+ ```ts
377
+ close: []
378
+ ```
379
+
380
+ ---
381
+
382
+ ## WProgress
383
+
384
+ **Import:** `import { WProgress } from '@wpic/ui'`
385
+
386
+ **Props:**
387
+ ```ts
388
+ value: number
389
+ max?: number
390
+ color?: string
391
+ size?: 'sm' | 'md' | 'lg'
392
+ label?: string
393
+ animated?: boolean
394
+ ```
395
+
396
+ ---
397
+
398
+ ## WSearchSelect
399
+
400
+ **Import:** `import { WSearchSelect } from '@wpic/ui'`
401
+
402
+ **Props:**
403
+ ```ts
404
+ modelValue?: string | number | (string | number)[]
405
+ options?: Option[]
406
+ placeholder?: string
407
+ size?: 'sm' | 'md' | 'lg'
408
+ multiple?: boolean
409
+ maxLabels?: number
410
+ pinnedOptions?: Option[]
411
+ ```
412
+
413
+ **Emits:**
414
+ ```ts
415
+ 'update:modelValue': [v: any]
416
+ ```
417
+
418
+ ---
419
+
420
+ ## WSelect
421
+
422
+ **Import:** `import { WSelect } from '@wpic/ui'`
423
+
424
+ **Props:**
425
+ ```ts
426
+ modelValue?: string | number | (string | number)[] | null
427
+ options?: OptionRaw[]
428
+ multiple?: boolean
429
+ searchable?: boolean
430
+ label?: string
431
+ placeholder?: string
432
+ allLabel?: string
433
+ maxLabels?: number
434
+ error?: boolean
435
+ disabled?: boolean
436
+ size?: 'sm' | 'md' | 'lg'
437
+ ```
438
+
439
+ **Emits:**
440
+ ```ts
441
+ 'update:modelValue': [v: any]
442
+ ```
443
+
444
+ ---
445
+
446
+ ## WSidePanel
447
+
448
+ **Import:** `import { WSidePanel } from '@wpic/ui'`
449
+
450
+ **Props:**
451
+ ```ts
452
+ open: boolean
453
+ title?: string
454
+ subtitle?: string
455
+ width?: string // e.g. '420px', '560px' — default 420px
456
+ zIndex?: number // default 50
457
+ ```
458
+
459
+ **Emits:**
460
+ ```ts
461
+ close: []
462
+ ```
463
+
464
+ ---
465
+
466
+ ## WSkeleton
467
+
468
+ **Import:** `import { WSkeleton } from '@wpic/ui'`
469
+
470
+ **Props:**
471
+ ```ts
472
+ width?: string
473
+ height?: string
474
+ rounded?: string
475
+ ```
476
+
477
+ ---
478
+
479
+ ## WSpinner
480
+
481
+ **Import:** `import { WSpinner } from '@wpic/ui'`
482
+
483
+ **Props:**
484
+ ```ts
485
+ size?: 'sm' | 'md' | 'lg' // default 'md'
486
+ tone?: 'primary' | 'muted' // default 'primary'
487
+ ```
488
+
489
+ ---
490
+
491
+ ## WLoader
492
+
493
+ **Import:** `import { WLoader } from '@wpic/ui'`
494
+
495
+ **Props:**
496
+ ```ts
497
+ text?: string // default 'Loading...'
498
+ size?: 'sm' | 'md' | 'lg' // default 'md'
499
+ ```
500
+
501
+ ---
502
+
503
+ ## WSlidePanel
504
+
505
+ **Import:** `import { WSlidePanel } from '@wpic/ui'`
506
+
507
+ **Props:**
508
+ ```ts
509
+ modelValue: boolean
510
+ title?: string
511
+ width?: string
512
+ ```
513
+
514
+ **Emits:**
515
+ ```ts
516
+ 'update:modelValue': [v: boolean]
517
+ ```
518
+
519
+ ---
520
+
521
+ ## WSlider
522
+
523
+ **Import:** `import { WSlider } from '@wpic/ui'`
524
+
525
+ **Props:**
526
+ ```ts
527
+ min?: number
528
+ max?: number
529
+ step?: number
530
+ disabled?: boolean
531
+ ```
532
+
533
+ ---
534
+
535
+ ## WStatCard
536
+
537
+ **Import:** `import { WStatCard } from '@wpic/ui'`
538
+
539
+ **Props:**
540
+ ```ts
541
+ label: string
542
+ value: string | number
543
+ unit?: string
544
+ color?: string
545
+ sub?: string
546
+ subColor?: string
547
+ ```
548
+
549
+ ---
550
+
551
+ ## WTable
552
+
553
+ **Import:** `import { WTable } from '@wpic/ui'`
554
+
555
+ **Props:**
556
+ ```ts
557
+ data?: Record<string, unknown>[]
558
+ columns: WTableColumn[]
559
+ loading?: boolean
560
+ emptyText?: string
561
+ pageSize?: number
562
+ searchPlaceholder?: string
563
+ rowClickable?: boolean
564
+ showHeader?: boolean
565
+ ```
566
+
567
+ **Emits:**
568
+ ```ts
569
+ 'row-click': [row: Record<string, unknown>]
570
+ ```
571
+
572
+ ---
573
+
574
+ ## WTag
575
+
576
+ **Import:** `import { WTag } from '@wpic/ui'`
577
+
578
+ ```ts
579
+ variant?: 'default' | 'neutral' | 'primary' | 'success' | 'warning' | 'error' | 'info' | 'overlay' | 'teal' | 'meta'
580
+ size?: 'sm' | 'md'
581
+ theme?: 'auto' | 'light' | 'dark'
582
+ checkable?: boolean
583
+ checked?: boolean // v-model:checked; controlled by the caller
584
+ checkedVariant?: 'neutral' | 'info' | 'warning' | 'success' | 'error' // default neutral
585
+ uncheckedAppearance?: 'normal' | 'muted' // default normal
586
+ disabled?: boolean
587
+ editable?: boolean
588
+ modelValue?: string | null
589
+ options?: PopoverOption[]
590
+ removable?: boolean
591
+ removeLabel?: string // default "Remove tag"; provide contextual/localized labels
592
+ ```
593
+
594
+ Emits `update:checked(boolean)`, `update:modelValue(string)` and `remove()`.
595
+
596
+ Checkable tags are native toggle buttons with `aria-pressed`, a 14px circle, and a custom check. They support Space and Enter. Checkable takes precedence over editable; use one interaction mode at a time. Editable tags retain the option popover. Removable interactive tags use sibling buttons, avoiding nested buttons. Disabled tags block selection, editing and removal. Borders remain subtle (5% opacity).
597
+
598
+ ---
599
+
600
+ ## WTagInput
601
+
602
+ **Import:** `import { WTagInput } from '@wpic/ui'`
603
+
604
+ **Props:**
605
+ ```ts
606
+ modelValue?: WTagItem[]
607
+ locale?: string
608
+ locales?: string[]
609
+ recentKey?: string
610
+ /** Called for typeahead suggestions; return matching tags */
611
+ searchFn?: (q: string, locale: string) => Promise<WTagItem[]>
612
+ /** Called on Enter with raw input; return a resolved/new tag with translations */
613
+ resolveFn?: (input: string, locale: string) => Promise<WTagItem>
614
+ /** Called to persist add/remove; throw to signal failure (triggers rollback) */
615
+ mutateFn?: (action: 'add' | 'remove', tag: WTagItem) => Promise<void>
616
+ placeholder?: string
617
+ labelRecent?: string
618
+ labelAutoTranslated?: string
619
+ labelAlreadyAdded?: string
620
+ labelExisting?: string
621
+ labelNew?: string
622
+ labelEdit?: string
623
+ labelCancel?: string
624
+ labelConfirm?: string
625
+ labelErrorResolving?: string
626
+ labelErrorAdding?: string
627
+ ```
628
+
629
+ **Emits:**
630
+ ```ts
631
+ 'update:modelValue': [tags: WTagItem[]]
632
+ ```
633
+
634
+ ---
635
+
636
+ ## WTextarea
637
+
638
+ **Import:** `import { WTextarea } from '@wpic/ui'`
639
+
640
+ **Props:**
641
+ ```ts
642
+ modelValue?: string
643
+ placeholder?: string
644
+ rows?: number
645
+ disabled?: boolean
646
+ error?: boolean
647
+ label?: string
648
+ resize?: 'none' | 'vertical' | 'both'
649
+ ```
650
+
651
+ **Emits:**
652
+ ```ts
653
+ 'update:modelValue': [string]
654
+ ```
655
+
656
+ ---
657
+
658
+ ## WToggle
659
+
660
+ **Import:** `import { WToggle } from '@wpic/ui'`
661
+
662
+ **Props:**
663
+ ```ts
664
+ disabled?: boolean
665
+ ```
666
+
667
+ ---
668
+
669
+ ## WTooltip
670
+
671
+ **Import:** `import { WTooltip } from '@wpic/ui'`
672
+
673
+ **Props:**
674
+ ```ts
675
+ content: string
676
+ placement?: 'top' | 'bottom' | 'left' | 'right'
677
+ delay?: number
678
+ ```
679
+
680
+ ---