@blotch/dolan 22.2.5 → 22.2.7
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/dist/commands/install-packages.test.js +7 -2
- package/dist/commands/install-packages.test.js.map +1 -1
- package/dist/registry.js +8 -2
- package/dist/registry.js.map +1 -1
- package/dist/registry.test.js +1 -0
- package/dist/registry.test.js.map +1 -1
- package/dist/ui/components/accordion/accordion.test.ts +24 -7
- package/dist/ui/components/accordion/accordion.ts +34 -44
- package/dist/ui/components/action-sheet/action-sheet.test.ts +18 -1
- package/dist/ui/components/action-sheet/action-sheet.ts +55 -60
- package/dist/ui/components/alert/alert.test.ts +17 -0
- package/dist/ui/components/alert/alert.ts +23 -14
- package/dist/ui/components/alert-dialog/alert-dialog.test.ts +19 -4
- package/dist/ui/components/alert-dialog/alert-dialog.ts +31 -20
- package/dist/ui/components/aspect-ratio/aspect-ratio.test.ts +38 -2
- package/dist/ui/components/aspect-ratio/aspect-ratio.ts +11 -8
- package/dist/ui/components/avatar/avatar.test.ts +7 -0
- package/dist/ui/components/avatar/avatar.ts +35 -30
- package/dist/ui/components/badge/badge.test.ts +9 -0
- package/dist/ui/components/badge/badge.ts +12 -10
- package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +7 -0
- package/dist/ui/components/bottom-sheet/bottom-sheet.ts +5 -0
- package/dist/ui/components/button/button.test.ts +29 -3
- package/dist/ui/components/button/button.ts +67 -62
- package/dist/ui/components/button-group/button-group.test.ts +37 -4
- package/dist/ui/components/button-group/button-group.ts +60 -51
- package/dist/ui/components/card/card.test.ts +35 -2
- package/dist/ui/components/card/card.ts +41 -53
- package/dist/ui/components/chart/chart-presets.test.ts +16 -2
- package/dist/ui/components/chart/chart-presets.ts +22 -3
- package/dist/ui/components/chart/chart.test.ts +35 -6
- package/dist/ui/components/chart/chart.ts +14 -3
- package/dist/ui/components/checkbox/checkbox.test.ts +11 -4
- package/dist/ui/components/checkbox/checkbox.ts +21 -14
- package/dist/ui/components/collapsible/collapsible.test.ts +20 -10
- package/dist/ui/components/collapsible/collapsible.ts +29 -40
- package/dist/ui/components/dialog/dialog.test.ts +17 -1
- package/dist/ui/components/dialog/dialog.ts +24 -17
- package/dist/ui/components/dock/dock.test.ts +30 -5
- package/dist/ui/components/dock/dock.ts +29 -27
- package/dist/ui/components/empty-state/empty-state.test.ts +9 -0
- package/dist/ui/components/empty-state/empty-state.ts +20 -11
- package/dist/ui/components/icon/icon.test.ts +17 -0
- package/dist/ui/components/icon/icon.ts +19 -17
- package/dist/ui/components/input/input.test.ts +55 -3
- package/dist/ui/components/input/input.ts +91 -65
- package/dist/ui/components/label/label.test.ts +15 -0
- package/dist/ui/components/label/label.ts +12 -4
- package/dist/ui/components/list/list.test.ts +13 -3
- package/dist/ui/components/list/list.ts +14 -2
- package/dist/ui/components/menu/menu.test.ts +8 -0
- package/dist/ui/components/menu/menu.ts +4 -0
- package/dist/ui/components/nav-drawer/nav-drawer.test.ts +34 -21
- package/dist/ui/components/nav-drawer/nav-drawer.ts +60 -68
- package/dist/ui/components/navigation-bar/navigation-bar.test.ts +13 -0
- package/dist/ui/components/navigation-bar/navigation-bar.ts +5 -0
- package/dist/ui/components/pagination/pagination.test.ts +9 -0
- package/dist/ui/components/pagination/pagination.ts +52 -49
- package/dist/ui/components/popover/popover.test.ts +3 -0
- package/dist/ui/components/progress/progress.test.ts +7 -0
- package/dist/ui/components/progress/progress.ts +8 -6
- package/dist/ui/components/radio-group/radio-group.test.ts +8 -0
- package/dist/ui/components/radio-group/radio-group.ts +22 -19
- package/dist/ui/components/scroll-area/scroll-area.test.ts +14 -2
- package/dist/ui/components/scroll-area/scroll-area.ts +15 -3
- package/dist/ui/components/search-field/search-field.test.ts +25 -0
- package/dist/ui/components/search-field/search-field.ts +7 -1
- package/dist/ui/components/select/select.test.ts +32 -5
- package/dist/ui/components/select/select.ts +32 -11
- package/dist/ui/components/separator/separator.test.ts +3 -0
- package/dist/ui/components/sheet/sheet.test.ts +18 -0
- package/dist/ui/components/sheet/sheet.ts +24 -24
- package/dist/ui/components/skeleton/skeleton.test.ts +8 -0
- package/dist/ui/components/skeleton/skeleton.ts +13 -8
- package/dist/ui/components/slider/slider.test.ts +13 -1
- package/dist/ui/components/slider/slider.ts +16 -3
- package/dist/ui/components/spinner/spinner.test.ts +16 -1
- package/dist/ui/components/spinner/spinner.ts +15 -7
- package/dist/ui/components/switch/switch.test.ts +13 -2
- package/dist/ui/components/switch/switch.ts +15 -1
- package/dist/ui/components/tab-bar/tab-bar.test.ts +32 -6
- package/dist/ui/components/tab-bar/tab-bar.ts +28 -24
- package/dist/ui/components/tabs/tabs.test.ts +34 -32
- package/dist/ui/components/tabs/tabs.ts +56 -73
- package/dist/ui/components/textarea/textarea.test.ts +11 -1
- package/dist/ui/components/textarea/textarea.ts +77 -71
- package/dist/ui/components/toggle/toggle.test.ts +47 -16
- package/dist/ui/components/toggle/toggle.ts +78 -80
- package/dist/ui/components/typography/typography.test.ts +16 -0
- package/dist/ui/components/typography/typography.ts +12 -4
- package/package.json +1 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type ElementRef,
|
|
3
2
|
Component,
|
|
3
|
+
ElementRef,
|
|
4
4
|
ViewEncapsulation,
|
|
5
5
|
booleanAttribute,
|
|
6
6
|
computed,
|
|
7
7
|
contentChildren,
|
|
8
|
+
effect,
|
|
8
9
|
inject,
|
|
9
10
|
input,
|
|
10
11
|
model,
|
|
11
|
-
viewChild,
|
|
12
12
|
} from '@angular/core';
|
|
13
|
-
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
13
|
+
import { type BaseLynxElement, LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
14
14
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
15
15
|
|
|
16
16
|
import {
|
|
@@ -86,23 +86,25 @@ const itemIconColor = () => {
|
|
|
86
86
|
|
|
87
87
|
@Component({
|
|
88
88
|
selector: 'ui-toggle',
|
|
89
|
-
imports: [LYNX_ELEMENTS],
|
|
90
89
|
encapsulation: ViewEncapsulation.None,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
90
|
+
// Each toggle, group and item is its host element, styled through `host`,
|
|
91
|
+
// rather than a view inside the host. Angular gives a component's host
|
|
92
|
+
// every static attribute the app writes on it, so `<ui-toggle
|
|
93
|
+
// class="px-6">` puts `px-6` on the host whatever the component does with
|
|
94
|
+
// its `class` input: with a styled view inside as well, the padding applied
|
|
95
|
+
// twice. On the host alone, a class also acts where the toggle sits in its
|
|
96
|
+
// parent's layout, and an item's accessibility-label names one element.
|
|
97
|
+
host: {
|
|
98
|
+
'[class]': 'containerClass()',
|
|
99
|
+
'(bindtouchstart)': 'onPressStart()',
|
|
100
|
+
'(bindtouchend)': 'onPressEnd()',
|
|
101
|
+
'(bindtouchcancel)': 'onPressCancel()',
|
|
102
|
+
'(bindmousedown)': 'onPressStart()',
|
|
103
|
+
'(bindmouseup)': 'onPressEnd()',
|
|
104
|
+
'(bindmouseleave)': 'onPressCancel()',
|
|
105
|
+
'(bindtap)': 'onTap()',
|
|
106
|
+
},
|
|
107
|
+
template: '<ng-content />',
|
|
106
108
|
})
|
|
107
109
|
export class UiToggle {
|
|
108
110
|
readonly pressed = model(false);
|
|
@@ -111,7 +113,7 @@ export class UiToggle {
|
|
|
111
113
|
readonly disabled = input(false, { transform: booleanAttribute });
|
|
112
114
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
113
115
|
|
|
114
|
-
readonly
|
|
116
|
+
readonly #host = inject(ElementRef).nativeElement;
|
|
115
117
|
#pressAnim?: AnimationHandle;
|
|
116
118
|
|
|
117
119
|
protected readonly containerClass = computed(() =>
|
|
@@ -126,18 +128,18 @@ export class UiToggle {
|
|
|
126
128
|
protected onPressStart(): void {
|
|
127
129
|
if (this.disabled()) return;
|
|
128
130
|
this.#pressAnim?.cancel();
|
|
129
|
-
this.#pressAnim = pressDown(this
|
|
131
|
+
this.#pressAnim = pressDown(this.#host);
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
protected onPressEnd(): void {
|
|
133
135
|
if (this.disabled()) return;
|
|
134
136
|
this.#pressAnim?.cancel();
|
|
135
|
-
this.#pressAnim = pressRelease(this
|
|
137
|
+
this.#pressAnim = pressRelease(this.#host);
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
protected onPressCancel(): void {
|
|
139
141
|
this.#pressAnim?.cancel();
|
|
140
|
-
this.#pressAnim = pressRelease(this
|
|
142
|
+
this.#pressAnim = pressRelease(this.#host);
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
protected onTap(): void {
|
|
@@ -163,7 +165,7 @@ export class UiToggle {
|
|
|
163
165
|
*/
|
|
164
166
|
@Component({
|
|
165
167
|
selector: 'ui-toggle-group',
|
|
166
|
-
imports: [
|
|
168
|
+
imports: [UiSegmentedIndicator],
|
|
167
169
|
encapsulation: ViewEncapsulation.None,
|
|
168
170
|
providers: [
|
|
169
171
|
UiSegmentedControl,
|
|
@@ -171,16 +173,15 @@ export class UiToggle {
|
|
|
171
173
|
],
|
|
172
174
|
// The mouse leaving the row lets go of an indicator dragged in the web
|
|
173
175
|
// preview, where a drag gets no touchcancel.
|
|
176
|
+
host: {
|
|
177
|
+
'[class]': 'containerClass()',
|
|
178
|
+
'(bindmouseleave)': 'segmentedControl.cancel()',
|
|
179
|
+
},
|
|
174
180
|
template: `
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
@if (segmentedControl.underlay()) {
|
|
180
|
-
<ui-segmented-indicator />
|
|
181
|
-
}
|
|
182
|
-
<ng-content />
|
|
183
|
-
</view>
|
|
181
|
+
@if (segmentedControl.underlay()) {
|
|
182
|
+
<ui-segmented-indicator />
|
|
183
|
+
}
|
|
184
|
+
<ng-content />
|
|
184
185
|
`,
|
|
185
186
|
})
|
|
186
187
|
export class UiToggleGroup implements SegmentedHost {
|
|
@@ -254,38 +255,32 @@ export class UiToggleGroup implements SegmentedHost {
|
|
|
254
255
|
encapsulation: ViewEncapsulation.None,
|
|
255
256
|
// Icons projected into the item inject this: they're declared inside it.
|
|
256
257
|
providers: [{ provide: UI_ICON_COLOR, useFactory: itemIconColor }],
|
|
258
|
+
// The accessibility attributes are attribute bindings: a property binding
|
|
259
|
+
// on a component's host is checked against the DOM schema, which has no
|
|
260
|
+
// `accessibility-label`.
|
|
257
261
|
host: {
|
|
258
|
-
'[class]': '
|
|
262
|
+
'[class]': 'containerClass()',
|
|
263
|
+
'[style]': 'containerStyle()',
|
|
264
|
+
'[attr.accessibility-element]': "accessibilityLabel() ? 'true' : null",
|
|
265
|
+
'[attr.accessibility-traits]': "accessibilityLabel() ? 'button' : null",
|
|
266
|
+
'[attr.accessibility-label]': 'accessibilityLabel() || null',
|
|
259
267
|
// The segmented control places its indicator from each item's width.
|
|
260
268
|
'(bindlayoutchange)': 'measure($event)',
|
|
269
|
+
'(bindtouchstart)': 'onPressStart($event)',
|
|
270
|
+
'(bindtouchmove)': 'onPressMove($event)',
|
|
271
|
+
'(bindtouchend)': 'onPressEnd()',
|
|
272
|
+
'(bindtouchcancel)': 'onPressCancel()',
|
|
273
|
+
'(bindmousedown)': 'onPressStart($event)',
|
|
274
|
+
'(bindmousemove)': 'onPressMove($event)',
|
|
275
|
+
'(bindmouseup)': 'onPressEnd()',
|
|
276
|
+
'(bindmouseleave)': 'onPointerLeave()',
|
|
277
|
+
'(bindtap)': 'onTap()',
|
|
261
278
|
},
|
|
262
279
|
template: `
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
<
|
|
267
|
-
#container
|
|
268
|
-
[class]="containerClass()"
|
|
269
|
-
[style]="containerStyle()"
|
|
270
|
-
[attr.accessibility-element]="accessibilityLabel() ? 'true' : null"
|
|
271
|
-
[attr.accessibility-traits]="accessibilityLabel() ? 'button' : null"
|
|
272
|
-
[attr.accessibility-label]="accessibilityLabel() || null"
|
|
273
|
-
[lynxAccessibilityTraits]="{ selected: selectedTrait() }"
|
|
274
|
-
(bindtouchstart)="onPressStart($event)"
|
|
275
|
-
(bindtouchmove)="onPressMove($event)"
|
|
276
|
-
(bindtouchend)="onPressEnd()"
|
|
277
|
-
(bindtouchcancel)="onPressCancel()"
|
|
278
|
-
(bindmousedown)="onPressStart($event)"
|
|
279
|
-
(bindmousemove)="onPressMove($event)"
|
|
280
|
-
(bindmouseup)="onPressEnd()"
|
|
281
|
-
(bindmouseleave)="onPointerLeave()"
|
|
282
|
-
(bindtap)="onTap()"
|
|
283
|
-
>
|
|
284
|
-
@if (stateLayerClass(); as stateLayer) {
|
|
285
|
-
<view [class]="stateLayer"></view>
|
|
286
|
-
}
|
|
287
|
-
<ng-content />
|
|
288
|
-
</view>
|
|
280
|
+
@if (stateLayerClass(); as stateLayer) {
|
|
281
|
+
<view [class]="stateLayer"></view>
|
|
282
|
+
}
|
|
283
|
+
<ng-content />
|
|
289
284
|
`,
|
|
290
285
|
})
|
|
291
286
|
export class UiToggleGroupItem {
|
|
@@ -303,17 +298,27 @@ export class UiToggleGroupItem {
|
|
|
303
298
|
});
|
|
304
299
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
305
300
|
|
|
306
|
-
|
|
307
|
-
readonly containerRef = viewChild.required<ElementRef>('container');
|
|
301
|
+
readonly #host: BaseLynxElement = inject(ElementRef).nativeElement;
|
|
308
302
|
#pressAnim?: AnimationHandle;
|
|
309
303
|
|
|
310
304
|
protected readonly isSelected = computed(() =>
|
|
311
305
|
this.#group.isSelected(this.itemValue()),
|
|
312
306
|
);
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* A labeled item is `selected` to a screen reader while pressed, which iOS
|
|
310
|
+
* reads. The trait is added to the host's own `button`, as
|
|
311
|
+
* lynxAccessibilityTraits does on an element in a template: on iOS it
|
|
312
|
+
* reads `button,selected`, and Android, which reads one trait, keeps
|
|
313
|
+
* `button`. Only a labeled item is an accessibility element to be
|
|
314
|
+
* `selected`.
|
|
315
|
+
*/
|
|
316
|
+
protected readonly markingSelected = effect(() => {
|
|
317
|
+
this.#host.setAccessibilityTrait(
|
|
318
|
+
'selected',
|
|
319
|
+
!!this.accessibilityLabel() && this.isSelected(),
|
|
320
|
+
);
|
|
321
|
+
});
|
|
317
322
|
|
|
318
323
|
/** Whether the item is a segment of a segmented control. */
|
|
319
324
|
readonly #segment = computed(() => this.#segmented.enabled());
|
|
@@ -327,20 +332,13 @@ export class UiToggleGroupItem {
|
|
|
327
332
|
this.#segment() ? this.#segmented.iconColor(this.itemValue()) : undefined,
|
|
328
333
|
);
|
|
329
334
|
|
|
330
|
-
/**
|
|
331
|
-
* A segment's host is the track's flex item, an equal share of it. Angular
|
|
332
|
-
* puts the app's static `class` on the host as well as on the item, so the
|
|
333
|
-
* two are merged here: `class="flex-[2]"` makes a wider segment.
|
|
334
|
-
*/
|
|
335
|
-
protected readonly hostClass = computed(() =>
|
|
336
|
-
this.#segment()
|
|
337
|
-
? cn(this.#segmented.segmentHostClass, this.userClass())
|
|
338
|
-
: '',
|
|
339
|
-
);
|
|
340
|
-
|
|
341
335
|
protected readonly containerClass = computed(() => {
|
|
342
336
|
if (this.#segment()) {
|
|
337
|
+
// The host is both the segment's slot in the track (an equal share of
|
|
338
|
+
// it) and its own view, so an app's `class="flex-[2]"` makes a wider
|
|
339
|
+
// segment.
|
|
343
340
|
return cn(
|
|
341
|
+
this.#segmented.segmentHostClass,
|
|
344
342
|
this.#segmented.segmentClass(
|
|
345
343
|
this.itemValue(),
|
|
346
344
|
segmentPlace(this.#group.items(), this),
|
|
@@ -384,7 +382,7 @@ export class UiToggleGroupItem {
|
|
|
384
382
|
return;
|
|
385
383
|
}
|
|
386
384
|
this.#pressAnim?.cancel();
|
|
387
|
-
this.#pressAnim = pressDown(this
|
|
385
|
+
this.#pressAnim = pressDown(this.#host);
|
|
388
386
|
}
|
|
389
387
|
|
|
390
388
|
protected onPressMove(event: unknown): void {
|
|
@@ -398,7 +396,7 @@ export class UiToggleGroupItem {
|
|
|
398
396
|
return;
|
|
399
397
|
}
|
|
400
398
|
this.#pressAnim?.cancel();
|
|
401
|
-
this.#pressAnim = pressRelease(this
|
|
399
|
+
this.#pressAnim = pressRelease(this.#host);
|
|
402
400
|
}
|
|
403
401
|
|
|
404
402
|
protected onPressCancel(): void {
|
|
@@ -428,6 +426,6 @@ export class UiToggleGroupItem {
|
|
|
428
426
|
|
|
429
427
|
#release(): void {
|
|
430
428
|
this.#pressAnim?.cancel();
|
|
431
|
-
this.#pressAnim = pressRelease(this
|
|
429
|
+
this.#pressAnim = pressRelease(this.#host);
|
|
432
430
|
}
|
|
433
431
|
}
|
|
@@ -22,6 +22,12 @@ class TypographyHost {
|
|
|
22
22
|
| 'muted' = 'p';
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
@Component({
|
|
26
|
+
imports: [UiText],
|
|
27
|
+
template: `<ui-text variant="muted" class="mb-4 text-center">Note</ui-text>`,
|
|
28
|
+
})
|
|
29
|
+
class StaticClassHost {}
|
|
30
|
+
|
|
25
31
|
describe('typographyVariants', () => {
|
|
26
32
|
it('returns default variant classes', () => {
|
|
27
33
|
expect(typographyVariants()).toContain('text-base');
|
|
@@ -52,4 +58,14 @@ describe('UiText', () => {
|
|
|
52
58
|
const { getByText } = await render(UiText);
|
|
53
59
|
expect(getByText).toBeTruthy();
|
|
54
60
|
});
|
|
61
|
+
|
|
62
|
+
// A class on a component lands on its host too (issue #181): the text is
|
|
63
|
+
// one <text>, so the class styles one element.
|
|
64
|
+
it('is one text, with the class written on it', async () => {
|
|
65
|
+
const { container, getByText } = await render(StaticClassHost);
|
|
66
|
+
const text = getByText('Note');
|
|
67
|
+
expect(text.tagName.toLowerCase()).toBe('text');
|
|
68
|
+
expect(text.classList).toContain('text-muted-foreground');
|
|
69
|
+
expect([...container.querySelectorAll('.mb-4')]).toEqual([text]);
|
|
70
|
+
});
|
|
55
71
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component, ViewEncapsulation, computed, input } from '@angular/core';
|
|
2
|
-
import {
|
|
2
|
+
import { registerHostElement } from '@blotch/angular-lynx';
|
|
3
3
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
import { cn } from '../../utils/cn';
|
|
@@ -27,12 +27,18 @@ export type TypographyVariant = NonNullable<
|
|
|
27
27
|
VariantProps<typeof typographyVariants>['variant']
|
|
28
28
|
>;
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* The text is its host, a `<text>` (registerHostElement, below), styled
|
|
32
|
+
* through `host`. Angular gives a component's host every static attribute the
|
|
33
|
+
* app writes on it, so `<ui-text class="mb-4">` puts `mb-4` on the host
|
|
34
|
+
* whatever the component does: with a styled `<text>` inside a view host as
|
|
35
|
+
* well, the margin applied twice.
|
|
36
|
+
*/
|
|
30
37
|
@Component({
|
|
31
38
|
selector: 'ui-text',
|
|
32
|
-
standalone: true,
|
|
33
|
-
imports: [LYNX_ELEMENTS],
|
|
34
39
|
encapsulation: ViewEncapsulation.None,
|
|
35
|
-
|
|
40
|
+
host: { '[class]': 'textClass()' },
|
|
41
|
+
template: '<ng-content />',
|
|
36
42
|
})
|
|
37
43
|
export class UiText {
|
|
38
44
|
readonly variant = input<TypographyVariant>('p');
|
|
@@ -42,3 +48,5 @@ export class UiText {
|
|
|
42
48
|
cn(typographyVariants({ variant: this.variant() }), this.userClass()),
|
|
43
49
|
);
|
|
44
50
|
}
|
|
51
|
+
|
|
52
|
+
registerHostElement(UiText, 'text');
|