@blotch/dolan 22.2.4 → 22.2.5
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/add.test.js +2 -0
- package/dist/commands/add.test.js.map +1 -1
- package/dist/commands/doctor.js +78 -21
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/doctor.test.js +167 -26
- package/dist/commands/doctor.test.js.map +1 -1
- package/dist/commands/init.test.js +4 -7
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/update.d.ts +3 -1
- package/dist/commands/update.js +78 -15
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/update.test.js +257 -2
- package/dist/commands/update.test.js.map +1 -1
- package/dist/lockfile.d.ts +10 -0
- package/dist/lockfile.js +20 -1
- package/dist/lockfile.js.map +1 -1
- package/dist/lockfile.test.js +43 -3
- package/dist/lockfile.test.js.map +1 -1
- package/dist/registry.js +73 -11
- package/dist/registry.js.map +1 -1
- package/dist/registry.test.js +16 -3
- package/dist/registry.test.js.map +1 -1
- package/dist/test-utils.d.ts +24 -2
- package/dist/test-utils.js +41 -2
- package/dist/test-utils.js.map +1 -1
- package/dist/ui/boolean-inputs.test.ts +76 -0
- package/dist/ui/components/action-sheet/action-sheet.test.ts +370 -1
- package/dist/ui/components/action-sheet/action-sheet.ts +306 -28
- package/dist/ui/components/action-sheet/index.ts +1 -0
- package/dist/ui/components/alert-dialog/alert-dialog.test.ts +552 -8
- package/dist/ui/components/alert-dialog/alert-dialog.ts +644 -36
- package/dist/ui/components/alert-dialog/index.ts +5 -0
- package/dist/ui/components/area-chart/area-chart.ts +3 -2
- package/dist/ui/components/badge/badge.ts +2 -1
- package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +968 -3
- package/dist/ui/components/bottom-sheet/bottom-sheet.ts +1021 -95
- package/dist/ui/components/bottom-sheet/index.ts +7 -1
- package/dist/ui/components/button/button.test.ts +36 -1
- package/dist/ui/components/button/button.ts +17 -7
- package/dist/ui/components/button-group/button-group.test.ts +53 -9
- package/dist/ui/components/button-group/button-group.ts +67 -21
- package/dist/ui/components/card/card.test.ts +16 -0
- package/dist/ui/components/card/card.ts +2 -1
- package/dist/ui/components/chart/chart-presets.ts +6 -6
- package/dist/ui/components/chart/chart.test.ts +14 -0
- package/dist/ui/components/chart/chart.ts +10 -3
- package/dist/ui/components/checkbox/checkbox.ts +2 -1
- package/dist/ui/components/collapsible/collapsible.ts +2 -1
- package/dist/ui/components/dialog/dialog.ts +6 -6
- package/dist/ui/components/dock/dock-scroll.ts +9 -0
- package/dist/ui/components/dock/dock-search.test.ts +594 -0
- package/dist/ui/components/dock/dock.test.ts +4 -1
- package/dist/ui/components/dock/dock.ts +529 -51
- package/dist/ui/components/dynamic-color/dynamic-color.test.ts +144 -0
- package/dist/ui/components/dynamic-color/dynamic-color.ts +146 -0
- package/dist/ui/components/dynamic-color/index.ts +5 -0
- package/dist/ui/components/glass/glass.test.ts +39 -1
- package/dist/ui/components/glass/glass.ts +87 -17
- package/dist/ui/components/glass/index.ts +1 -0
- package/dist/ui/components/icon/icon-theme.test.ts +28 -0
- package/dist/ui/components/icon/icon-theme.ts +38 -7
- package/dist/ui/components/input/input.ts +2 -1
- package/dist/ui/components/label/label.ts +8 -2
- package/dist/ui/components/line-chart/line-chart.ts +3 -2
- package/dist/ui/components/list/list.ts +5 -4
- package/dist/ui/components/menu/menu-popup.test.ts +18 -14
- package/dist/ui/components/menu/menu-popup.ts +24 -3
- package/dist/ui/components/menu/menu.test.ts +99 -5
- package/dist/ui/components/menu/menu.ts +74 -34
- package/dist/ui/components/nav-drawer/nav-drawer.ts +3 -2
- package/dist/ui/components/navigation-bar/index.ts +1 -0
- package/dist/ui/components/navigation-bar/navigation-bar.test.ts +425 -0
- package/dist/ui/components/navigation-bar/navigation-bar.ts +411 -0
- package/dist/ui/components/popover/index.ts +9 -0
- package/dist/ui/components/popover/popover-panel.test.ts +735 -0
- package/dist/ui/components/popover/popover-panel.ts +603 -0
- package/dist/ui/components/popover/popover.test.ts +286 -0
- package/dist/ui/components/popover/popover.ts +207 -0
- package/dist/ui/components/progress/progress.ts +2 -1
- package/dist/ui/components/radio-group/radio-group.ts +2 -1
- package/dist/ui/components/scroll-area/scroll-area.ts +4 -3
- package/dist/ui/components/search-field/index.ts +15 -0
- package/dist/ui/components/search-field/keyboard.test.ts +147 -0
- package/dist/ui/components/search-field/keyboard.ts +123 -0
- package/dist/ui/components/search-field/search-field.test.ts +549 -0
- package/dist/ui/components/search-field/search-field.ts +628 -0
- package/dist/ui/components/segmented-control/index.ts +8 -0
- package/dist/ui/components/segmented-control/segmented-control.test.ts +861 -0
- package/dist/ui/components/segmented-control/segmented-control.ts +884 -0
- package/dist/ui/components/select/select.ts +2 -1
- package/dist/ui/components/sheet/sheet.test.ts +90 -3
- package/dist/ui/components/sheet/sheet.ts +59 -4
- package/dist/ui/components/slider/index.ts +1 -0
- package/dist/ui/components/slider/slider.test.ts +695 -0
- package/dist/ui/components/slider/slider.ts +901 -0
- package/dist/ui/components/switch/index.ts +1 -1
- package/dist/ui/components/switch/switch.test.ts +464 -48
- package/dist/ui/components/switch/switch.ts +613 -43
- package/dist/ui/components/tabs/index.ts +7 -1
- package/dist/ui/components/tabs/tabs.test.ts +278 -2
- package/dist/ui/components/tabs/tabs.ts +233 -30
- package/dist/ui/components/textarea/textarea.ts +2 -1
- package/dist/ui/components/toggle/index.ts +1 -0
- package/dist/ui/components/toggle/toggle.test.ts +310 -4
- package/dist/ui/components/toggle/toggle.ts +241 -26
- package/dist/ui/components/toolbar/bar-scroll.test.ts +310 -0
- package/dist/ui/components/toolbar/bar-scroll.ts +372 -0
- package/dist/ui/components/toolbar/index.ts +12 -0
- package/dist/ui/components/toolbar/toolbar.test.ts +438 -0
- package/dist/ui/components/toolbar/toolbar.ts +535 -0
- package/dist/utils/analyze.d.ts +10 -5
- package/dist/utils/analyze.js +3 -2
- package/dist/utils/analyze.js.map +1 -1
- package/dist/utils/installed.d.ts +13 -1
- package/dist/utils/installed.js +17 -0
- package/dist/utils/installed.js.map +1 -1
- package/dist/utils/installed.test.js +39 -1
- package/dist/utils/installed.test.js.map +1 -1
- package/dist/utils/tailwind.d.ts +25 -5
- package/dist/utils/tailwind.js +53 -14
- package/dist/utils/tailwind.js.map +1 -1
- package/dist/utils/tailwind.test.js +85 -22
- package/dist/utils/tailwind.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Component,
|
|
4
4
|
ViewEncapsulation,
|
|
5
5
|
computed,
|
|
6
|
+
contentChildren,
|
|
6
7
|
effect,
|
|
7
8
|
inject,
|
|
8
9
|
input,
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
signal,
|
|
12
13
|
viewChild,
|
|
13
14
|
} from '@angular/core';
|
|
14
|
-
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
15
|
+
import { LYNX_ELEMENTS, LynxAccessibilitySettings } from '@blotch/angular-lynx';
|
|
15
16
|
|
|
16
17
|
import {
|
|
17
18
|
type AnimationHandle,
|
|
@@ -21,10 +22,37 @@ import {
|
|
|
21
22
|
revealIn,
|
|
22
23
|
} from '../../utils/animate';
|
|
23
24
|
import { cn } from '../../utils/cn';
|
|
25
|
+
import {
|
|
26
|
+
type SegmentedHost,
|
|
27
|
+
segmentPlace,
|
|
28
|
+
UI_SEGMENTED_HOST,
|
|
29
|
+
UiSegmentedControl,
|
|
30
|
+
UiSegmentedIndicator,
|
|
31
|
+
} from '../segmented-control/segmented-control';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A tab list's look: shadcn's tabs, or `'segmented'` for the platform's
|
|
35
|
+
* segmented control (see UiTabsList).
|
|
36
|
+
*/
|
|
37
|
+
export type TabsListVariant = 'default' | 'segmented';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* `UiTabsTrigger`, for the list's content query. A call rather than the
|
|
41
|
+
* class itself: for a class, Angular's test build keeps an arrow function
|
|
42
|
+
* that nothing calls, which an app's coverage report would show as
|
|
43
|
+
* uncovered.
|
|
44
|
+
*/
|
|
45
|
+
const triggerType = () => UiTabsTrigger;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The list, as the segmented control it provides reads it. Named, not
|
|
49
|
+
* inline: the component's metadata keeps its own copy of an inline factory,
|
|
50
|
+
* which nothing calls.
|
|
51
|
+
*/
|
|
52
|
+
const tabsListHost = (): SegmentedHost => inject(UiTabsList).segmentedHost;
|
|
24
53
|
|
|
25
54
|
@Component({
|
|
26
55
|
selector: 'ui-tabs',
|
|
27
|
-
standalone: true,
|
|
28
56
|
imports: [LYNX_ELEMENTS],
|
|
29
57
|
encapsulation: ViewEncapsulation.None,
|
|
30
58
|
template: `
|
|
@@ -95,47 +123,133 @@ export class UiTabs {
|
|
|
95
123
|
}
|
|
96
124
|
}
|
|
97
125
|
|
|
126
|
+
/**
|
|
127
|
+
* The row of tab triggers.
|
|
128
|
+
*
|
|
129
|
+
* `variant="segmented"` draws the platform's segmented control instead (see
|
|
130
|
+
* UiSegmentedControl), Apple's control for switching between closely
|
|
131
|
+
* related views: iOS 26's capsule with a thumb that lifts into Liquid Glass
|
|
132
|
+
* as it slides between the tabs (it can be dragged across them too), iOS 13
|
|
133
|
+
* to 18's raised thumb before iOS 26, and Material 3 Expressive's connected
|
|
134
|
+
* button group on Android. It fills the width it's given, and the tabs
|
|
135
|
+
* share it.
|
|
136
|
+
*
|
|
137
|
+
* The row is a tab bar to VoiceOver (`tabbar`), which reads each trigger as
|
|
138
|
+
* a tab, and the selected one as selected.
|
|
139
|
+
*/
|
|
98
140
|
@Component({
|
|
99
141
|
selector: 'ui-tabs-list',
|
|
100
|
-
|
|
101
|
-
imports: [LYNX_ELEMENTS],
|
|
142
|
+
imports: [LYNX_ELEMENTS, UiSegmentedIndicator],
|
|
102
143
|
encapsulation: ViewEncapsulation.None,
|
|
144
|
+
providers: [
|
|
145
|
+
UiSegmentedControl,
|
|
146
|
+
{ provide: UI_SEGMENTED_HOST, useFactory: tabsListHost },
|
|
147
|
+
],
|
|
148
|
+
// The mouse leaving the row lets go of an indicator dragged in the web
|
|
149
|
+
// preview, where a drag gets no touchcancel.
|
|
103
150
|
template: `
|
|
104
|
-
<view
|
|
151
|
+
<view
|
|
152
|
+
accessibility-traits="tabbar"
|
|
153
|
+
[class]="listClass()"
|
|
154
|
+
(bindmouseleave)="segmentedControl.cancel()"
|
|
155
|
+
>
|
|
156
|
+
@if (segmentedControl.underlay()) {
|
|
157
|
+
<ui-segmented-indicator />
|
|
158
|
+
}
|
|
105
159
|
<ng-content />
|
|
106
160
|
</view>
|
|
107
161
|
`,
|
|
108
162
|
})
|
|
109
163
|
export class UiTabsList {
|
|
164
|
+
/**
|
|
165
|
+
* `'default'`, or `'segmented'` for the platform's segmented control.
|
|
166
|
+
*/
|
|
167
|
+
readonly variant = input<TabsListVariant>('default');
|
|
110
168
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
111
169
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.
|
|
170
|
+
readonly #tabs = inject(UiTabs);
|
|
171
|
+
protected readonly segmentedControl = inject(UiSegmentedControl);
|
|
172
|
+
/** The triggers in the list, in order. */
|
|
173
|
+
readonly triggers = contentChildren(triggerType(), { descendants: true });
|
|
174
|
+
|
|
175
|
+
/** What the segmented control reads from the list. */
|
|
176
|
+
readonly segmentedHost: SegmentedHost = {
|
|
177
|
+
segmented: computed(() => this.variant() === 'segmented'),
|
|
178
|
+
segmentValues: computed(() =>
|
|
179
|
+
this.triggers().map((trigger) => trigger.triggerValue()),
|
|
122
180
|
),
|
|
181
|
+
selectedValues: computed(() => [this.#tabs.value()]),
|
|
182
|
+
// One tab is active at a time, and tabs are never disabled.
|
|
183
|
+
multiple: signal(false).asReadonly(),
|
|
184
|
+
disabled: signal(false).asReadonly(),
|
|
185
|
+
select: (value) => this.#tabs.select(value),
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
protected readonly listClass = computed(() =>
|
|
189
|
+
this.segmentedControl.enabled()
|
|
190
|
+
? // A segmented control fills the width it's given, as UIKit's does
|
|
191
|
+
// in a form, and its tabs share it.
|
|
192
|
+
cn(this.segmentedControl.trackClass(), this.userClass())
|
|
193
|
+
: cn(
|
|
194
|
+
// `self-start` keeps the tab bar hugging its triggers. The parent <ui-tabs>
|
|
195
|
+
// is a `flex flex-col` column, and in Lynx's linear/flex layout a child with
|
|
196
|
+
// no explicit width stretches to fill the parent's cross axis — so without
|
|
197
|
+
// it the muted pill spans the full width, leaving empty space around the
|
|
198
|
+
// tabs. Same lever as the badge fix: Lynx has no `inline-flex`, so
|
|
199
|
+
// align-self is how you opt a child into shrink-to-fit.
|
|
200
|
+
'flex-row items-center self-start rounded-lg bg-muted p-1 flex',
|
|
201
|
+
this.userClass(),
|
|
202
|
+
),
|
|
123
203
|
);
|
|
124
204
|
}
|
|
125
205
|
|
|
126
206
|
@Component({
|
|
127
207
|
selector: 'ui-tabs-trigger',
|
|
128
|
-
standalone: true,
|
|
129
208
|
imports: [LYNX_ELEMENTS],
|
|
130
209
|
encapsulation: ViewEncapsulation.None,
|
|
210
|
+
host: {
|
|
211
|
+
'[class]': 'hostClass()',
|
|
212
|
+
// The segmented control places its indicator from each tab's width.
|
|
213
|
+
'(bindlayoutchange)': 'measure($event)',
|
|
214
|
+
},
|
|
215
|
+
// The label is the accessibility element, a button (a tab, in the list's
|
|
216
|
+
// tab bar) named by its own text. A view would need a label of its own.
|
|
217
|
+
// The selected tab is `selected` to a screen reader, which iOS reads:
|
|
218
|
+
// lynxAccessibilityTraits adds the trait to `button` on iOS, and leaves
|
|
219
|
+
// Android, which reads one trait, with `button`.
|
|
131
220
|
template: `
|
|
132
|
-
<view
|
|
133
|
-
|
|
221
|
+
<view
|
|
222
|
+
#trigger
|
|
223
|
+
[class]="triggerClass()"
|
|
224
|
+
[style]="triggerStyle()"
|
|
225
|
+
(bindtap)="onTap()"
|
|
226
|
+
(bindtouchstart)="press($event)"
|
|
227
|
+
(bindtouchmove)="move($event)"
|
|
228
|
+
(bindtouchend)="release()"
|
|
229
|
+
(bindtouchcancel)="cancel()"
|
|
230
|
+
(bindmousedown)="press($event)"
|
|
231
|
+
(bindmousemove)="move($event)"
|
|
232
|
+
(bindmouseup)="release()"
|
|
233
|
+
(bindmouseleave)="leave()"
|
|
234
|
+
>
|
|
235
|
+
@if (stateLayerClass(); as stateLayer) {
|
|
236
|
+
<view [class]="stateLayer"></view>
|
|
237
|
+
}
|
|
238
|
+
<text
|
|
239
|
+
accessibility-traits="button"
|
|
240
|
+
[lynxAccessibilityTraits]="{ selected: isActive() }"
|
|
241
|
+
[class]="textClass()"
|
|
242
|
+
><ng-content
|
|
243
|
+
/></text>
|
|
134
244
|
</view>
|
|
135
245
|
`,
|
|
136
246
|
})
|
|
137
247
|
export class UiTabsTrigger {
|
|
138
248
|
readonly #tabs = inject(UiTabs);
|
|
249
|
+
readonly #settings = inject(LynxAccessibilitySettings);
|
|
250
|
+
// Both absent for a trigger outside a <ui-tabs-list>.
|
|
251
|
+
readonly #list = inject(UiTabsList, { optional: true });
|
|
252
|
+
readonly #segmented = inject(UiSegmentedControl, { optional: true });
|
|
139
253
|
|
|
140
254
|
readonly triggerValue = input.required<string>({ alias: 'value' });
|
|
141
255
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
@@ -148,6 +262,32 @@ export class UiTabsTrigger {
|
|
|
148
262
|
() => this.#tabs.value() === this.triggerValue(),
|
|
149
263
|
);
|
|
150
264
|
|
|
265
|
+
/**
|
|
266
|
+
* The segmented control this trigger is a segment of, or null when its
|
|
267
|
+
* list is the default tabs (or it has no list).
|
|
268
|
+
*/
|
|
269
|
+
readonly #segment = computed(() =>
|
|
270
|
+
this.#segmented?.enabled() ? this.#segmented : null,
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* A segment's host is the track's flex item, an equal share of it. Angular
|
|
275
|
+
* puts the app's static `class` on the host as well as on the trigger, so
|
|
276
|
+
* the two are merged here: `class="flex-[2]"` makes a wider tab.
|
|
277
|
+
*/
|
|
278
|
+
protected readonly hostClass = computed(() => {
|
|
279
|
+
const segment = this.#segment();
|
|
280
|
+
return segment ? cn(segment.segmentHostClass, this.userClass()) : '';
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
protected readonly triggerStyle = computed(
|
|
284
|
+
() => this.#segment()?.segmentStyle(this.triggerValue()) ?? null,
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
protected readonly stateLayerClass = computed(
|
|
288
|
+
() => this.#segment()?.stateLayerClass(this.triggerValue()) ?? null,
|
|
289
|
+
);
|
|
290
|
+
|
|
151
291
|
/** Registers this trigger with the parent for direction detection. */
|
|
152
292
|
protected readonly registering = effect(() => {
|
|
153
293
|
this.#tabs.registerTab(this.triggerValue());
|
|
@@ -161,8 +301,17 @@ export class UiTabsTrigger {
|
|
|
161
301
|
this.#previousActive = active;
|
|
162
302
|
// The first run only records the starting state, and only a change has
|
|
163
303
|
// anything to animate. (`isActive` is a computed, so the effect only
|
|
164
|
-
// re-runs once it changes; the second check is a safeguard.)
|
|
165
|
-
|
|
304
|
+
// re-runs once it changes; the second check is a safeguard.) A segment
|
|
305
|
+
// doesn't animate itself: the segmented control's indicator moves. Under
|
|
306
|
+
// Reduce Motion the trigger changes at once.
|
|
307
|
+
if (
|
|
308
|
+
wasActive === undefined ||
|
|
309
|
+
wasActive === active ||
|
|
310
|
+
this.#segment() ||
|
|
311
|
+
this.#settings.reduceMotion()
|
|
312
|
+
) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
166
315
|
|
|
167
316
|
this.#anim?.cancel();
|
|
168
317
|
|
|
@@ -195,8 +344,17 @@ export class UiTabsTrigger {
|
|
|
195
344
|
}
|
|
196
345
|
});
|
|
197
346
|
|
|
198
|
-
protected readonly triggerClass = computed(() =>
|
|
199
|
-
|
|
347
|
+
protected readonly triggerClass = computed(() => {
|
|
348
|
+
const segment = this.#segment();
|
|
349
|
+
if (segment) {
|
|
350
|
+
// The list provides the segmented control, so a segment is in one.
|
|
351
|
+
const place = segmentPlace(this.#list!.triggers(), this);
|
|
352
|
+
return cn(
|
|
353
|
+
segment.segmentClass(this.triggerValue(), place),
|
|
354
|
+
this.userClass(),
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return cn(
|
|
200
358
|
// No `flex-1` here: now that the list shrinks to fit its content (see
|
|
201
359
|
// `self-start` in UiTabsList), stretching triggers to equal width is
|
|
202
360
|
// pointless, and `flex-basis: 0` growth combined with Lynx treating
|
|
@@ -206,24 +364,62 @@ export class UiTabsTrigger {
|
|
|
206
364
|
'items-center rounded-sm px-3 py-1.5 flex justify-center',
|
|
207
365
|
this.isActive() ? 'bg-background' : 'bg-transparent',
|
|
208
366
|
this.userClass(),
|
|
209
|
-
)
|
|
210
|
-
);
|
|
367
|
+
);
|
|
368
|
+
});
|
|
211
369
|
|
|
212
|
-
protected readonly textClass = computed(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
370
|
+
protected readonly textClass = computed(
|
|
371
|
+
() =>
|
|
372
|
+
this.#segment()?.labelClass(this.triggerValue()) ??
|
|
373
|
+
cn(
|
|
374
|
+
'text-sm font-medium',
|
|
375
|
+
this.isActive() ? 'text-foreground' : 'text-muted-foreground',
|
|
376
|
+
),
|
|
217
377
|
);
|
|
218
378
|
|
|
219
379
|
select(): void {
|
|
220
380
|
this.#tabs.select(this.triggerValue());
|
|
221
381
|
}
|
|
382
|
+
|
|
383
|
+
protected measure(event: unknown): void {
|
|
384
|
+
this.#segmented?.measure(this.triggerValue(), event);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* A press. A segment's touches go to the segmented control, which draws
|
|
389
|
+
* their feedback and moves its indicator.
|
|
390
|
+
*/
|
|
391
|
+
protected press(event: unknown): void {
|
|
392
|
+
this.#segment()?.press(this.triggerValue(), event);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
protected move(event: unknown): void {
|
|
396
|
+
this.#segment()?.move(event);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
protected release(): void {
|
|
400
|
+
this.#segment()?.release();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
protected cancel(): void {
|
|
404
|
+
this.#segment()?.cancel();
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
protected leave(): void {
|
|
408
|
+
this.#segment()?.leave(this.triggerValue());
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* A tap that ends a drag of the indicator doesn't select this tab: the
|
|
413
|
+
* drag chose already.
|
|
414
|
+
*/
|
|
415
|
+
protected onTap(): void {
|
|
416
|
+
if (this.#segment()?.tap(this.triggerValue()) === false) return;
|
|
417
|
+
this.select();
|
|
418
|
+
}
|
|
222
419
|
}
|
|
223
420
|
|
|
224
421
|
@Component({
|
|
225
422
|
selector: 'ui-tabs-content',
|
|
226
|
-
standalone: true,
|
|
227
423
|
imports: [LYNX_ELEMENTS],
|
|
228
424
|
encapsulation: ViewEncapsulation.None,
|
|
229
425
|
host: {
|
|
@@ -242,6 +438,10 @@ export class UiTabsTrigger {
|
|
|
242
438
|
// makes the host lay its content out vertically when shown.
|
|
243
439
|
'[style.display]': "isActive() ? 'flex' : 'none'",
|
|
244
440
|
'[style.flexDirection]': "'column'",
|
|
441
|
+
// A hidden panel's 0×0 doesn't stop iOS drawing its text: each line is
|
|
442
|
+
// laid out at 0×0 too, and its glyphs spill out from the panel's corner,
|
|
443
|
+
// over the tab list. Clipping the hidden host to its 0×0 hides them.
|
|
444
|
+
'[style.overflow]': "isActive() ? null : 'hidden'",
|
|
245
445
|
},
|
|
246
446
|
template: `
|
|
247
447
|
<view #content [class]="contentClass()">
|
|
@@ -251,6 +451,7 @@ export class UiTabsTrigger {
|
|
|
251
451
|
})
|
|
252
452
|
export class UiTabsContent {
|
|
253
453
|
readonly #tabs = inject(UiTabs);
|
|
454
|
+
readonly #settings = inject(LynxAccessibilitySettings);
|
|
254
455
|
|
|
255
456
|
readonly contentValue = input.required<string>({ alias: 'value' });
|
|
256
457
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
@@ -282,6 +483,8 @@ export class UiTabsContent {
|
|
|
282
483
|
if (!this.isActive()) return;
|
|
283
484
|
// Defer one tick so the display flip to 'flex' has applied before we animate.
|
|
284
485
|
setTimeout(() => {
|
|
486
|
+
// Under Reduce Motion the panel just appears.
|
|
487
|
+
if (this.#settings.reduceMotion()) return;
|
|
285
488
|
const el = this.contentRef().nativeElement;
|
|
286
489
|
|
|
287
490
|
this.#anim?.cancel();
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
type ElementRef,
|
|
3
3
|
Component,
|
|
4
4
|
ViewEncapsulation,
|
|
5
|
+
booleanAttribute,
|
|
5
6
|
computed,
|
|
6
7
|
effect,
|
|
7
8
|
input,
|
|
@@ -95,7 +96,7 @@ export class UiTextarea implements FormValueControl<string> {
|
|
|
95
96
|
readonly placeholder = input<string>('');
|
|
96
97
|
readonly helperText = input<string>('');
|
|
97
98
|
readonly error = input<string>('');
|
|
98
|
-
readonly disabled = input(false);
|
|
99
|
+
readonly disabled = input(false, { transform: booleanAttribute });
|
|
99
100
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
100
101
|
|
|
101
102
|
// SwiftUI-style lineLimit(min...max). The field opens at minLines, grows with
|