nicklabs-ui 1.0.81 → 1.0.83

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.79 | **Framework**: Vue 3.5+
5
+ **Version**: 1.0.81 | **Framework**: Vue 3.5+
6
6
 
7
7
  ---
8
8
 
@@ -544,7 +544,7 @@ function handleFiles(files: File[]) {
544
544
 
545
545
  #### NImageSelect
546
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`.
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`. Supports backfilling existing data by URL and a read-only view mode (see notes below).
548
548
 
549
549
  > Removal uses `useAlert` internally, so mount `<NAlert />` once at the app root (see [NAlert](#nalert)).
550
550
 
@@ -556,7 +556,8 @@ A card-grid image uploader with drag-and-drop, multi-file support, automatic res
556
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
557
  | `multiple` | `boolean` | `false` | Allow multiple images; when `false`, extra files are rejected with a `count` error |
558
558
  | `maxSize` | `number` | `0` | Per-file size limit in **MB** (`0` = unlimited) |
559
- | `disabled` | `boolean` | `false` | Disable adding and removing |
559
+ | `disabled` | `boolean` | `false` | Disable adding and removing (also dims the component) |
560
+ | `readonly` | `boolean` | `false` | View-only mode: browse and preview only. Hides the add/delete affordances and disables drag/drop, but keeps a normal (non-dimmed) appearance |
560
561
  | `title` | `string` | `""` | Label above the grid |
561
562
  | `showCount` | `boolean` | `false` | Show a count chip next to the title |
562
563
  | `emptyTitle` | `string` | `"將圖片拖放到這裡"` | Empty-state heading |
@@ -612,13 +613,24 @@ async function upload() {
612
613
  </script>
613
614
  ```
614
615
 
615
- > `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 automatically, and on unmount too unless `autoRevoke` is `false` — in which case the parent owns the URLs and must revoke them.
616
+ > `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 automatically, and on unmount too unless `autoRevoke` is `false` — in which case the parent owns the URLs and must revoke them. Only `blob:` URLs the component created are revoked; remote URLs are never touched.
617
+
618
+ **Backfilling existing data (edit mode)**
619
+
620
+ After upload, the server usually returns only a URL. To show that data, push items with just `id` and `url` (leave `file` empty) — the component probes `width`/`height` and derives the display name from the URL:
621
+
622
+ ```ts
623
+ const images = ref<NImageSelectItem[]>([
624
+ { id: 'remote-1', url: 'https://cdn.example.com/images/cover.jpg' },
625
+ ])
626
+ // <NImageSelect v-model="images" readonly title="Image content" />
627
+ ```
616
628
 
617
629
  ---
618
630
 
619
631
  #### NVideoSelect
620
632
 
621
- 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`.
633
+ 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`. Supports backfilling existing data by URL and a read-only view mode (same as [NImageSelect](#nimageselect)).
622
634
 
623
635
  > Removal uses `useAlert` internally, so mount `<NAlert />` once at the app root (see [NAlert](#nalert)).
624
636
 
@@ -630,7 +642,8 @@ A card-grid video uploader. Same interaction model as [NImageSelect](#nimagesele
630
642
  | `accept` | `string` | `"video/*"` | Accepted types — supports `video/*`, exact MIME (`video/mp4`), extensions (`.mp4`), and comma-separated combinations |
631
643
  | `multiple` | `boolean` | `false` | Allow multiple videos |
632
644
  | `maxSize` | `number` | `0` | Per-file size limit in **MB** (`0` = unlimited) |
633
- | `disabled` | `boolean` | `false` | Disable adding and removing |
645
+ | `disabled` | `boolean` | `false` | Disable adding and removing (also dims the component) |
646
+ | `readonly` | `boolean` | `false` | View-only mode: browse and preview only. Hides the add/delete affordances and disables drag/drop, but keeps a normal (non-dimmed) appearance |
634
647
  | `title` | `string` | `""` | Label above the grid |
635
648
  | `showCount` | `boolean` | `false` | Show a count chip next to the title |
636
649
  | `emptyTitle` | `string` | `"將影片拖放到這裡"` | Empty-state heading |
@@ -667,11 +680,13 @@ function onError(err: NVideoSelectError) {
667
680
  </script>
668
681
  ```
669
682
 
683
+ > **Backfilling existing data:** push items with just `id` and `url` (leave `file` empty); `duration` / `width` / `height` are probed and the name is derived from the URL. Only component-created `blob:` URLs are revoked on remove/unmount — remote URLs are never touched.
684
+
670
685
  ---
671
686
 
672
687
  #### NAudioSelect
673
688
 
674
- 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`.
689
+ An audio uploader that decodes each file with the Web Audio API to render a waveform, showing a skeleton row while decoding. Each row has an inline player (play/pause + click-and-drag seek on the waveform); only one track plays at a time. Same drag-and-drop / multi-file / confirm-before-remove model as the other media selectors; items carry `duration`. Supports backfilling existing data by URL and a read-only view mode.
675
690
 
676
691
  > Removal uses `useAlert` internally, so mount `<NAlert />` once at the app root (see [NAlert](#nalert)).
677
692
 
@@ -683,7 +698,8 @@ An audio uploader that decodes each file with the Web Audio API to render a wave
683
698
  | `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 |
684
699
  | `multiple` | `boolean` | `false` | Allow multiple files |
685
700
  | `maxSize` | `number` | `0` | Per-file size limit in **MB** (`0` = unlimited) |
686
- | `disabled` | `boolean` | `false` | Disable adding and removing |
701
+ | `disabled` | `boolean` | `false` | Disable adding and removing (also dims the component) |
702
+ | `readonly` | `boolean` | `false` | View-only mode: browse and play only. Hides the add/delete affordances and disables drag/drop, but keeps a normal (non-dimmed) appearance. **Playback and seek stay enabled** |
687
703
  | `title` | `string` | `""` | Label above the list |
688
704
  | `showCount` | `boolean` | `false` | Show a count chip next to the title |
689
705
  | `emptyTitle` | `string` | `"將音訊拖放到這裡"` | Empty-state heading |
@@ -720,6 +736,21 @@ function onError(err: NAudioSelectError) {
720
736
  </script>
721
737
  ```
722
738
 
739
+ **Backfilling existing data (edit mode)**
740
+
741
+ Push items with just `id` and `url` (leave `file` empty); the display name is derived from the URL:
742
+
743
+ ```ts
744
+ const tracks = ref<NAudioSelectItem[]>([
745
+ { id: 'remote-1', url: 'https://cdn.example.com/audios/track.mp3' },
746
+ ])
747
+ // <NAudioSelect v-model="tracks" readonly title="Audio content" />
748
+ ```
749
+
750
+ > **Waveform vs. duration for remote URLs.** The waveform needs the raw bytes (`fetch` + decode), which is subject to **CORS** — if the host doesn't allow it, the row falls back to a plain progress bar (track + played fill) instead of a waveform. Either way, **click/drag seek and the progress visual stay functional** — seeking only depends on the audio element's `duration`, not the waveform. Duration itself always resolves: it falls back to the `<audio>` element's metadata (no CORS needed), and playback works regardless.
751
+
752
+ > **Waveform cache (survives `v-if` remount).** Decoded waveforms are kept in a module-level cache keyed by `url`, so toggling the component with `v-if` (unmount → remount) restores the waveform instantly with no re-decode and no flash. A `v-if` unmount does **not** clear the cache; only an explicit remove (the delete button) evicts the entry. Note this is independent of `autoRevoke`: the cache preserves the *waveform*, but `autoRevoke: true` still revokes the *blob* on unmount, which breaks **playback** of locally-added files after remount — set `autoRevoke: false` (and take over `URL.revokeObjectURL` in the parent) if you need playback to survive a remount.
753
+
723
754
  ---
724
755
 
725
756
  #### NSwitch
@@ -2179,35 +2210,38 @@ type RadiusSize = 'none' | 'sm' | 'md' | 'lg' | 'xl'
2179
2210
  // Media selectors (NImageSelect / NVideoSelect / NAudioSelect)
2180
2211
  type MediaSelectErrorType = 'size' | 'type' | 'count'
2181
2212
 
2213
+ // Only `id` and `url` are required. The rest are optional so you can backfill
2214
+ // existing data from the server with just a URL — the component probes the
2215
+ // missing metadata (and, for audio, the waveform) itself.
2182
2216
  interface NImageSelectItem {
2183
2217
  id: string
2184
- file: File | null // native File for upload; null for seed/preset items
2185
- name: string
2186
- size: number // bytes
2187
- width: number | null
2188
- height: number | null
2189
- url: string // object URL for preview
2218
+ file?: File | null // native File for upload; null/absent for remote-URL items
2219
+ name?: string // derived from the URL when absent
2220
+ size?: number | null // bytes; null when unknown (remote)
2221
+ width?: number | null
2222
+ height?: number | null
2223
+ url: string // object URL or a remote URL — for thumbnail & preview
2190
2224
  }
2191
2225
  interface NImageSelectError { type: MediaSelectErrorType; file: File }
2192
2226
 
2193
2227
  interface NVideoSelectItem {
2194
2228
  id: string
2195
- file: File | null
2196
- name: string
2197
- size: number // bytes
2198
- duration: number | null // seconds
2199
- width: number | null
2200
- height: number | null
2229
+ file?: File | null
2230
+ name?: string
2231
+ size?: number | null
2232
+ duration?: number | null // seconds
2233
+ width?: number | null
2234
+ height?: number | null
2201
2235
  url: string
2202
2236
  }
2203
2237
  interface NVideoSelectError { type: MediaSelectErrorType; file: File }
2204
2238
 
2205
2239
  interface NAudioSelectItem {
2206
2240
  id: string
2207
- file: File | null
2208
- name: string
2209
- size: number // bytes
2210
- duration: number | null // seconds
2241
+ file?: File | null
2242
+ name?: string
2243
+ size?: number | null
2244
+ duration?: number | null // seconds
2211
2245
  url: string
2212
2246
  }
2213
2247
  interface NAudioSelectError { type: MediaSelectErrorType; file: File }