@joanroucoux/cairn-ui 0.8.2 → 0.9.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/fesm2022/joanroucoux-cairn-ui-action-bar.mjs +57 -53
  3. package/fesm2022/joanroucoux-cairn-ui-action-bar.mjs.map +1 -1
  4. package/fesm2022/joanroucoux-cairn-ui-alert.mjs +155 -47
  5. package/fesm2022/joanroucoux-cairn-ui-alert.mjs.map +1 -1
  6. package/fesm2022/joanroucoux-cairn-ui-amount.mjs +220 -110
  7. package/fesm2022/joanroucoux-cairn-ui-amount.mjs.map +1 -1
  8. package/fesm2022/joanroucoux-cairn-ui-async.mjs +392 -216
  9. package/fesm2022/joanroucoux-cairn-ui-async.mjs.map +1 -1
  10. package/fesm2022/joanroucoux-cairn-ui-avatar.mjs +145 -68
  11. package/fesm2022/joanroucoux-cairn-ui-avatar.mjs.map +1 -1
  12. package/fesm2022/joanroucoux-cairn-ui-back-link.mjs +87 -47
  13. package/fesm2022/joanroucoux-cairn-ui-back-link.mjs.map +1 -1
  14. package/fesm2022/joanroucoux-cairn-ui-badge.mjs +83 -43
  15. package/fesm2022/joanroucoux-cairn-ui-badge.mjs.map +1 -1
  16. package/fesm2022/joanroucoux-cairn-ui-button.mjs +184 -104
  17. package/fesm2022/joanroucoux-cairn-ui-button.mjs.map +1 -1
  18. package/fesm2022/joanroucoux-cairn-ui-card.mjs +232 -137
  19. package/fesm2022/joanroucoux-cairn-ui-card.mjs.map +1 -1
  20. package/fesm2022/joanroucoux-cairn-ui-choice-chips.mjs +298 -102
  21. package/fesm2022/joanroucoux-cairn-ui-choice-chips.mjs.map +1 -1
  22. package/fesm2022/joanroucoux-cairn-ui-control.mjs +4 -17
  23. package/fesm2022/joanroucoux-cairn-ui-control.mjs.map +1 -1
  24. package/fesm2022/joanroucoux-cairn-ui-delta.mjs +150 -83
  25. package/fesm2022/joanroucoux-cairn-ui-delta.mjs.map +1 -1
  26. package/fesm2022/joanroucoux-cairn-ui-dialog.mjs +508 -471
  27. package/fesm2022/joanroucoux-cairn-ui-dialog.mjs.map +1 -1
  28. package/fesm2022/joanroucoux-cairn-ui-donut.mjs +255 -157
  29. package/fesm2022/joanroucoux-cairn-ui-donut.mjs.map +1 -1
  30. package/fesm2022/joanroucoux-cairn-ui-drawer.mjs +303 -0
  31. package/fesm2022/joanroucoux-cairn-ui-drawer.mjs.map +1 -0
  32. package/fesm2022/joanroucoux-cairn-ui-empty.mjs +76 -35
  33. package/fesm2022/joanroucoux-cairn-ui-empty.mjs.map +1 -1
  34. package/fesm2022/joanroucoux-cairn-ui-external-link.mjs +40 -31
  35. package/fesm2022/joanroucoux-cairn-ui-external-link.mjs.map +1 -1
  36. package/fesm2022/joanroucoux-cairn-ui-fact.mjs +188 -79
  37. package/fesm2022/joanroucoux-cairn-ui-fact.mjs.map +1 -1
  38. package/fesm2022/joanroucoux-cairn-ui-field.mjs +309 -173
  39. package/fesm2022/joanroucoux-cairn-ui-field.mjs.map +1 -1
  40. package/fesm2022/joanroucoux-cairn-ui-filter-chips.mjs +165 -87
  41. package/fesm2022/joanroucoux-cairn-ui-filter-chips.mjs.map +1 -1
  42. package/fesm2022/joanroucoux-cairn-ui-group-header.mjs +269 -0
  43. package/fesm2022/joanroucoux-cairn-ui-group-header.mjs.map +1 -0
  44. package/fesm2022/joanroucoux-cairn-ui-input.mjs +205 -85
  45. package/fesm2022/joanroucoux-cairn-ui-input.mjs.map +1 -1
  46. package/fesm2022/joanroucoux-cairn-ui-line-chart.mjs +838 -512
  47. package/fesm2022/joanroucoux-cairn-ui-line-chart.mjs.map +1 -1
  48. package/fesm2022/joanroucoux-cairn-ui-menu.mjs +401 -225
  49. package/fesm2022/joanroucoux-cairn-ui-menu.mjs.map +1 -1
  50. package/fesm2022/joanroucoux-cairn-ui-meter.mjs +143 -57
  51. package/fesm2022/joanroucoux-cairn-ui-meter.mjs.map +1 -1
  52. package/fesm2022/joanroucoux-cairn-ui-motion.mjs +367 -399
  53. package/fesm2022/joanroucoux-cairn-ui-motion.mjs.map +1 -1
  54. package/fesm2022/joanroucoux-cairn-ui-nav-item.mjs +73 -44
  55. package/fesm2022/joanroucoux-cairn-ui-nav-item.mjs.map +1 -1
  56. package/fesm2022/joanroucoux-cairn-ui-result-group.mjs +191 -0
  57. package/fesm2022/joanroucoux-cairn-ui-result-group.mjs.map +1 -0
  58. package/fesm2022/joanroucoux-cairn-ui-row.mjs +376 -146
  59. package/fesm2022/joanroucoux-cairn-ui-row.mjs.map +1 -1
  60. package/fesm2022/joanroucoux-cairn-ui-segmented.mjs +183 -81
  61. package/fesm2022/joanroucoux-cairn-ui-segmented.mjs.map +1 -1
  62. package/fesm2022/joanroucoux-cairn-ui-select-pill.mjs +233 -0
  63. package/fesm2022/joanroucoux-cairn-ui-select-pill.mjs.map +1 -0
  64. package/fesm2022/joanroucoux-cairn-ui-select.mjs +121 -67
  65. package/fesm2022/joanroucoux-cairn-ui-select.mjs.map +1 -1
  66. package/fesm2022/joanroucoux-cairn-ui-skeleton.mjs +129 -51
  67. package/fesm2022/joanroucoux-cairn-ui-skeleton.mjs.map +1 -1
  68. package/fesm2022/joanroucoux-cairn-ui-stale-link.mjs +80 -39
  69. package/fesm2022/joanroucoux-cairn-ui-stale-link.mjs.map +1 -1
  70. package/fesm2022/joanroucoux-cairn-ui-stat.mjs +85 -55
  71. package/fesm2022/joanroucoux-cairn-ui-stat.mjs.map +1 -1
  72. package/fesm2022/joanroucoux-cairn-ui-switch.mjs +48 -35
  73. package/fesm2022/joanroucoux-cairn-ui-switch.mjs.map +1 -1
  74. package/fesm2022/joanroucoux-cairn-ui-tab-bar.mjs +113 -69
  75. package/fesm2022/joanroucoux-cairn-ui-tab-bar.mjs.map +1 -1
  76. package/fesm2022/joanroucoux-cairn-ui-table.mjs +1036 -310
  77. package/fesm2022/joanroucoux-cairn-ui-table.mjs.map +1 -1
  78. package/fesm2022/joanroucoux-cairn-ui-toast.mjs +240 -127
  79. package/fesm2022/joanroucoux-cairn-ui-toast.mjs.map +1 -1
  80. package/fesm2022/joanroucoux-cairn-ui.mjs +0 -4
  81. package/package.json +17 -1
  82. package/styles/motion.css +1 -13
  83. package/styles/tokens.css +35 -64
  84. package/types/joanroucoux-cairn-ui-action-bar.d.ts +9 -14
  85. package/types/joanroucoux-cairn-ui-alert.d.ts +29 -15
  86. package/types/joanroucoux-cairn-ui-amount.d.ts +64 -32
  87. package/types/joanroucoux-cairn-ui-async.d.ts +85 -38
  88. package/types/joanroucoux-cairn-ui-avatar.d.ts +34 -22
  89. package/types/joanroucoux-cairn-ui-back-link.d.ts +20 -15
  90. package/types/joanroucoux-cairn-ui-badge.d.ts +22 -14
  91. package/types/joanroucoux-cairn-ui-button.d.ts +36 -18
  92. package/types/joanroucoux-cairn-ui-card.d.ts +48 -23
  93. package/types/joanroucoux-cairn-ui-choice-chips.d.ts +91 -35
  94. package/types/joanroucoux-cairn-ui-control.d.ts +9 -18
  95. package/types/joanroucoux-cairn-ui-delta.d.ts +46 -24
  96. package/types/joanroucoux-cairn-ui-dialog.d.ts +124 -57
  97. package/types/joanroucoux-cairn-ui-donut.d.ts +83 -46
  98. package/types/joanroucoux-cairn-ui-drawer.d.ts +73 -0
  99. package/types/joanroucoux-cairn-ui-empty.d.ts +20 -13
  100. package/types/joanroucoux-cairn-ui-external-link.d.ts +6 -10
  101. package/types/joanroucoux-cairn-ui-fact.d.ts +49 -25
  102. package/types/joanroucoux-cairn-ui-field.d.ts +72 -49
  103. package/types/joanroucoux-cairn-ui-filter-chips.d.ts +35 -20
  104. package/types/joanroucoux-cairn-ui-group-header.d.ts +58 -0
  105. package/types/joanroucoux-cairn-ui-input.d.ts +59 -37
  106. package/types/joanroucoux-cairn-ui-line-chart.d.ts +199 -89
  107. package/types/joanroucoux-cairn-ui-menu.d.ts +81 -44
  108. package/types/joanroucoux-cairn-ui-meter.d.ts +44 -22
  109. package/types/joanroucoux-cairn-ui-motion.d.ts +61 -75
  110. package/types/joanroucoux-cairn-ui-nav-item.d.ts +15 -13
  111. package/types/joanroucoux-cairn-ui-result-group.d.ts +52 -0
  112. package/types/joanroucoux-cairn-ui-row.d.ts +79 -37
  113. package/types/joanroucoux-cairn-ui-segmented.d.ts +51 -27
  114. package/types/joanroucoux-cairn-ui-select-pill.d.ts +52 -0
  115. package/types/joanroucoux-cairn-ui-select.d.ts +28 -18
  116. package/types/joanroucoux-cairn-ui-skeleton.d.ts +35 -18
  117. package/types/joanroucoux-cairn-ui-stale-link.d.ts +23 -16
  118. package/types/joanroucoux-cairn-ui-stat.d.ts +26 -21
  119. package/types/joanroucoux-cairn-ui-switch.d.ts +7 -11
  120. package/types/joanroucoux-cairn-ui-tab-bar.d.ts +20 -17
  121. package/types/joanroucoux-cairn-ui-table.d.ts +251 -89
  122. package/types/joanroucoux-cairn-ui-toast.d.ts +46 -36
  123. package/types/joanroucoux-cairn-ui.d.ts +1 -2
  124. package/fesm2022/joanroucoux-cairn-ui.mjs.map +0 -1
