@atelier-ui/angular 0.2.13 → 0.2.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atelier-ui/angular",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
4
4
  "description": "Atelier component library for Angular — LLM-optimised, accessible, design-token-driven",
5
5
  "keywords": [
6
6
  "angular",
@@ -38,6 +38,18 @@
38
38
  --ui-letter-spacing-wide: 0.01em;
39
39
  --ui-letter-spacing-uppercase: 0.08em;
40
40
  --ui-font-size-3xl: 2.25rem;
41
+
42
+ /* ── Control heights ────────────────────────────────────────
43
+ * The height of an interactive control is the primitive; its block padding
44
+ * is DERIVED from it (ADR-0041):
45
+ * padding-block: calc((height - line-height * font-size) / 2 - border)
46
+ * Stating the height and deriving the padding is what makes an input and a
47
+ * button the same height. Stating the padding and hoping, which is what the
48
+ * library did until 2026-08-26, made them 6px apart.
49
+ */
50
+ --ui-control-height-sm: 2rem;
51
+ --ui-control-height-md: 2.5rem;
52
+ --ui-control-height-lg: 3rem;
41
53
  --ui-font-weight-normal: 400;
42
54
  --ui-font-weight-medium: 500;
43
55
  --ui-font-weight-semibold: 600;
@@ -167,6 +179,18 @@
167
179
  --ui-radius-xl: 1.25rem; /* was 1rem */
168
180
  --ui-radius-full: 9999px;
169
181
 
182
+ /* ── Border widths ──────────────────────────────────────────────────────────
183
+ * Two steps, because the library only ever needed two: the hairline that
184
+ * separates surfaces (133 uses) and the heavier weight that marks a control
185
+ * (12 uses — the avatar status ring, the stepper circle, the checkbox mark,
186
+ * the table header rule). The 1.5px weight nine control outlines used is
187
+ * folded into `thick`: a half pixel renders blurry at 1x device-pixel-ratio
188
+ * and the two weights were never distinguishable side by side.
189
+ * Not here on purpose: AtlToast's 4px accent bar and AtlRadio's 6px dot are
190
+ * graphic devices drawn with `border-width`, not border weights. See ADR-0047. */
191
+ --ui-border-width: 1px;
192
+ --ui-border-width-thick: 2px;
193
+
170
194
  /* Spacing */
171
195
  --ui-spacing-1: 0.25rem;
172
196
  --ui-spacing-2: 0.5rem;
@@ -23,7 +23,7 @@ interface AtlComboboxOption {
23
23
  label: string;
24
24
  disabled?: boolean;
25
25
  }
26
- type AtlIconName = 'success' | 'warning' | 'danger' | 'info' | 'error' | 'chevron-up' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'sort-asc' | 'sort-desc' | 'arrow-right' | 'arrow-left' | 'copy' | 'paste' | 'add' | 'edit' | 'delete' | 'close' | 'more' | 'default-toast';
26
+ type AtlIconName = 'success' | 'check' | 'warning' | 'danger' | 'info' | 'error' | 'chevron-up' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'sort-asc' | 'sort-desc' | 'arrow-right' | 'arrow-left' | 'copy' | 'paste' | 'add' | 'edit' | 'delete' | 'close' | 'more' | 'person' | 'default-toast';
27
27
  type AtlIconSize = 'sm' | 'md' | 'lg';
28
28
  type AtlChatVariant = 'drawer' | 'popup' | 'inline';
29
29
  type AtlChatStatus = 'idle' | 'streaming' | 'error';
@@ -145,7 +145,42 @@ declare class AtlBadge {
145
145
  }
146
146
 
147
147
  /**
148
- * Pictogram glyph icon. 21 named variants matching the Figma `AtlIcon`
148
+ * Icon geometry the single source of truth for what every AtlIcon draws.
149
+ *
150
+ * Before this file, the library had four ways to draw the same thing: AtlIcon
151
+ * rendered Unicode glyphs and no component used it; eleven components drew 14
152
+ * inline `<svg>`s of their own; and two stylesheets put a literal `✕` in a CSS
153
+ * `content:`. The result was one concept with several drawings — `close` existed
154
+ * as an identical two-line SVG in five components, as a *differently drawn* X in
155
+ * AtlStepper, as `'×'` in AtlIcon and as `'✕'` in CSS, and `check` existed as
156
+ * three unrelated paths. Nothing gated it. See ADR-0046.
157
+ *
158
+ * Conventions, which every entry follows so a consumer can scale and colour icons
159
+ * without knowing which one they got:
160
+ *
161
+ * - **One viewBox: `0 0 24 24`.** Rendered size comes from the `size` prop, never
162
+ * from the geometry, so `sm`/`md`/`lg` are the same shape at three scales.
163
+ * - **`stroke` icons** are drawn with `currentColor`, `stroke-width: 2`, round caps
164
+ * and joins, and no fill. **`fill` icons** are solid `currentColor` silhouettes.
165
+ * Mixing the two inside one icon is not allowed — it reads wrong at small sizes.
166
+ * - **Paths only** (no `<line>`/`<rect>`/`<polyline>`), so the renderer in each
167
+ * framework is a single `<path>` loop rather than a per-primitive switch.
168
+ *
169
+ * The seven icons the library itself uses (`close`, `check`, `chevron-down`,
170
+ * `copy`, `person`, `sort-asc`, `sort-desc`) keep the geometry the components
171
+ * already shipped, normalised to the 24 viewBox. The rest are the same idiom.
172
+ */
173
+ type AtlIconKind = 'stroke' | 'fill';
174
+ interface AtlIconGeometry {
175
+ /** How the paths are painted. Never mixed within one icon. */
176
+ kind: AtlIconKind;
177
+ /** Path `d` values, drawn in order, all in the 0 0 24 24 viewBox. */
178
+ paths: string[];
179
+ }
180
+
181
+ /**
182
+ * Vector icon. 23 named variants, drawn from the geometry in `icons.ts` so the
183
+ * same shape is used everywhere it appears. Matches the Figma `AtlIcon`
149
184
  * component set. Decorative by default; pass `label` to announce a meaning
150
185
  * to assistive tech.
151
186
  *
@@ -159,7 +194,10 @@ declare class AtlIcon {
159
194
  readonly name: _angular_core.InputSignal<AtlIconName>;
160
195
  readonly size: _angular_core.InputSignal<AtlIconSize>;
161
196
  readonly label: _angular_core.InputSignal<string | undefined>;
162
- protected readonly glyph: _angular_core.Signal<string>;
197
+ protected readonly viewBox = "0 0 24 24";
198
+ protected readonly strokeWidth = 2;
199
+ protected readonly geometry: _angular_core.Signal<AtlIconGeometry>;
200
+ protected readonly isStroke: _angular_core.Signal<boolean>;
163
201
  protected readonly hostClasses: _angular_core.Signal<string>;
164
202
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AtlIcon, never>;
165
203
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<AtlIcon, "atl-icon", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
@@ -304,6 +342,7 @@ interface AtlRadioGroupContext {
304
342
  readonly value: WritableSignal<string>;
305
343
  readonly name: Signal<string>;
306
344
  readonly disabled: Signal<boolean>;
345
+ readonly readonly: Signal<boolean>;
307
346
  select(v: string): void;
308
347
  markTouched(): void;
309
348
  registerItem(item: RadioItem): void;
@@ -348,6 +387,13 @@ declare class AtlRadio implements RadioItem, OnInit, OnDestroy {
348
387
  ngOnInit(): void;
349
388
  ngOnDestroy(): void;
350
389
  /** @internal */
390
+ /**
391
+ * readonly is enforced by cancelling the click, not only by the group's
392
+ * `select()` guard: HTML ignores `readonly` on a radio, so the input would flip
393
+ * its own DOM state while the model stayed put — and with OnPush nothing
394
+ * re-renders to correct it. See ADR-0045.
395
+ */
396
+ protected onClick(event: Event): void;
351
397
  protected onChange(): void;
352
398
  /** @internal */
353
399
  protected onBlur(): void;
@@ -380,6 +426,12 @@ declare class AtlRadioGroup implements FormValueControl<string>, AtlRadioGroupCo
380
426
  readonly touched: _angular_core.ModelSignal<boolean>;
381
427
  /** Whether the group is disabled. Bound by [formField] directive. */
382
428
  readonly disabled: _angular_core.InputSignal<boolean>;
429
+ /**
430
+ * Whether the selection can be read but not changed. Enforced by the guards in
431
+ * `select()` and `onKeydown()`: HTML ignores `readonly` on a radio input, so the
432
+ * group has to block the change itself. See ADR-0045.
433
+ */
434
+ readonly readonly: _angular_core.InputSignal<boolean>;
383
435
  /** Whether the group has validation errors. Bound by [formField] directive. */
384
436
  readonly invalid: _angular_core.InputSignal<boolean>;
385
437
  /** Whether the group is required. Bound by [formField] directive. */
@@ -409,7 +461,7 @@ declare class AtlRadioGroup implements FormValueControl<string>, AtlRadioGroupCo
409
461
  /** @internal */
410
462
  protected onKeydown(event: KeyboardEvent): void;
411
463
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AtlRadioGroup, never>;
412
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<AtlRadioGroup, "atl-radio-group", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "touched": { "alias": "touched"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "errors": { "alias": "errors"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "touched": "touchedChange"; }, never, ["*"], true, never>;
464
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AtlRadioGroup, "atl-radio-group", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "touched": { "alias": "touched"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "errors": { "alias": "errors"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "touched": "touchedChange"; }, never, ["*"], true, never>;
413
465
  }
414
466
 
415
467
  /**
@@ -631,6 +683,12 @@ declare class AtlCombobox implements FormValueControl<string> {
631
683
  readonly placeholder: _angular_core.InputSignal<string>;
632
684
  /** Whether the combobox is disabled. */
633
685
  readonly disabled: _angular_core.InputSignal<boolean>;
686
+ /**
687
+ * Whether the value can be read but not changed. Enforced here rather than by
688
+ * the DOM: `readonly` on the text input would stop typing, but nothing stops the
689
+ * panel from opening and an option from being picked. See ADR-0045.
690
+ */
691
+ readonly readonly: _angular_core.InputSignal<boolean>;
634
692
  /** Whether the combobox has validation errors. */
635
693
  readonly invalid: _angular_core.InputSignal<boolean>;
636
694
  /** Whether the combobox is required. */
@@ -680,7 +738,7 @@ declare class AtlCombobox implements FormValueControl<string> {
680
738
  private open;
681
739
  private close;
682
740
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AtlCombobox, never>;
683
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<AtlCombobox, "atl-combobox", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "touched": { "alias": "touched"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "errors": { "alias": "errors"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "touched": "touchedChange"; }, never, never, true, never>;
741
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AtlCombobox, "atl-combobox", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "touched": { "alias": "touched"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "errors": { "alias": "errors"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "touched": "touchedChange"; }, never, never, true, never>;
684
742
  }
685
743
 
686
744
  /**