ng-hub-ui-forms 22.5.0 → 22.7.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.
Binary file
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "ng-hub-ui-forms",
3
- "version": "22.5.0",
3
+ "version": "22.7.0",
4
4
  "license": "MIT",
5
- "description": "Accessible, signal-based form fields for Angular (input, textarea, slider, select, datepicker) with automatic error display for controls, FormGroups and FormArrays. Reactive Forms today, Signal Forms ready. Part of the ng-hub-ui family.",
5
+ "description": "Accessible, signal-based form fields for Angular (input, textarea, slider, segmented, select, datepicker, file input with drag & drop and upload progress) with automatic error display for controls, FormGroups and FormArrays. Reactive Forms today, Signal Forms ready. Part of the ng-hub-ui family.",
6
6
  "author": "Carlos Morcillo <carlos.morcillo@me.com> (https://www.carlosmorcillo.com)",
7
7
  "repository": {
8
8
  "type": "git",
@@ -18,6 +18,10 @@
18
18
  "slider",
19
19
  "select",
20
20
  "datepicker",
21
+ "file-input",
22
+ "file-upload",
23
+ "drag-and-drop",
24
+ "dropzone",
21
25
  "validation",
22
26
  "reactive-forms",
23
27
  "signal-forms",
@@ -90,6 +90,123 @@
90
90
  // Input · color format
91
91
  --hub-input-color-size: 2.5rem;
92
92
 
93
+ // ── File input ────────────────────────────────────────────────────────────
94
+ // Dropzone surface
95
+ --hub-file-input-bg: var(--hub-sys-surface-page, #fff);
96
+ --hub-file-input-color: var(--hub-sys-text-primary, #212529);
97
+ --hub-file-input-border-width: var(--hub-ref-border-width, 1px);
98
+ --hub-file-input-border-style: dashed;
99
+ --hub-file-input-border-color: var(--hub-sys-border-color-default, #dee2e6);
100
+ --hub-file-input-border-radius: var(--hub-input-border-radius);
101
+ --hub-file-input-padding-x: var(--hub-ref-space-3, 1rem);
102
+ --hub-file-input-padding-y: var(--hub-ref-space-4, 1.5rem);
103
+ --hub-file-input-min-height: 7rem;
104
+ --hub-file-input-gap: var(--hub-ref-space-2, 0.5rem);
105
+ --hub-file-input-transition: var(--hub-form-transition);
106
+ --hub-file-input-hover-border-color: var(--hub-sys-color-primary, #0d6efd);
107
+ --hub-file-input-hover-bg: var(--hub-sys-surface-elevated, #f8f9fa);
108
+ --hub-file-input-dragover-bg: color-mix(in oklch, var(--hub-sys-color-primary, #0d6efd) 8%, transparent);
109
+ --hub-file-input-dragover-border-color: var(--hub-sys-color-primary, #0d6efd);
110
+ --hub-file-input-focus-border-color: var(--hub-input-focus-border-color);
111
+ --hub-file-input-focus-box-shadow: var(--hub-input-focus-box-shadow);
112
+
113
+ // Dropzone copy. The prompt is a row by default (invitation and browse action side by
114
+ // side); `--hub-file-input-prompt-direction: column` stacks them, which is what a
115
+ // dropzone carrying a `dropSubtext` wants.
116
+ --hub-file-input-prompt-color: var(--hub-sys-text-muted, #6c757d);
117
+ --hub-file-input-prompt-font-size: var(--hub-ref-font-size-base, 1rem);
118
+ --hub-file-input-prompt-direction: row;
119
+ // `baseline` aligns a row of text; a column prompt wants `center`.
120
+ --hub-file-input-prompt-align: baseline;
121
+ --hub-file-input-prompt-gap: 0.25em;
122
+ --hub-file-input-drop-text-color: inherit;
123
+ --hub-file-input-drop-text-font-size: inherit;
124
+ --hub-file-input-drop-text-font-weight: inherit;
125
+ --hub-file-input-drop-subtext-color: var(--hub-file-input-hint-color);
126
+ --hub-file-input-drop-subtext-font-size: var(--hub-file-input-hint-font-size);
127
+ --hub-file-input-hint-color: var(--hub-sys-text-muted, #6c757d);
128
+ --hub-file-input-hint-font-size: var(--hub-ref-font-size-sm, 0.875rem);
129
+
130
+ // Browse action — a transparent, underlined text affordance by default. Give it a
131
+ // background, padding and a radius to render it as a button instead.
132
+ --hub-file-input-browse-color: var(--hub-sys-color-primary, #0d6efd);
133
+ --hub-file-input-browse-font-weight: var(--hub-ref-font-weight-medium, 500);
134
+ --hub-file-input-browse-font-size: inherit;
135
+ --hub-file-input-browse-text-decoration: underline;
136
+ --hub-file-input-browse-bg: transparent;
137
+ --hub-file-input-browse-hover-bg: var(--hub-file-input-browse-bg);
138
+ --hub-file-input-browse-padding-x: 0;
139
+ --hub-file-input-browse-padding-y: 0;
140
+ --hub-file-input-browse-radius: 0;
141
+ --hub-file-input-browse-gap: var(--hub-ref-space-2, 0.5rem);
142
+ --hub-file-input-browse-margin-top: 0;
143
+ // Optional leading glyph of the browse action — OFF until `-display` is switched on and
144
+ // a mask is supplied, mirroring the leading-icon contract of the rest of the family.
145
+ --hub-file-input-browse-icon: none;
146
+ --hub-file-input-browse-icon-display: none;
147
+ --hub-file-input-browse-icon-size: 1.125em;
148
+
149
+ // Glyphs — every icon is a swappable mask, tinted by its `-color` token. Replace the
150
+ // `url(…)` to change the drawing without touching the template; project an
151
+ // `<ng-template hubFileIcon>` to replace the per-file glyph with a component.
152
+ --hub-file-input-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='black' d='M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5'/%3E%3Cpath fill='black' d='M7.646.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 1.707V10.5a.5.5 0 0 1-1 0V1.707L5.354 3.854a.5.5 0 1 1-.708-.708z'/%3E%3C/svg%3E");
153
+ --hub-file-input-icon-size: 2rem;
154
+ --hub-file-input-icon-color: var(--hub-sys-text-muted, #6c757d);
155
+ // The glyph can sit on a medallion: a tinted, rounded surface behind it. Off by default
156
+ // (transparent, square, exactly the glyph's box), so the dropzone renders unchanged.
157
+ --hub-file-input-icon-bg: transparent;
158
+ --hub-file-input-icon-chip-size: var(--hub-file-input-icon-size);
159
+ --hub-file-input-icon-chip-radius: 0;
160
+ --hub-file-input-file-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='black' d='M4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4.414A1.5 1.5 0 0 0 13.56 3.35L10.65.44A1.5 1.5 0 0 0 9.586 0zm5.5 1.5v2a1 1 0 0 0 1 1h2z'/%3E%3C/svg%3E");
161
+ --hub-file-input-file-icon-size: 1.5rem;
162
+ --hub-file-input-file-icon-color: var(--hub-sys-text-muted, #6c757d);
163
+ --hub-file-input-remove-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='black' d='M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708'/%3E%3C/svg%3E");
164
+ --hub-file-input-cancel-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='black' d='M5 3.5h6a1.5 1.5 0 0 1 1.5 1.5v6a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 11V5A1.5 1.5 0 0 1 5 3.5'/%3E%3C/svg%3E");
165
+ --hub-file-input-retry-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='black' d='M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z'/%3E%3Cpath fill='black' d='M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466'/%3E%3C/svg%3E");
166
+ --hub-file-input-done-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='black' stroke-width='2' d='M3 8.5 6.5 12 13 4.5'/%3E%3C/svg%3E");
167
+ --hub-file-input-done-color: var(--hub-sys-color-success, #198754);
168
+
169
+ // Per-file action buttons
170
+ --hub-file-input-action-size: 1rem;
171
+ --hub-file-input-action-color: var(--hub-sys-text-muted, #6c757d);
172
+ --hub-file-input-action-hover-color: var(--hub-sys-text-primary, #212529);
173
+ --hub-file-input-remove-hover-color: var(--hub-sys-color-danger, #dc3545);
174
+
175
+ // File list
176
+ --hub-file-input-list-gap: var(--hub-ref-space-2, 0.5rem);
177
+ --hub-file-input-list-margin-top: var(--hub-ref-space-2, 0.5rem);
178
+ --hub-file-input-item-bg: var(--hub-sys-surface-elevated, #f8f9fa);
179
+ --hub-file-input-item-color: var(--hub-sys-text-primary, #212529);
180
+ --hub-file-input-item-border-color: var(--hub-sys-border-color-default, #dee2e6);
181
+ --hub-file-input-item-border-radius: var(--hub-ref-radius-sm, 0.25rem);
182
+ --hub-file-input-item-padding-x: var(--hub-ref-space-2, 0.5rem);
183
+ --hub-file-input-item-padding-y: var(--hub-ref-space-2, 0.5rem);
184
+ --hub-file-input-item-gap: var(--hub-ref-space-2, 0.5rem);
185
+ --hub-file-input-name-font-size: var(--hub-ref-font-size-sm, 0.875rem);
186
+ --hub-file-input-size-color: var(--hub-sys-text-muted, #6c757d);
187
+ --hub-file-input-size-font-size: var(--hub-ref-font-size-sm, 0.875rem);
188
+ --hub-file-input-error-color: var(--hub-sys-color-danger, #dc3545);
189
+ --hub-file-input-error-font-size: var(--hub-ref-font-size-sm, 0.875rem);
190
+ --hub-file-input-clear-color: var(--hub-sys-text-muted, #6c757d);
191
+ --hub-file-input-clear-font-size: var(--hub-ref-font-size-sm, 0.875rem);
192
+
193
+ // Thumbnail
194
+ --hub-file-input-thumb-size: 2.5rem;
195
+ --hub-file-input-thumb-radius: var(--hub-ref-radius-sm, 0.25rem);
196
+ --hub-file-input-thumb-bg: var(--hub-sys-surface-page, #fff);
197
+
198
+ // Grid preview
199
+ --hub-file-input-grid-min-width: 8rem;
200
+ --hub-file-input-grid-gap: var(--hub-ref-space-2, 0.5rem);
201
+ --hub-file-input-grid-thumb-height: 6rem;
202
+
203
+ // Upload progress
204
+ --hub-file-input-progress-height: 0.25rem;
205
+ --hub-file-input-progress-radius: var(--hub-ref-radius-pill, 50rem);
206
+ --hub-file-input-progress-track-bg: var(--hub-sys-border-color-default, #dee2e6);
207
+ --hub-file-input-progress-bar-bg: var(--hub-sys-color-primary, #0d6efd);
208
+ --hub-file-input-progress-indeterminate-duration: 1.2s;
209
+
93
210
  // ── Textarea (inherits input) ──────────────────────────────────────────────
94
211
  --hub-textarea-padding-x: var(--hub-input-padding-x);
95
212
  --hub-textarea-padding-y: var(--hub-input-padding-y);
package/styles/index.scss CHANGED
@@ -13,6 +13,7 @@
13
13
  @use 'field';
14
14
 
15
15
  // One-call theming mixins (opt-in; forwarded so consumers can `@use` them from the package root).
16
- // `hub-forms-theme` themes the shared field chrome; `hub-segmented-theme` tunes `<hub-segmented>`.
16
+ // `hub-forms-theme` themes the shared field chrome; the others tune a single control.
17
17
  @forward 'mixins/forms-theme';
18
18
  @forward 'mixins/segmented-theme';
19
+ @forward 'mixins/file-input-theme';
@@ -0,0 +1,199 @@
1
+ // ─────────────────────────────────────────────────────────────────────────
2
+ // hub-file-input-theme — one-call theming for `<hub-file-input>`
3
+ // ─────────────────────────────────────────────────────────────────────────
4
+ //
5
+ // Override any of the `--hub-file-input-*` design tokens in a single include.
6
+ // Every parameter is OPTIONAL and defaults to `null`: only the parameters you
7
+ // pass are emitted, so the rest keep the component's defaults. Token-based and
8
+ // self-contained (no Bootstrap dependencies).
9
+ //
10
+ // The glyph parameters take a CSS `<image>` — typically a `url("data:image/svg+xml,…")`
11
+ // — used as a MASK, so the drawing is tinted by its matching colour token and needs
12
+ // no fill of its own. To render a real icon component instead, project an
13
+ // `<ng-template hubFileIcon>` into the field.
14
+ //
15
+ // Apply it inside any selector that wraps the field — the host element, a
16
+ // container, a route, or `:root` for a global theme.
17
+ //
18
+ // @example — a brand-accented dropzone with a solid border and a custom upload glyph:
19
+ //
20
+ // @use 'ng-hub-ui-forms/styles' as *;
21
+ //
22
+ // .attachments {
23
+ // @include hub-file-input-theme(
24
+ // $border-style: solid,
25
+ // $border-radius: 0.75rem,
26
+ // $dragover-border-color: var(--hub-sys-color-brand),
27
+ // $browse-color: var(--hub-sys-color-brand),
28
+ // $icon: url('/assets/upload.svg'),
29
+ // $icon-color: var(--hub-sys-color-brand),
30
+ // $progress-bar-bg: var(--hub-sys-color-brand)
31
+ // );
32
+ // }
33
+ //
34
+ // scss-docs-start hub-file-input-theme
35
+ @mixin hub-file-input-theme(
36
+ // ── Dropzone surface ──
37
+ $bg: null, // dropzone background
38
+ $color: null, // dropzone text colour
39
+ $border-width: null, // dropzone border width
40
+ $border-style: null, // dropzone border style (defaults to `dashed`)
41
+ $border-color: null, // dropzone border colour at rest
42
+ $border-radius: null, // dropzone corner radius
43
+ $padding-x: null, // dropzone horizontal padding
44
+ $padding-y: null, // dropzone vertical padding
45
+ $min-height: null, // dropzone minimum height
46
+ $gap: null, // gutter between icon, prompt and hint
47
+ // ── States ──
48
+ $hover-bg: null, // background on hover
49
+ $hover-border-color: null, // border colour on hover
50
+ $dragover-bg: null, // background while a drag hovers the zone
51
+ $dragover-border-color: null, // border colour while a drag hovers the zone
52
+ // ── Copy ──
53
+ $prompt-color: null, // "drag files here" colour
54
+ $browse-color: null, // "browse files" colour
55
+ $hint-color: null, // constraints-hint colour
56
+ // ── Glyphs (CSS masks) ──
57
+ $icon: null, // upload glyph shown in the dropzone
58
+ $icon-size: null, // upload glyph size
59
+ $icon-color: null, // upload glyph tint
60
+ $file-icon: null, // per-file glyph, for files with no thumbnail
61
+ $file-icon-color: null, // per-file glyph tint
62
+ $remove-icon: null, // remove-button glyph
63
+ $cancel-icon: null, // cancel-upload glyph
64
+ $retry-icon: null, // retry-upload glyph
65
+ $done-icon: null, // finished-upload tick
66
+ $done-color: null, // finished-upload tick + item border colour
67
+ // ── File list ──
68
+ $item-bg: null, // background of a file row / tile
69
+ $item-border-color: null, // border of a file row / tile
70
+ $item-border-radius: null, // corner radius of a file row / tile
71
+ $thumb-size: null, // thumbnail size in list preview
72
+ $grid-min-width: null, // minimum tile width in grid preview
73
+ $grid-thumb-height: null, // thumbnail height in grid preview
74
+ // ── Upload progress ──
75
+ $progress-height: null, // progress-bar thickness
76
+ $progress-track-bg: null, // progress-bar track colour
77
+ $progress-bar-bg: null // progress-bar fill colour
78
+ ) {
79
+ // Dropzone surface
80
+ @if $bg != null {
81
+ --hub-file-input-bg: #{$bg};
82
+ }
83
+ @if $color != null {
84
+ --hub-file-input-color: #{$color};
85
+ }
86
+ @if $border-width != null {
87
+ --hub-file-input-border-width: #{$border-width};
88
+ }
89
+ @if $border-style != null {
90
+ --hub-file-input-border-style: #{$border-style};
91
+ }
92
+ @if $border-color != null {
93
+ --hub-file-input-border-color: #{$border-color};
94
+ }
95
+ @if $border-radius != null {
96
+ --hub-file-input-border-radius: #{$border-radius};
97
+ }
98
+ @if $padding-x != null {
99
+ --hub-file-input-padding-x: #{$padding-x};
100
+ }
101
+ @if $padding-y != null {
102
+ --hub-file-input-padding-y: #{$padding-y};
103
+ }
104
+ @if $min-height != null {
105
+ --hub-file-input-min-height: #{$min-height};
106
+ }
107
+ @if $gap != null {
108
+ --hub-file-input-gap: #{$gap};
109
+ }
110
+
111
+ // States
112
+ @if $hover-bg != null {
113
+ --hub-file-input-hover-bg: #{$hover-bg};
114
+ }
115
+ @if $hover-border-color != null {
116
+ --hub-file-input-hover-border-color: #{$hover-border-color};
117
+ }
118
+ @if $dragover-bg != null {
119
+ --hub-file-input-dragover-bg: #{$dragover-bg};
120
+ }
121
+ @if $dragover-border-color != null {
122
+ --hub-file-input-dragover-border-color: #{$dragover-border-color};
123
+ }
124
+
125
+ // Copy
126
+ @if $prompt-color != null {
127
+ --hub-file-input-prompt-color: #{$prompt-color};
128
+ }
129
+ @if $browse-color != null {
130
+ --hub-file-input-browse-color: #{$browse-color};
131
+ }
132
+ @if $hint-color != null {
133
+ --hub-file-input-hint-color: #{$hint-color};
134
+ }
135
+
136
+ // Glyphs
137
+ @if $icon != null {
138
+ --hub-file-input-icon: #{$icon};
139
+ }
140
+ @if $icon-size != null {
141
+ --hub-file-input-icon-size: #{$icon-size};
142
+ }
143
+ @if $icon-color != null {
144
+ --hub-file-input-icon-color: #{$icon-color};
145
+ }
146
+ @if $file-icon != null {
147
+ --hub-file-input-file-icon: #{$file-icon};
148
+ }
149
+ @if $file-icon-color != null {
150
+ --hub-file-input-file-icon-color: #{$file-icon-color};
151
+ }
152
+ @if $remove-icon != null {
153
+ --hub-file-input-remove-icon: #{$remove-icon};
154
+ }
155
+ @if $cancel-icon != null {
156
+ --hub-file-input-cancel-icon: #{$cancel-icon};
157
+ }
158
+ @if $retry-icon != null {
159
+ --hub-file-input-retry-icon: #{$retry-icon};
160
+ }
161
+ @if $done-icon != null {
162
+ --hub-file-input-done-icon: #{$done-icon};
163
+ }
164
+ @if $done-color != null {
165
+ --hub-file-input-done-color: #{$done-color};
166
+ }
167
+
168
+ // File list
169
+ @if $item-bg != null {
170
+ --hub-file-input-item-bg: #{$item-bg};
171
+ }
172
+ @if $item-border-color != null {
173
+ --hub-file-input-item-border-color: #{$item-border-color};
174
+ }
175
+ @if $item-border-radius != null {
176
+ --hub-file-input-item-border-radius: #{$item-border-radius};
177
+ }
178
+ @if $thumb-size != null {
179
+ --hub-file-input-thumb-size: #{$thumb-size};
180
+ }
181
+ @if $grid-min-width != null {
182
+ --hub-file-input-grid-min-width: #{$grid-min-width};
183
+ }
184
+ @if $grid-thumb-height != null {
185
+ --hub-file-input-grid-thumb-height: #{$grid-thumb-height};
186
+ }
187
+
188
+ // Upload progress
189
+ @if $progress-height != null {
190
+ --hub-file-input-progress-height: #{$progress-height};
191
+ }
192
+ @if $progress-track-bg != null {
193
+ --hub-file-input-progress-track-bg: #{$progress-track-bg};
194
+ }
195
+ @if $progress-bar-bg != null {
196
+ --hub-file-input-progress-bar-bg: #{$progress-bar-bg};
197
+ }
198
+ }
199
+ // scss-docs-end hub-file-input-theme