ng-hub-ui-forms 22.34.0 → 22.35.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.
package/README.md CHANGED
@@ -14,6 +14,7 @@ This package is part of [Hub UI](https://hubui.dev/en/), a collection of Angular
14
14
  - Docs: https://hubui.dev/en/forms/overview/
15
15
  - Live examples: https://hubui.dev/en/forms/examples/
16
16
  - Hub UI: https://hubui.dev/en/
17
+ - Hub UI on GitHub (issues, roadmap and contributing): https://github.com/hub-env/hub-ui
17
18
 
18
19
  ## 🧩 Library Family `ng-hub-ui`
19
20
 
@@ -95,7 +96,7 @@ mode — no Bootstrap dependency.
95
96
 
96
97
  ## 🎯 Features
97
98
 
98
- - **Fields** — `hub-input` (text/number/email/password/color/switch/checkbox/counter, with input-group addons & masks, projected in-field affixes, a built-in `clearable` button, the mixed `indeterminate` state on checkboxes and debounced typeahead `search`; the `file` format is **deprecated** → use `hub-file-input`), `hub-otp-input`, `hub-textarea` (+ `hubAutoresize`), `hub-slider` (single / dual thumb, gradient fill), `hub-segmented` (segmented control field — single & multiple selection, horizontal & vertical, with label + validation), `hub-select` (dropdown format, grouping, client-side search via `searchable` **and** server-side async typeahead via a `typeahead` Subject, tag creation with `addTag`, custom templates, `prepend` / `append` group addons and attached icons/buttons via `hubPrepend` / `hubAppend`; the `buttons` / `checkbox` / `radio` formats are **deprecated** → use `hub-segmented`), `hub-datepicker` (single & range at any granularity from a year to a second, time picking, min/max down to the minute, keyboard nav, i18n), `hub-timepicker` (a time of day as `HH:MM`, on the platform's own time control, with `min` / `max` / `step`), `hub-file-input` (drag & drop, clipboard paste, type/size limits, previews, optional upload progress).
99
+ - **Fields** — `hub-input` (text/number/email/password/color/switch/checkbox/counter, the colour format as a hex field or, given a palette, a grid of swatches, with input-group addons & masks, projected in-field affixes, a built-in `clearable` button, the mixed `indeterminate` state on checkboxes and debounced typeahead `search`; the `file` format is **deprecated** → use `hub-file-input`), `hub-otp-input`, `hub-textarea` (+ `hubAutoresize`), `hub-slider` (single / dual thumb, gradient fill), `hub-segmented` (segmented control field — single & multiple selection, horizontal & vertical, with label + validation), `hub-select` (dropdown format, grouping, client-side search via `searchable` **and** server-side async typeahead via a `typeahead` Subject, tag creation with `addTag`, custom templates, `prepend` / `append` group addons and attached icons/buttons via `hubPrepend` / `hubAppend`; the `buttons` / `checkbox` / `radio` formats are **deprecated** → use `hub-segmented`), `hub-datepicker` (single & range at any granularity from a year to a second, time picking, min/max down to the minute, keyboard nav, i18n), `hub-timepicker` (a time of day as `HH:MM`, on the platform's own time control, with `min` / `max` / `step`), `hub-file-input` (drag & drop, clipboard paste, type/size limits, previews as a list, as tiles or inside the field together with the files a record already has, optional upload progress).
99
100
  - **Automatic error display** — bind a field and its control errors render below it; `fieldset[hubFieldset]`, `form[hubForm]` and `hub-legend` surface group- and form-level (cross-field) errors the same way, with zero wiring.
100
101
  - **Containers** — `fieldset[hubFieldset]` (or the `<hub-fieldset>` element) / `form[hubForm]` group fields and show their group errors; `hub-legend` renders an accessible legend.
101
102
  - **Configurable** — `provideHubForms({ … })` sets the invalid-feedback templates, datepicker locale/labels, file-input labels and more, app-wide or per instance.
@@ -195,6 +196,57 @@ string, so asking it to carry markup would drop the markup silently.
195
196
  <hub-input formControlName="darkMode" type="switch" label="Dark mode" />
196
197
  ```
197
198
 
199
+ #### Colour fields
200
+
201
+ `type="color"` is a text field for the hex code, with the colour in a square at its start. The square
202
+ opens the browser's picker. The text takes a colour typed with or without `#`, in three or six digits,
203
+ and the form stores it as lowercase `#rrggbb`, the one notation the native picker reads. Invalid text
204
+ leaves the value alone and goes back to the last valid colour on blur.
205
+
206
+ Give the field a list of colours and it becomes a grid of swatches, one row the height of a field:
207
+
208
+ ```html
209
+ <hub-input formControlName="status" type="color" label="Status colour" [swatches]="palettes.status" />
210
+
211
+ <hub-input
212
+ formControlName="tag"
213
+ type="color"
214
+ label="Tag"
215
+ [swatches]="['#ef4444', { value: '#22c55e', label: 'Done' }]"
216
+ [allowCustomColor]="false"
217
+ />
218
+ ```
219
+
220
+ ```ts
221
+ import { HUB_COLOR_PALETTES } from 'ng-hub-ui-forms';
222
+
223
+ readonly palettes = HUB_COLOR_PALETTES;
224
+ ```
225
+
226
+ - A swatch is any CSS colour that `parseColor` from `ng-hub-ui-utils` reads (hex, `rgb()`, `hsl()`,
227
+ `oklch()`, `oklab()`, a named colour), bare or as `{ value, label }`. The label is what a screen reader
228
+ says, so name the colours that have a name. The control receives the string exactly as written. An
229
+ entry that is not a colour is dropped, with a warning in development builds.
230
+ - The last cell opens the native picker for a colour outside the list. `[allowCustomColor]="false"`
231
+ leaves it out for a closed palette; `customColorLabel` names it.
232
+ - The cells share the row down to `--hub-input-swatch-min-width`, then wrap onto more rows. Once they
233
+ wrap the field drops its box; `--hub-input-swatch-wrapped-border-color` and `-wrapped-bg` bring it back.
234
+ - The grid is a radio group named by the field label, with one Tab stop; the arrows, Home and End move
235
+ the selection.
236
+ - `HUB_COLOR_PALETTES` has five frozen lists of lowercase hex, each swatch named in English: `tailwind`
237
+ (17), `material` (19), `pastel` (17), `neutral` (11) and `status` (5).
238
+
239
+ Which field is drawn:
240
+
241
+ | `swatches` | Application palette (`provideHubForms`) | Result |
242
+ | ---------------- | --------------------------------------- | -------------------------------- |
243
+ | `null` (default) | none (default) | hex field |
244
+ | `null` | a list | grid with the application palette |
245
+ | `[]` | any | hex field |
246
+ | a list | any | grid with the field's list |
247
+
248
+ A list in which no entry is a colour also leaves the hex field.
249
+
198
250
  #### Icon affix & typeahead (search boxes)
199
251
 
200
252
  Project a leading / trailing icon **inside** the field, emit a debounced term on every keystroke, and let the field render its own clear button:
@@ -597,7 +649,7 @@ Whatever the uploader reports on `done` is kept on the item, so the ids the serv
597
649
  const uploadedIds = fileInput.files().map((item) => (item.response as { id: string }).id);
598
650
  ```
599
651
 
600
- Customize it without forking the template: the `--hub-file-input-*` tokens (every icon is a swappable CSS mask), the `hub-file-input-theme(...)` mixin, and three projection slots.
652
+ Customize it without forking the template: the `--hub-file-input-*` tokens (every icon is a swappable CSS mask), the `hub-file-input-theme(...)` mixin, and three projection slots. `hubFileIcon` applies to `preview="list"`; the tiles of `grid` and `inline` draw the family icons described below.
601
653
 
602
654
  ```html
603
655
  <hub-file-input formControlName="attachments" [multiple]="true">
@@ -607,6 +659,73 @@ Customize it without forking the template: the `--hub-file-input-*` tokens (ever
607
659
  </hub-file-input>
608
660
  ```
609
661
 
662
+ #### Inline preview and stored files
663
+
664
+ `preview="inline"` puts the file inside the field. One tile fills it: the image when the browser can
665
+ paint it, otherwise the icon of its family and its name. Hover, keyboard focus or a drag over the tile
666
+ raise a "Replace" pill, and a button in the corner removes the file. With `multiple` the tiles form a
667
+ grid inside the field that ends in a tile for adding more, and `maxFiles` adds a "3 of 5 files"
668
+ counter; at the limit the add tile goes away.
669
+
670
+ `currentFile` shows what the record already has:
671
+
672
+ ```html
673
+ <hub-file-input
674
+ formControlName="logo"
675
+ label="Logo"
676
+ accept="image/*"
677
+ preview="inline"
678
+ [currentFile]="company.logoUrl"
679
+ (currentFileRemoved)="markForDeletion($event)"
680
+ />
681
+
682
+ <hub-file-input
683
+ formControlName="contract"
684
+ label="Signed contract"
685
+ preview="inline"
686
+ [currentFile]="{ url: '/api/contracts/42/file', name: 'contract.pdf', type: 'application/pdf' }"
687
+ />
688
+ ```
689
+
690
+ - A bare URL gives the name from its last segment, when that has an extension, and the type from a
691
+ `data:` URL. Pass a `HubCurrentFile` when the URL reveals neither, and a list with `multiple`.
692
+ - A stored file is only shown: the form value stays a `File`, a `File[]` or `null`. When the user
693
+ removes it, or replaces it with a picked file, `currentFileRemoved` emits it. That is the moment to
694
+ delete it on the server.
695
+ - A click on a tile opens its file: a picked image in a native `<dialog>`, a stored file or any other
696
+ picked file in a new tab. Delete or Backspace on a focused tile removes it.
697
+ - `readonly` keeps the files in view and openable but blocks every change. `[clearable]="false"` keeps
698
+ the user from removing files. `[imagePreview]="false"` draws every file as its icon and creates no
699
+ object URLs.
700
+
701
+ `preview="grid"` draws the same tiles under the dropzone. An avatar takes five tokens:
702
+
703
+ ```css
704
+ .avatar-field {
705
+ --hub-file-input-inline-width: 8rem;
706
+ --hub-file-input-inline-aspect-ratio: 1;
707
+ --hub-file-input-tile-radius: 50%;
708
+ --hub-file-input-tile-fit: cover;
709
+ --hub-file-input-tile-padding: 0;
710
+ }
711
+ ```
712
+
713
+ Each family icon (`pdf`, `document`, `spreadsheet`, `presentation`, `archive`, `audio`, `video`, `code`,
714
+ `image`, `generic`) is a mask token, so replacing one is one line, and the tile's `data-file-kind`
715
+ attribute scopes a colour to one family:
716
+
717
+ ```css
718
+ .my-form {
719
+ --hub-file-input-kind-pdf-icon: url('/icons/pdf.svg');
720
+ }
721
+
722
+ .my-form .hub-file-input__tile[data-file-kind='pdf'] {
723
+ --hub-file-input-kind-icon-color: #dc2626;
724
+ }
725
+ ```
726
+
727
+ The built-in drawings are [Bootstrap Icons](https://icons.getbootstrap.com) 1.13.1, under the MIT License.
728
+
610
729
  #### Reproducing your own dropzone
611
730
 
612
731
  The dropzone is built from a glyph, an invitation and a browse action, each themeable on its own — so a design system reproduces its own without forking the template.
@@ -722,6 +841,18 @@ field once it is touched and valid. The invalid state is unaffected — it is al
722
841
  automatic; only success is gated behind this flag. A per-field `showValid` input
723
842
  overrides the global default.
724
843
 
844
+ `color` sets an application palette for every colour field without `swatches` of its own (there is
845
+ none by default) and the two accessible names the colour field adds: `customColorLabel`
846
+ (`'Custom color'`) for the grid's last cell and `pickerLabel` (`'Choose color'`) for the hex field's
847
+ square. `fileInput` carries the file-input labels, among them `removeFile(name)`, `open(name)`,
848
+ `replace`, `replaceFile(name)`, `close`, `count(count, max)` and `currentFile`.
849
+
850
+ ```ts
851
+ provideHubForms({
852
+ color: { swatches: HUB_COLOR_PALETTES.tailwind, customColorLabel: 'Other colour' }
853
+ });
854
+ ```
855
+
725
856
  ---
726
857
 
727
858
  ## 🎨 Styling
@@ -753,6 +884,11 @@ hub-input {
753
884
  }
754
885
  ```
755
886
 
887
+ **Colour field** — `--hub-input-color-size` is the width of the hex field's square; its height is
888
+ always the field's, and the default, the field's inner height, keeps it square. The grid of swatches
889
+ reads the `--hub-input-swatch-*` tokens, and `--hub-input-swatch-mark-color` forces one colour for every
890
+ check mark (unset, each mark is black or white, whichever reads on its swatch).
891
+
756
892
  **`hub-slider`** — `--hub-slider-track-fill` takes a full `<image>` (e.g. a `linear-gradient(to right, …)`) for the filled part of the track, which renders intact clipped to the current percentage; `--hub-slider-value-space` is the value-bubble headroom and collapses to `0` on a `[showValue]="false"` (flush) slider:
757
893
 
758
894
  ```css
@@ -799,7 +935,9 @@ import { HubSignalFieldControl, hubSignalErrorMessages } from 'ng-hub-ui-forms/s
799
935
 
800
936
  ## ♿ Accessibility
801
937
 
802
- - Labels are associated with their control (`for`/`id`); required fields are marked.
938
+ - Labels are associated with their control (`for`/`id`); required fields are marked. The colour
939
+ grid is a radio group, which a `<label for>` cannot name, so it points at the label's `id` through
940
+ `aria-labelledby`.
803
941
  - **`labelType="visually-hidden"` names a control that has no room for a visible label.** A
804
942
  toolbar search box or a compact grid cell cannot repeat the same word down every row, and the
805
943
  alternative was a control with no accessible name at all — a placeholder is not a name. The