@@ -1,73 +1,121 @@
1
- import * as i0 from '@angular/core';
2
- import { input, booleanAttribute, model, viewChildren, computed, Component } from '@angular/core';
3
- import { holdTransitionsUntilRendered } from '@joanroucoux/cairn-ui/motion';
4
-
1
+ import * as i0 from "@angular/core";
2
+ import { Component, booleanAttribute, computed, input, model, viewChildren } from "@angular/core";
3
+ import { holdTransitionsUntilRendered } from "@joanroucoux/cairn-ui/motion";
5
4
  const nextId = (() => {
6
- let count = 0;
7
- return () => `ui-segmented-${++count}`;
5
+ let count = 0;
6
+ return () => `ui-segmented-${++count}`;
8
7
  })();
9
8
  const OPTION_CLASSES = "relative z-10 flex-1 cursor-pointer px-3 text-label font-medium transition-[scale] duration-(--duration-press) ease-out after:absolute after:inset-x-0 after:top-1/2 after:h-(--row-min) after:-translate-y-1/2 after:content-[''] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) active:scale-(--press-scale)";
10
- const SEGMENTED_SIZES = ['md', 'sm'];
9
+ const SEGMENTED_SIZES = ["md", "sm"];
11
10
  const SIZE_CLASSES = {
12
- md: 'h-10 pointer-fine:h-8',
13
- sm: 'h-10 pointer-fine:h-[30px]',
11
+ md: "h-10 pointer-fine:h-8",
12
+ sm: "h-10 pointer-fine:h-[30px]"
14
13
  };
