nicklabs-ui 1.0.71 → 1.0.74

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A Vue 3 component library with glassmorphism design, built for modern web applications.
4
4
 
5
- **Version**: 1.0.51 | **Framework**: Vue 3.5+
5
+ **Version**: 1.0.73 | **Framework**: Vue 3.5+
6
6
 
7
7
  ---
8
8
 
@@ -18,6 +18,9 @@ A Vue 3 component library with glassmorphism design, built for modern web applic
18
18
  - [NCheckbox](#ncheckbox)
19
19
  - [NSelect](#nselect)
20
20
  - [NFileSelect](#nfileselect)
21
+ - [NImageSelect](#nimageselect)
22
+ - [NVideoSelect](#nvideoselect)
23
+ - [NAudioSelect](#naudioselect)
21
24
  - [NSwitch](#nswitch)
22
25
  - [NDatePicker](#ndatepicker)
23
26
  - [Data Display](#data-display)
@@ -539,6 +542,183 @@ function handleFiles(files: File[]) {
539
542
 
540
543
  ---
541
544
 
545
+ #### NImageSelect
546
+
547
+ A card-grid image uploader with drag-and-drop, multi-file support, automatic resolution probing, a built-in dark preview lightbox, and a confirm dialog before removal. Files are tracked as `NImageSelectItem[]` via `v-model`.
548
+
549
+ > Removal uses `useAlert` internally, so mount `<NAlert />` once at the app root (see [NAlert](#nalert)).
550
+
551
+ **Props**
552
+
553
+ | Prop | Type | Default | Description |
554
+ |------|------|---------|-------------|
555
+ | `modelValue` | `NImageSelectItem[]` | `[]` | Bound image list (v-model) |
556
+ | `accept` | `string` | `"image/*"` | Accepted types — used for both the file dialog and drop validation. Supports `image/*`, exact MIME (`image/png`), extensions (`.png`), and comma-separated combinations |
557
+ | `multiple` | `boolean` | `false` | Allow multiple images; when `false`, extra files are rejected with a `count` error |
558
+ | `maxSize` | `number` | `0` | Per-file size limit in **MB** (`0` = unlimited) |
559
+ | `disabled` | `boolean` | `false` | Disable adding and removing |
560
+ | `title` | `string` | `""` | Label above the grid |
561
+ | `showCount` | `boolean` | `false` | Show a count chip next to the title |
562
+ | `emptyTitle` | `string` | `"將圖片拖放到這裡"` | Empty-state heading |
563
+ | `emptyHint` | `string` | `"或點擊以選擇檔案 · 支援 JPG / PNG / WebP / GIF,可一次多張"` | Empty-state sub-text |
564
+
565
+ **Events**
566
+
567
+ | Event | Payload | Description |
568
+ |-------|---------|-------------|
569
+ | `update:modelValue` | `NImageSelectItem[]` | The full list changed (v-model) |
570
+ | `change` | `NImageSelectItem[]` | The full list changed |
571
+ | `add` | `NImageSelectItem[]` | Items just added (this batch only) |
572
+ | `remove` | `NImageSelectItem` | An item was removed (after confirm) |
573
+ | `error` | `NImageSelectError` | A file was rejected (`type` / `size` / `count`) |
574
+
575
+ **Usage**
576
+
577
+ ```vue
578
+ <template>
579
+ <NImageSelect
580
+ v-model="images"
581
+ title="Product Photos"
582
+ multiple
583
+ :max-size="5"
584
+ show-count
585
+ @add="onAdd"
586
+ @error="onError"
587
+ />
588
+ </template>
589
+
590
+ <script setup lang="ts">
591
+ import { ref } from 'vue'
592
+ import { NImageSelect } from 'nicklabs-ui'
593
+ import type { NImageSelectItem, NImageSelectError } from 'nicklabs-ui'
594
+
595
+ const images = ref<NImageSelectItem[]>([])
596
+
597
+ function onAdd(items: NImageSelectItem[]) {
598
+ console.log('Added', items.length, 'images')
599
+ }
600
+
601
+ function onError(err: NImageSelectError) {
602
+ console.warn('Rejected:', err.file.name, err.type)
603
+ }
604
+
605
+ // Each item carries the native File for upload:
606
+ async function upload() {
607
+ const form = new FormData()
608
+ images.value.forEach((img) => img.file && form.append('files', img.file))
609
+ await api.upload(form)
610
+ }
611
+ </script>
612
+ ```
613
+
614
+ > `width` / `height` are probed asynchronously after a file is added and written back into the item. The component creates object URLs for previews and revokes them on removal and unmount automatically.
615
+
616
+ ---
617
+
618
+ #### NVideoSelect
619
+
620
+ A card-grid video uploader. Same interaction model as [NImageSelect](#nimageselect) — drag-and-drop, multi-file, confirm-before-remove, and a built-in preview modal — but items also carry `duration`, `width`, and `height`.
621
+
622
+ > Removal uses `useAlert` internally, so mount `<NAlert />` once at the app root (see [NAlert](#nalert)).
623
+
624
+ **Props**
625
+
626
+ | Prop | Type | Default | Description |
627
+ |------|------|---------|-------------|
628
+ | `modelValue` | `NVideoSelectItem[]` | `[]` | Bound video list (v-model) |
629
+ | `accept` | `string` | `"video/*"` | Accepted types — supports `video/*`, exact MIME (`video/mp4`), extensions (`.mp4`), and comma-separated combinations |
630
+ | `multiple` | `boolean` | `false` | Allow multiple videos |
631
+ | `maxSize` | `number` | `0` | Per-file size limit in **MB** (`0` = unlimited) |
632
+ | `disabled` | `boolean` | `false` | Disable adding and removing |
633
+ | `title` | `string` | `""` | Label above the grid |
634
+ | `showCount` | `boolean` | `false` | Show a count chip next to the title |
635
+ | `emptyTitle` | `string` | `"將影片拖放到這裡"` | Empty-state heading |
636
+ | `emptyHint` | `string` | `"或點擊以選擇檔案 · 支援 MP4 / MOV / WebM,可一次多支"` | Empty-state sub-text |
637
+
638
+ **Events**
639
+
640
+ | Event | Payload | Description |
641
+ |-------|---------|-------------|
642
+ | `update:modelValue` | `NVideoSelectItem[]` | The full list changed (v-model) |
643
+ | `change` | `NVideoSelectItem[]` | The full list changed |
644
+ | `add` | `NVideoSelectItem[]` | Items just added (this batch only) |
645
+ | `remove` | `NVideoSelectItem` | An item was removed (after confirm) |
646
+ | `error` | `NVideoSelectError` | A file was rejected (`type` / `size` / `count`) |
647
+
648
+ **Usage**
649
+
650
+ ```vue
651
+ <template>
652
+ <NVideoSelect v-model="videos" title="Clips" multiple :max-size="200" @error="onError" />
653
+ </template>
654
+
655
+ <script setup lang="ts">
656
+ import { ref } from 'vue'
657
+ import { NVideoSelect } from 'nicklabs-ui'
658
+ import type { NVideoSelectItem, NVideoSelectError } from 'nicklabs-ui'
659
+
660
+ const videos = ref<NVideoSelectItem[]>([])
661
+
662
+ function onError(err: NVideoSelectError) {
663
+ console.warn('Rejected:', err.file.name, err.type)
664
+ }
665
+ </script>
666
+ ```
667
+
668
+ ---
669
+
670
+ #### NAudioSelect
671
+
672
+ An audio uploader that decodes each file with the Web Audio API to render a waveform, showing a skeleton row while decoding. Same drag-and-drop / multi-file / confirm-before-remove model as the other media selectors; items carry `duration`.
673
+
674
+ > Removal uses `useAlert` internally, so mount `<NAlert />` once at the app root (see [NAlert](#nalert)).
675
+
676
+ **Props**
677
+
678
+ | Prop | Type | Default | Description |
679
+ |------|------|---------|-------------|
680
+ | `modelValue` | `NAudioSelectItem[]` | `[]` | Bound audio list (v-model) |
681
+ | `accept` | `string` | `".mp3,.wav,.ogg,.m4a,.aac,.flac,audio/mpeg,audio/wav,audio/ogg,audio/aac,audio/flac"` | Accepted types. Defaults to an explicit extension + MIME list (rather than `audio/*`) so the dialog doesn't offer `.mp4`-style containers |
682
+ | `multiple` | `boolean` | `false` | Allow multiple files |
683
+ | `maxSize` | `number` | `0` | Per-file size limit in **MB** (`0` = unlimited) |
684
+ | `disabled` | `boolean` | `false` | Disable adding and removing |
685
+ | `title` | `string` | `""` | Label above the list |
686
+ | `showCount` | `boolean` | `false` | Show a count chip next to the title |
687
+ | `emptyTitle` | `string` | `"將音訊拖放到這裡"` | Empty-state heading |
688
+ | `emptyHint` | `string` | `"或點擊以選擇檔案 · 支援 MP3 / WAV / OGG / M4A,可一次多首"` | Empty-state sub-text |
689
+
690
+ **Events**
691
+
692
+ | Event | Payload | Description |
693
+ |-------|---------|-------------|
694
+ | `update:modelValue` | `NAudioSelectItem[]` | The full list changed (v-model) |
695
+ | `change` | `NAudioSelectItem[]` | The full list changed |
696
+ | `add` | `NAudioSelectItem[]` | Items just added (this batch only) |
697
+ | `remove` | `NAudioSelectItem` | An item was removed (after confirm) |
698
+ | `error` | `NAudioSelectError` | A file was rejected (`type` / `size` / `count`) |
699
+
700
+ **Usage**
701
+
702
+ ```vue
703
+ <template>
704
+ <NAudioSelect v-model="tracks" title="Soundtrack" multiple @error="onError" />
705
+ </template>
706
+
707
+ <script setup lang="ts">
708
+ import { ref } from 'vue'
709
+ import { NAudioSelect } from 'nicklabs-ui'
710
+ import type { NAudioSelectItem, NAudioSelectError } from 'nicklabs-ui'
711
+
712
+ const tracks = ref<NAudioSelectItem[]>([])
713
+
714
+ function onError(err: NAudioSelectError) {
715
+ console.warn('Rejected:', err.file.name, err.type)
716
+ }
717
+ </script>
718
+ ```
719
+
720
+ ---
721
+
542
722
  #### NSwitch
543
723
 
544
724
  Animated toggle switch.
@@ -1992,8 +2172,46 @@ type NTagIntent = 'none' | 'primary' | 'success' | 'warning' | 'error' | 'info'
1992
2172
  type NTagVariant = 'solid' | 'light' | 'outline'
1993
2173
  type PaddingSize = 'none' | 'sm' | 'md' | 'lg'
1994
2174
  type RadiusSize = 'none' | 'sm' | 'md' | 'lg' | 'xl'
2175
+
2176
+ // Media selectors (NImageSelect / NVideoSelect / NAudioSelect)
2177
+ type MediaSelectErrorType = 'size' | 'type' | 'count'
2178
+
2179
+ interface NImageSelectItem {
2180
+ id: string
2181
+ file: File | null // native File for upload; null for seed/preset items
2182
+ name: string
2183
+ size: number // bytes
2184
+ width: number | null
2185
+ height: number | null
2186
+ url: string // object URL for preview
2187
+ }
2188
+ interface NImageSelectError { type: MediaSelectErrorType; file: File }
2189
+
2190
+ interface NVideoSelectItem {
2191
+ id: string
2192
+ file: File | null
2193
+ name: string
2194
+ size: number // bytes
2195
+ duration: number | null // seconds
2196
+ width: number | null
2197
+ height: number | null
2198
+ url: string
2199
+ }
2200
+ interface NVideoSelectError { type: MediaSelectErrorType; file: File }
2201
+
2202
+ interface NAudioSelectItem {
2203
+ id: string
2204
+ file: File | null
2205
+ name: string
2206
+ size: number // bytes
2207
+ duration: number | null // seconds
2208
+ url: string
2209
+ }
2210
+ interface NAudioSelectError { type: MediaSelectErrorType; file: File }
1995
2211
  ```
1996
2212
 
2213
+ > `NImageSelectErrorType`, `NVideoSelectErrorType`, and `NAudioSelectErrorType` are each exported individually (all aliased to `'size' | 'type' | 'count'`).
2214
+
1997
2215
  ---
1998
2216
 
1999
2217
  ## CSS Variables