15
- const SELECTED_CLASSES = 'text-(--foreground)';
16
- const UNSELECTED_CLASSES = 'text-(--muted-foreground) hover:text-(--foreground)';
17
- /**
18
- * Exclusive choice within a small, known set: a time range, a unit, a mode. The selected option's
19
- * background is a single thumb that slides under it in `transform`, instead of being redrawn.
20
- *
21
- * @example
22
- * <ui-segmented [options]="ranges" label="Time range" [(value)]="range" />
23
- */
24
- class UiSegmented {
25
- options = input.required(/* @ts-ignore */
26
- ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
27
- label = input.required(/* @ts-ignore */
28
- ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
29
- showLabel = input(false, { ...(ngDevMode ? { debugName: "showLabel" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
30
- labelId = nextId();
31
- value = model.required(/* @ts-ignore */
32
- ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
33
- size = input('md', /* @ts-ignore */
34
- ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
35
- radios = viewChildren('radio', /* @ts-ignore */
36
- ...(ngDevMode ? [{ debugName: "radios" }] : /* istanbul ignore next */ []));
37
- thumbIndex = computed(() => {
38
- const index = this.options().findIndex((option) => option.value === this.value());
39
- return index === -1 ? 0 : index + 1;
40
- }, /* @ts-ignore */
41
- ...(ngDevMode ? [{ debugName: "thumbIndex" }] : /* istanbul ignore next */ []));
42
- constructor() {
43
- holdTransitionsUntilRendered();
44
- }
45
- optionClasses(optionValue) {
46
- return `${OPTION_CLASSES} ${SIZE_CLASSES[this.size()]} ${optionValue === this.value() ? SELECTED_CLASSES : UNSELECTED_CLASSES}`;
47
- }
48
- tabIndexFor(optionValue) {
49
- const opts = this.options();
50
- const tabStopValue = opts.some((option) => option.value === this.value()) ? this.value() : opts[0]?.value;
51
- return optionValue === tabStopValue ? 0 : -1;
52
- }
53
- select(optionValue) {
54
- this.value.set(optionValue);
55
- }
56
- onKeydown(event) {
57
- const delta = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[event.key];
58
- if (delta === undefined) {
59
- return;
60
- }
61
- event.preventDefault();
62
- const opts = this.options();
63
- const currentIndex = opts.findIndex((option) => option.value === this.value());
64
- const nextIndex = (currentIndex + delta + opts.length) % opts.length;
65
- const next = opts[nextIndex];
66
- this.value.set(next.value);
67
- this.radios()[nextIndex]?.nativeElement.focus();
68
- }
69
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSegmented, deps: [], target: i0.ɵɵFactoryTarget.Component });
70
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiSegmented, isStandalone: true, selector: "ui-segmented", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, viewQueries: [{ propertyName: "radios", predicate: ["radio"], descendants: true, isSignal: true }], ngImport: i0, template: `
14
+ const SELECTED_CLASSES = "text-(--foreground)";
15
+ const UNSELECTED_CLASSES = "text-(--muted-foreground) hover:text-(--foreground)";
16
+ var UiSegmented = class UiSegmented {
17
+ options = input.required(...ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []);
18
+ label = input.required(...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
19
+ showLabel = input(false, {
20
+ ...ngDevMode ? { debugName: "showLabel" } : /* istanbul ignore next */ {},
21
+ transform: booleanAttribute
22
+ });
23
+ labelId = nextId();
24
+ value = model.required(...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
25
+ size = input("md", ...ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []);
26
+ radios = viewChildren("radio", ...ngDevMode ? [{ debugName: "radios" }] : /* istanbul ignore next */ []);
27
+ thumbIndex = computed(() => {
28
+ const index = this.options().findIndex((option) => option.value === this.value());
29
+ return index === -1 ? 0 : index + 1;
30
+ }, ...ngDevMode ? [{ debugName: "thumbIndex" }] : /* istanbul ignore next */ []);
31
+ constructor() {
32
+ holdTransitionsUntilRendered();
33
+ }
34
+ optionClasses(optionValue) {
35
+ return `${OPTION_CLASSES} ${SIZE_CLASSES[this.size()]} ${optionValue === this.value() ? SELECTED_CLASSES : UNSELECTED_CLASSES}`;
36
+ }
37
+ tabIndexFor(optionValue) {
38
+ const opts = this.options();
39
+ return optionValue === (opts.some((option) => option.value === this.value()) ? this.value() : opts[0]?.value) ? 0 : -1;
40
+ }
41
+ select(optionValue) {
42
+ this.value.set(optionValue);
43
+ }
44
+ onKeydown(event) {
45
+ const delta = {
46
+ ArrowRight: 1,
47
+ ArrowDown: 1,
48
+ ArrowLeft: -1,
49
+ ArrowUp: -1
50
+ }[event.key];
51
+ if (delta === void 0) return;
52
+ event.preventDefault();
53
+ const opts = this.options();
54
+ const nextIndex = (opts.findIndex((option) => option.value === this.value()) + delta + opts.length) % opts.length;
55
+ const next = opts[nextIndex];
56
+ this.value.set(next.value);
57
+ this.radios()[nextIndex]?.nativeElement.focus();
58
+ }
59
+ static ɵfac = i0.ɵɵngDeclareFactory({
60
+ minVersion: "12.0.0",
61
+ version: "22.2.0",
62
+ ngImport: i0,
63
+ type: UiSegmented,
64
+ deps: [],
65
+ target: i0.ɵɵFactoryTarget.Component
66
+ });
67
+ static ɵcmp = i0.ɵɵngDeclareComponent({
68
+ minVersion: "17.0.0",
69
+ version: "22.2.0",
70
+ type: UiSegmented,
71
+ isStandalone: true,
72
+ selector: "ui-segmented",
73
+ inputs: {
74
+ options: {
75
+ classPropertyName: "options",
76
+ publicName: "options",
77
+ isSignal: true,
78
+ isRequired: true,
79
+ transformFunction: null
80
+ },
81
+ label: {
82
+ classPropertyName: "label",
83
+ publicName: "label",
84
+ isSignal: true,
85
+ isRequired: true,
86
+ transformFunction: null
87
+ },
88
+ showLabel: {
89
+ classPropertyName: "showLabel",
90
+ publicName: "showLabel",
91
+ isSignal: true,
92
+ isRequired: false,
93
+ transformFunction: null
94
+ },
95
+ value: {
96
+ classPropertyName: "value",
97
+ publicName: "value",
98
+ isSignal: true,
99
+ isRequired: true,
100
+ transformFunction: null
101
+ },
102
+ size: {
103
+ classPropertyName: "size",
104
+ publicName: "size",
105
+ isSignal: true,
106
+ isRequired: false,
107
+ transformFunction: null
108
+ }
109
+ },
110
+ outputs: { value: "valueChange" },
111
+ viewQueries: [{
112
+ propertyName: "radios",
113
+ predicate: ["radio"],
114
+ descendants: true,
115
+ isSignal: true
116
+ }],
117
+ ngImport: i0,
118
+ template: `
71
119
  <div [class]="showLabel() ? 'flex flex-col gap-1.5' : ''">
72
120
  @if (showLabel()) {
73
121
  <span class="text-label leading-[17px] font-medium text-(--muted-foreground)" [id]="labelId">{{
@@ -107,13 +155,20 @@ class UiSegmented {
107
155
  }
108
156
  </div>
109
157
  </div>
110
- `, isInline: true });
111
- }
112
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSegmented, decorators: [{
113
- type: Component,
114
- args: [{
115
- selector: 'ui-segmented',
116
- template: `
158
+ `,
159
+ isInline: true
160
+ });
161
+ };
162
+ i0.ɵɵngDeclareClassMetadata({
163
+ minVersion: "12.0.0",
164
+ version: "22.2.0",
165
+ ngImport: i0,
166
+ type: UiSegmented,
167
+ decorators: [{
168
+ type: Component,
169
+ args: [{
170
+ selector: "ui-segmented",
171
+ template: `
117
172
  <div [class]="showLabel() ? 'flex flex-col gap-1.5' : ''">
118
173
  @if (showLabel()) {
119
174
  <span class="text-label leading-[17px] font-medium text-(--muted-foreground)" [id]="labelId">{{
@@ -153,13 +208,60 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
153
208
  }
154
209
  </div>
155
210
  </div>
156
- `,
157
- }]
158
- }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }, { type: i0.Output, args: ["valueChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], radios: [{ type: i0.ViewChildren, args: ['radio', { isSignal: true }] }] } });
159
-
160
- /**
161
- * Generated bundle index. Do not edit.
162
- */
163
-
211
+ `
212
+ }]
213
+ }],
214
+ ctorParameters: () => [],
215
+ propDecorators: {
216
+ options: [{
217
+ type: i0.Input,
218
+ args: [{
219
+ isSignal: true,
220
+ alias: "options",
221
+ required: true
222
+ }]
223
+ }],
224
+ label: [{
225
+ type: i0.Input,
226
+ args: [{
227
+ isSignal: true,
228
+ alias: "label",
229
+ required: true
230
+ }]
231
+ }],
232
+ showLabel: [{
233
+ type: i0.Input,
234
+ args: [{
235
+ isSignal: true,
236
+ alias: "showLabel",
237
+ required: false
238
+ }]
239
+ }],
240
+ value: [{
241
+ type: i0.Input,
242
+ args: [{
243
+ isSignal: true,
244
+ alias: "value",
245
+ required: true
246
+ }]
247
+ }, {
248
+ type: i0.Output,
249
+ args: ["valueChange"]
250
+ }],
251
+ size: [{
252
+ type: i0.Input,
253
+ args: [{
254
+ isSignal: true,
255
+ alias: "size",
256
+ required: false
257
+ }]
258
+ }],
259
+ radios: [{
260
+ type: i0.ViewChildren,
261
+ args: ["radio", { isSignal: true }]
262
+ }]
263
+ }
264
+ });
164
265
  export { SEGMENTED_SIZES, UiSegmented };
165
- //# sourceMappingURL=joanroucoux-cairn-ui-segmented.mjs.map
266
+
267
+ //# sourceMappingURL=joanroucoux-cairn-ui-segmented.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"joanroucoux-cairn-ui-segmented.mjs","sources":["../../../projects/ui/segmented/segmented.ts","../../../projects/ui/segmented/joanroucoux-cairn-ui-segmented.ts"],"sourcesContent":["import { Component, ElementRef, booleanAttribute, computed, input, model, viewChildren } from '@angular/core';\n\nimport { holdTransitionsUntilRendered } from '@joanroucoux/cairn-ui/motion';\n\nconst nextId = (() => {\n let count = 0;\n\n return () => `ui-segmented-${++count}`;\n})();\n\nexport type SegmentedOption = {\n value: string;\n label: string;\n};\n\nconst OPTION_CLASSES =\n \"relative z-10 flex-1 cursor-pointer px-3 text-label font-medium transition-[scale] duration-(--duration-press) ease-out after:absolute after:inset-x-0 after:top-1/2 after:h-(--row-min) after:-translate-y-1/2 after:content-[''] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) active:scale-(--press-scale)\";\n\nexport const SEGMENTED_SIZES = ['md', 'sm'] as const;\nexport type SegmentedSize = (typeof SEGMENTED_SIZES)[number];\n\nconst SIZE_CLASSES: Record<SegmentedSize, string> = {\n md: 'h-10 pointer-fine:h-8',\n sm: 'h-10 pointer-fine:h-[30px]',\n};\n\nconst SELECTED_CLASSES = 'text-(--foreground)';\n\nconst UNSELECTED_CLASSES = 'text-(--muted-foreground) hover:text-(--foreground)';\n\n/**\n * Exclusive choice within a small, known set: a time range, a unit, a mode. The selected option's\n * background is a single thumb that slides under it in `transform`, instead of being redrawn.\n *\n * @example\n * <ui-segmented [options]=\"ranges\" label=\"Time range\" [(value)]=\"range\" />\n */\n@Component({\n selector: 'ui-segmented',\n template: `\n <div [class]=\"showLabel() ? 'flex flex-col gap-1.5' : ''\">\n @if (showLabel()) {\n <span class=\"text-label leading-[17px] font-medium text-(--muted-foreground)\" [id]=\"labelId\">{{\n label()\n }}</span>\n }\n <div\n class=\"rounded-control relative grid bg-(--muted) p-0.5\"\n role=\"radiogroup\"\n [attr.aria-label]=\"showLabel() ? null : label()\"\n [attr.aria-labelledby]=\"showLabel() ? labelId : null\"\n [style.grid-template-columns]=\"'repeat(' + options().length + ', 1fr)'\"\n >\n @if (thumbIndex(); as index) {\n <span\n aria-hidden=\"true\"\n class=\"absolute top-0.5 bottom-0.5 left-0.5 rounded-[calc(var(--radius-control)-2px)] bg-(--card) shadow-[0_1px_2px_rgb(0_0_0/0.08),inset_0_0_0_1px_var(--border)] transition-transform duration-(--duration-fast) ease-out\"\n data-thumb\n [style.transform]=\"'translateX(' + (index - 1) * 100 + '%)'\"\n [style.width]=\"'calc((100% - 4px) / ' + options().length + ')'\"\n ></span>\n }\n\n @for (option of options(); track option.value) {\n <button\n #radio\n role=\"radio\"\n type=\"button\"\n [attr.aria-checked]=\"option.value === value()\"\n [attr.tabindex]=\"tabIndexFor(option.value)\"\n [class]=\"optionClasses(option.value)\"\n (click)=\"select(option.value)\"\n (keydown)=\"onKeydown($event)\"\n >\n {{ option.label }}\n </button>\n }\n </div>\n </div>\n `,\n})\nexport class UiSegmented {\n readonly options = input.required<SegmentedOption[]>();\n readonly label = input.required<string>();\n readonly showLabel = input(false, { transform: booleanAttribute });\n\n protected readonly labelId = nextId();\n readonly value = model.required<string>();\n readonly size = input<SegmentedSize>('md');\n\n protected readonly radios = viewChildren<ElementRef<HTMLButtonElement>>('radio');\n\n protected readonly thumbIndex = computed(() => {\n const index = this.options().findIndex((option) => option.value === this.value());\n\n return index === -1 ? 0 : index + 1;\n });\n\n constructor() {\n holdTransitionsUntilRendered();\n }\n\n protected optionClasses(optionValue: string): string {\n return `${OPTION_CLASSES} ${SIZE_CLASSES[this.size()]} ${optionValue === this.value() ? SELECTED_CLASSES : UNSELECTED_CLASSES}`;\n }\n\n protected tabIndexFor(optionValue: string): number {\n const opts = this.options();\n const tabStopValue = opts.some((option) => option.value === this.value()) ? this.value() : opts[0]?.value;\n\n return optionValue === tabStopValue ? 0 : -1;\n }\n\n protected select(optionValue: string): void {\n this.value.set(optionValue);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n const delta = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[event.key];\n if (delta === undefined) {\n return;\n }\n event.preventDefault();\n\n const opts = this.options();\n const currentIndex = opts.findIndex((option) => option.value === this.value());\n const nextIndex = (currentIndex + delta + opts.length) % opts.length;\n const next = opts[nextIndex]!;\n\n this.value.set(next.value);\n this.radios()[nextIndex]?.nativeElement.focus();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"joanroucoux-cairn-ui-segmented.mjs","names":[],"sources":["../../../projects/ui/segmented/segmented.ts"],"sourcesContent":["import { Component, ElementRef, booleanAttribute, computed, input, model, viewChildren } from '@angular/core';\n\nimport { holdTransitionsUntilRendered } from '@joanroucoux/cairn-ui/motion';\n\nconst nextId = (() => {\n let count = 0;\n\n return () => `ui-segmented-${++count}`;\n})();\n\nexport type SegmentedOption = {\n value: string;\n label: string;\n};\n\nconst OPTION_CLASSES =\n \"relative z-10 flex-1 cursor-pointer px-3 text-label font-medium transition-[scale] duration-(--duration-press) ease-out after:absolute after:inset-x-0 after:top-1/2 after:h-(--row-min) after:-translate-y-1/2 after:content-[''] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) active:scale-(--press-scale)\";\n\nexport const SEGMENTED_SIZES = ['md', 'sm'] as const;\nexport type SegmentedSize = (typeof SEGMENTED_SIZES)[number];\n\nconst SIZE_CLASSES: Record<SegmentedSize, string> = {\n md: 'h-10 pointer-fine:h-8',\n sm: 'h-10 pointer-fine:h-[30px]',\n};\n\nconst SELECTED_CLASSES = 'text-(--foreground)';\n\nconst UNSELECTED_CLASSES = 'text-(--muted-foreground) hover:text-(--foreground)';\n\n/**\n * Exclusive choice within a small, known set: a time range, a unit, a mode.\n *\n * @example\n * <ui-segmented [options]=\"ranges\" label=\"Time range\" [(value)]=\"range\" />\n */\n@Component({\n selector: 'ui-segmented',\n template: `\n <div [class]=\"showLabel() ? 'flex flex-col gap-1.5' : ''\">\n @if (showLabel()) {\n <span class=\"text-label leading-[17px] font-medium text-(--muted-foreground)\" [id]=\"labelId\">{{\n label()\n }}</span>\n }\n <div\n class=\"rounded-control relative grid bg-(--muted) p-0.5\"\n role=\"radiogroup\"\n [attr.aria-label]=\"showLabel() ? null : label()\"\n [attr.aria-labelledby]=\"showLabel() ? labelId : null\"\n [style.grid-template-columns]=\"'repeat(' + options().length + ', 1fr)'\"\n >\n @if (thumbIndex(); as index) {\n <span\n aria-hidden=\"true\"\n class=\"absolute top-0.5 bottom-0.5 left-0.5 rounded-[calc(var(--radius-control)-2px)] bg-(--card) shadow-[0_1px_2px_rgb(0_0_0/0.08),inset_0_0_0_1px_var(--border)] transition-transform duration-(--duration-fast) ease-out\"\n data-thumb\n [style.transform]=\"'translateX(' + (index - 1) * 100 + '%)'\"\n [style.width]=\"'calc((100% - 4px) / ' + options().length + ')'\"\n ></span>\n }\n\n @for (option of options(); track option.value) {\n <button\n #radio\n role=\"radio\"\n type=\"button\"\n [attr.aria-checked]=\"option.value === value()\"\n [attr.tabindex]=\"tabIndexFor(option.value)\"\n [class]=\"optionClasses(option.value)\"\n (click)=\"select(option.value)\"\n (keydown)=\"onKeydown($event)\"\n >\n {{ option.label }}\n </button>\n }\n </div>\n </div>\n `,\n})\nexport class UiSegmented {\n readonly options = input.required<SegmentedOption[]>();\n readonly label = input.required<string>();\n readonly showLabel = input(false, { transform: booleanAttribute });\n\n protected readonly labelId = nextId();\n readonly value = model.required<string>();\n readonly size = input<SegmentedSize>('md');\n\n protected readonly radios = viewChildren<ElementRef<HTMLButtonElement>>('radio');\n\n protected readonly thumbIndex = computed(() => {\n const index = this.options().findIndex((option) => option.value === this.value());\n\n return index === -1 ? 0 : index + 1;\n });\n\n constructor() {\n holdTransitionsUntilRendered();\n }\n\n protected optionClasses(optionValue: string): string {\n return `${OPTION_CLASSES} ${SIZE_CLASSES[this.size()]} ${optionValue === this.value() ? SELECTED_CLASSES : UNSELECTED_CLASSES}`;\n }\n\n protected tabIndexFor(optionValue: string): number {\n const opts = this.options();\n const tabStopValue = opts.some((option) => option.value === this.value()) ? this.value() : opts[0]?.value;\n\n return optionValue === tabStopValue ? 0 : -1;\n }\n\n protected select(optionValue: string): void {\n this.value.set(optionValue);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n const delta = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[event.key];\n if (delta === undefined) {\n return;\n }\n event.preventDefault();\n\n const opts = this.options();\n const currentIndex = opts.findIndex((option) => option.value === this.value());\n const nextIndex = (currentIndex + delta + opts.length) % opts.length;\n const next = opts[nextIndex]!;\n\n this.value.set(next.value);\n this.radios()[nextIndex]?.nativeElement.focus();\n }\n}\n"],"mappings":";;;AAIA,MAAM,gBAAe;CACnB,IAAI,QAAQ;CAEZ,aAAa,gBAAgB,EAAE;AACjC,EAAA,CAAE;AAOF,MAAM,iBACJ;AAEF,MAAa,kBAAkB,CAAC,MAAM,IAAI;AAG1C,MAAM,eAA8C;CAClD,IAAI;CACJ,IAAI;AACL;AAED,MAAM,mBAAmB;AAEzB,MAAM,qBAAqB;AAoD3B,IAAa,cAAb,MAAa,YAAW;CACb,UAAU,MAAM,SAA6B,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAC7C,QAAQ,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAChC,YAAY,MAAM,OAAK;EAAA,GAAA,YAAA,EAAA,WAAA,YAAA,+BAAA,CAAA;EAAI,WAAW;CAAgB,CAAA;CAE5C,UAAU,OAAM;CAC1B,QAAQ,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAChC,OAAO,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAEvB,SAAS,aAA4C,SAAQ,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CAE7D,aAAa,eAAc;EAC5C,MAAM,QAAQ,KAAK,QAAO,CAAE,CAAC,WAAW,WAAW,OAAO,UAAU,KAAK,MAAK,CAAE;EAEhF,OAAO,UAAU,KAAK,IAAI,QAAQ;CACpC,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAEF,cAAA;EACE,6BAA4B;CAC9B;CAEU,cAAc,aAAmB;EACzC,OAAO,GAAG,eAAc,GAAI,aAAa,KAAK,KAAI,GAAG,GAAI,gBAAgB,KAAK,MAAK,IAAK,mBAAmB;CAC7G;CAEU,YAAY,aAAmB;EACvC,MAAM,OAAO,KAAK,QAAO;EAGzB,OAAO,iBAFc,KAAK,MAAM,WAAW,OAAO,UAAU,KAAK,MAAK,CAAE,IAAI,KAAK,MAAK,IAAK,KAAK,EAAE,EAAE,SAE9D,IAAI;CAC5C;CAEU,OAAO,aAAmB;EAClC,KAAK,MAAM,IAAI,WAAW;CAC5B;CAEU,UAAU,OAAoB;EACtC,MAAM,QAAQ;GAAE,YAAY;GAAG,WAAW;GAAG,WAAW;GAAI,SAAS;EAAE,EAAG,MAAM;EAChF,IAAI,UAAU,QACZ;EAEF,MAAM,eAAc;EAEpB,MAAM,OAAO,KAAK,QAAO;EAEzB,MAAM,aADe,KAAK,WAAW,WAAW,OAAO,UAAU,KAAK,MAAK,CACxD,IAAe,QAAQ,KAAK,UAAU,KAAK;EAC9D,MAAM,OAAO,KAAK;EAElB,KAAK,MAAM,IAAI,KAAK,KAAK;EACzB,KAAK,OAAM,CAAE,CAAC,UAAU,EAAE,cAAc,MAAK;CAC/C;CAlDW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAW,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAX,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAW,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA,EAAA,OAAA,cAAA;EAAA,aAAA,CAAA;GAAA,cAAA;GAAA,WAAA,CAAA,OAAA;GAAA,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UA1CZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwCT,UAAA;CAAA,CAAA;;AAEU,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAW,YAAA,CAAA;EA5CvB,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyCX,CAAA;CAUyE,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,GAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,aAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,QAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,SAAO,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA"}
@@ -0,0 +1,233 @@
1
+ import * as i0 from "@angular/core";
2
+ import { Component, booleanAttribute, computed, contentChild, input, output, viewChild } from "@angular/core";
3
+ import { UiMenu, UiMenuTrigger } from "@joanroucoux/cairn-ui/menu";
4
+ const HOST_CLASSES = "relative flex-none inline-flex h-11 items-center pointer-fine:h-8";
5
+ const TRIGGER_CLASSES = "-mx-[5px] flex h-11 cursor-pointer items-center rounded-pill border-x-[5px] border-y-0 border-transparent bg-transparent p-0 font-[inherit] focus-visible:outline-2 focus-visible:-outline-offset-5 focus-visible:outline-(--ring) pointer-fine:mx-0 pointer-fine:h-8 pointer-fine:border-x-0 pointer-fine:focus-visible:outline-offset-0";
6
+ const PILL_CLASSES = "flex h-[34px] items-center rounded-pill pr-[30px] pl-3 text-label font-medium whitespace-nowrap forced-colors:border pointer-fine:h-8";
7
+ const REST_PILL_CLASSES = "bg-(--card) text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)]";
8
+ const ACTIVE_PILL_CLASSES = "bg-(--primary) text-(--primary-foreground)";
9
+ const CLEAR_CLASSES = "absolute end-0 flex size-[34px] cursor-pointer items-center justify-center rounded-pill border-0 bg-transparent p-0 text-(--primary-foreground) focus-visible:outline-2 focus-visible:-outline-offset-7 focus-visible:outline-(--primary-foreground) pointer-fine:size-8 pointer-fine:focus-visible:-outline-offset-6 after:absolute after:-inset-[5px] pointer-fine:after:-inset-0.5";
10
+ var UiSelectPill = class UiSelectPill {
11
+ active = input(false, {
12
+ ...ngDevMode ? { debugName: "active" } : /* istanbul ignore next */ {},
13
+ transform: booleanAttribute
14
+ });
15
+ clearLabel = input.required(...ngDevMode ? [{ debugName: "clearLabel" }] : /* istanbul ignore next */ []);
16
+ contextLabel = input(...ngDevMode ? [void 0, { debugName: "contextLabel" }] : /* istanbul ignore next */ []);
17
+ cleared = output();
18
+ menu = contentChild.required(UiMenu, ...ngDevMode ? [{ debugName: "menu" }] : /* istanbul ignore next */ []);
19
+ trigger = viewChild.required("trigger", ...ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []);
20
+ hostClasses = HOST_CLASSES;
21
+ triggerClasses = TRIGGER_CLASSES;
22
+ clearClasses = CLEAR_CLASSES;
23
+ clear() {
24
+ this.cleared.emit();
25
+ this.trigger().nativeElement.focus();
26
+ }
27
+ pillClasses = computed(() => `${PILL_CLASSES} ${this.active() ? ACTIVE_PILL_CLASSES : REST_PILL_CLASSES}`, ...ngDevMode ? [{ debugName: "pillClasses" }] : /* istanbul ignore next */ []);
28
+ static ɵfac = i0.ɵɵngDeclareFactory({
29
+ minVersion: "12.0.0",
30
+ version: "22.2.0",
31
+ ngImport: i0,
32
+ type: UiSelectPill,
33
+ deps: [],
34
+ target: i0.ɵɵFactoryTarget.Component
35
+ });
36
+ static ɵcmp = i0.ɵɵngDeclareComponent({
37
+ minVersion: "17.0.0",
38
+ version: "22.2.0",
39
+ type: UiSelectPill,
40
+ isStandalone: true,
41
+ selector: "ui-select-pill",
42
+ inputs: {
43
+ active: {
44
+ classPropertyName: "active",
45
+ publicName: "active",
46
+ isSignal: true,
47
+ isRequired: false,
48
+ transformFunction: null
49
+ },
50
+ clearLabel: {
51
+ classPropertyName: "clearLabel",
52
+ publicName: "clearLabel",
53
+ isSignal: true,
54
+ isRequired: true,
55
+ transformFunction: null
56
+ },
57
+ contextLabel: {
58
+ classPropertyName: "contextLabel",
59
+ publicName: "contextLabel",
60
+ isSignal: true,
61
+ isRequired: false,
62
+ transformFunction: null
63
+ }
64
+ },
65
+ outputs: { cleared: "cleared" },
66
+ host: { properties: { "class": "hostClasses" } },
67
+ queries: [{
68
+ propertyName: "menu",
69
+ first: true,
70
+ predicate: UiMenu,
71
+ descendants: true,
72
+ isSignal: true
73
+ }],
74
+ viewQueries: [{
75
+ propertyName: "trigger",
76
+ first: true,
77
+ predicate: ["trigger"],
78
+ descendants: true,
79
+ isSignal: true
80
+ }],
81
+ ngImport: i0,
82
+ template: `
83
+ <button #trigger type="button" [class]="triggerClasses" [uiMenuTrigger]="menu()">
84
+ <span data-pill [class]="pillClasses()">
85
+ @if (active() && contextLabel()) {
86
+ <span class="sr-only">{{ contextLabel() }} </span>
87
+ }
88
+ <ng-content
89
+ /></span>
90
+ </button>
91
+ @if (active()) {
92
+ <button data-clear type="button" [attr.aria-label]="clearLabel()" [class]="clearClasses" (click)="clear()">
93
+ <svg
94
+ aria-hidden="true"
95
+ fill="none"
96
+ height="14"
97
+ stroke="currentColor"
98
+ stroke-linecap="round"
99
+ stroke-linejoin="round"
100
+ stroke-width="2"
101
+ viewBox="0 0 24 24"
102
+ width="14"
103
+ >
104
+ <path d="M18 6 6 18M6 6l12 12" />
105
+ </svg>
106
+ </button>
107
+ } @else {
108
+ <svg
109
+ aria-hidden="true"
110
+ class="pointer-events-none absolute right-2.5 text-(--foreground)"
111
+ data-chevron
112
+ fill="none"
113
+ height="14"
114
+ stroke="currentColor"
115
+ stroke-linecap="round"
116
+ stroke-linejoin="round"
117
+ stroke-width="2"
118
+ viewBox="0 0 24 24"
119
+ width="14"
120
+ >
121
+ <path d="m6 9 6 6 6-6" />
122
+ </svg>
123
+ }
124
+ <ng-content select="ui-menu" />
125
+ `,
126
+ isInline: true,
127
+ dependencies: [{
128
+ kind: "directive",
129
+ type: UiMenuTrigger,
130
+ selector: "[uiMenuTrigger]",
131
+ inputs: ["uiMenuTrigger"]
132
+ }]
133
+ });
134
+ };
135
+ i0.ɵɵngDeclareClassMetadata({
136
+ minVersion: "12.0.0",
137
+ version: "22.2.0",
138
+ ngImport: i0,
139
+ type: UiSelectPill,
140
+ decorators: [{
141
+ type: Component,
142
+ args: [{
143
+ selector: "ui-select-pill",
144
+ imports: [UiMenuTrigger],
145
+ template: `
146
+ <button #trigger type="button" [class]="triggerClasses" [uiMenuTrigger]="menu()">
147
+ <span data-pill [class]="pillClasses()">
148
+ @if (active() && contextLabel()) {
149
+ <span class="sr-only">{{ contextLabel() }} </span>
150
+ }
151
+ <ng-content
152
+ /></span>
153
+ </button>
154
+ @if (active()) {
155
+ <button data-clear type="button" [attr.aria-label]="clearLabel()" [class]="clearClasses" (click)="clear()">
156
+ <svg
157
+ aria-hidden="true"
158
+ fill="none"
159
+ height="14"
160
+ stroke="currentColor"
161
+ stroke-linecap="round"
162
+ stroke-linejoin="round"
163
+ stroke-width="2"
164
+ viewBox="0 0 24 24"
165
+ width="14"
166
+ >
167
+ <path d="M18 6 6 18M6 6l12 12" />
168
+ </svg>
169
+ </button>
170
+ } @else {
171
+ <svg
172
+ aria-hidden="true"
173
+ class="pointer-events-none absolute right-2.5 text-(--foreground)"
174
+ data-chevron
175
+ fill="none"
176
+ height="14"
177
+ stroke="currentColor"
178
+ stroke-linecap="round"
179
+ stroke-linejoin="round"
180
+ stroke-width="2"
181
+ viewBox="0 0 24 24"
182
+ width="14"
183
+ >
184
+ <path d="m6 9 6 6 6-6" />
185
+ </svg>
186
+ }
187
+ <ng-content select="ui-menu" />
188
+ `,
189
+ host: { "[class]": "hostClasses" }
190
+ }]
191
+ }],
192
+ propDecorators: {
193
+ active: [{
194
+ type: i0.Input,
195
+ args: [{
196
+ isSignal: true,
197
+ alias: "active",
198
+ required: false
199
+ }]
200
+ }],
201
+ clearLabel: [{
202
+ type: i0.Input,
203
+ args: [{
204
+ isSignal: true,
205
+ alias: "clearLabel",
206
+ required: true
207
+ }]
208
+ }],
209
+ contextLabel: [{
210
+ type: i0.Input,
211
+ args: [{
212
+ isSignal: true,
213
+ alias: "contextLabel",
214
+ required: false
215
+ }]
216
+ }],
217
+ cleared: [{
218
+ type: i0.Output,
219
+ args: ["cleared"]
220
+ }],
221
+ menu: [{
222
+ type: i0.ContentChild,
223
+ args: [i0.forwardRef(() => UiMenu), { isSignal: true }]
224
+ }],
225
+ trigger: [{
226
+ type: i0.ViewChild,
227
+ args: ["trigger", { isSignal: true }]
228
+ }]
229
+ }
230
+ });
231
+ export { UiSelectPill };
232
+
233
+ //# sourceMappingURL=joanroucoux-cairn-ui-select-pill.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"joanroucoux-cairn-ui-select-pill.mjs","names":[],"sources":["../../../projects/ui/select-pill/select-pill.ts"],"sourcesContent":["import {\n Component,\n ElementRef,\n booleanAttribute,\n computed,\n contentChild,\n input,\n output,\n viewChild,\n} from '@angular/core';\n\nimport { UiMenu, UiMenuTrigger } from '@joanroucoux/cairn-ui/menu';\n\nconst HOST_CLASSES = 'relative flex-none inline-flex h-11 items-center pointer-fine:h-8';\n\nconst TRIGGER_CLASSES =\n '-mx-[5px] flex h-11 cursor-pointer items-center rounded-pill border-x-[5px] border-y-0 border-transparent bg-transparent p-0 font-[inherit] focus-visible:outline-2 focus-visible:-outline-offset-5 focus-visible:outline-(--ring) pointer-fine:mx-0 pointer-fine:h-8 pointer-fine:border-x-0 pointer-fine:focus-visible:outline-offset-0';\n\nconst PILL_CLASSES =\n 'flex h-[34px] items-center rounded-pill pr-[30px] pl-3 text-label font-medium whitespace-nowrap forced-colors:border pointer-fine:h-8';\n\nconst REST_PILL_CLASSES = 'bg-(--card) text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)]';\n\nconst ACTIVE_PILL_CLASSES = 'bg-(--primary) text-(--primary-foreground)';\n\nconst CLEAR_CLASSES =\n 'absolute end-0 flex size-[34px] cursor-pointer items-center justify-center rounded-pill border-0 bg-transparent p-0 text-(--primary-foreground) focus-visible:outline-2 focus-visible:-outline-offset-7 focus-visible:outline-(--primary-foreground) pointer-fine:size-8 pointer-fine:focus-visible:-outline-offset-6 after:absolute after:-inset-[5px] pointer-fine:after:-inset-0.5';\n\n/**\n * A pill that opens a `ui-menu` of exclusive choices, to head a row of filter chips: outlined at rest with a\n * chevron, solid primary when `active`, where a separate 14 px cross replaces the chevron and emits `cleared`.\n * It is 34 px tall inside a 44 px target on touch and 32 px with a fine pointer; its focus ring is an outline\n * 2 px outside the pill; the cross has a 44 px hit area on touch (36 px with a fine pointer). `contextLabel`, when\n * given, is read before the label while active so a screen reader hears what the account is. Clearing moves focus\n * back to the trigger. The projected text is the pill label and the projected `ui-menu` is what it opens.\n *\n * @example\n * <ui-select-pill uiChipsLeading [active]=\"!!account()\" clearLabel=\"Clear the account\" (cleared)=\"account.set('')\">\n * {{ accountName() }}\n * <ui-menu label=\"Account\" sheet>\n * <button uiMenuItem [checked]=\"!account()\" (click)=\"account.set('')\">All accounts</button>\n * </ui-menu>\n * </ui-select-pill>\n */\n@Component({\n selector: 'ui-select-pill',\n imports: [UiMenuTrigger],\n template: `\n <button #trigger type=\"button\" [class]=\"triggerClasses\" [uiMenuTrigger]=\"menu()\">\n <span data-pill [class]=\"pillClasses()\">\n @if (active() && contextLabel()) {\n <span class=\"sr-only\">{{ contextLabel() }} </span>\n }\n <ng-content\n /></span>\n </button>\n @if (active()) {\n <button data-clear type=\"button\" [attr.aria-label]=\"clearLabel()\" [class]=\"clearClasses\" (click)=\"clear()\">\n <svg\n aria-hidden=\"true\"\n fill=\"none\"\n height=\"14\"\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n viewBox=\"0 0 24 24\"\n width=\"14\"\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\n } @else {\n <svg\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute right-2.5 text-(--foreground)\"\n data-chevron\n fill=\"none\"\n height=\"14\"\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n viewBox=\"0 0 24 24\"\n width=\"14\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n }\n <ng-content select=\"ui-menu\" />\n `,\n host: {\n '[class]': 'hostClasses',\n },\n})\nexport class UiSelectPill {\n readonly active = input(false, { transform: booleanAttribute });\n readonly clearLabel = input.required<string>();\n readonly contextLabel = input<string>();\n readonly cleared = output();\n\n protected readonly menu = contentChild.required(UiMenu);\n protected readonly trigger = viewChild.required<ElementRef<HTMLButtonElement>>('trigger');\n\n protected readonly hostClasses = HOST_CLASSES;\n protected readonly triggerClasses = TRIGGER_CLASSES;\n protected readonly clearClasses = CLEAR_CLASSES;\n protected clear(): void {\n this.cleared.emit();\n this.trigger().nativeElement.focus();\n }\n\n protected readonly pillClasses = computed(\n () => `${PILL_CLASSES} ${this.active() ? ACTIVE_PILL_CLASSES : REST_PILL_CLASSES}`,\n );\n}\n"],"mappings":"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"}