@blotch/dolan 22.2.4 → 22.2.6
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
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
1
2
|
import {
|
|
2
|
-
type ElementRef,
|
|
3
3
|
type OnDestroy,
|
|
4
4
|
Component,
|
|
5
|
+
Directive,
|
|
6
|
+
ElementRef,
|
|
5
7
|
ViewEncapsulation,
|
|
6
8
|
computed,
|
|
7
9
|
effect,
|
|
@@ -27,13 +29,56 @@ import {
|
|
|
27
29
|
slideOut,
|
|
28
30
|
} from '../../utils/animate';
|
|
29
31
|
import { cn } from '../../utils/cn';
|
|
30
|
-
|
|
32
|
+
import { UiGlass } from '../glass/glass';
|
|
33
|
+
import { injectGlassDesign } from '../glass/glass-design';
|
|
34
|
+
import { type PopoverAnchor, UiPopoverPanel } from '../popover/popover-panel';
|
|
35
|
+
|
|
36
|
+
/** How many action sheets there have been, for their glass ids. */
|
|
37
|
+
let sheetCount = 0;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The width of an action sheet opened from a control: iOS's, and Material's
|
|
41
|
+
* widest menu.
|
|
42
|
+
*/
|
|
43
|
+
const ANCHORED_WIDTH = { glass: 270, material: 280 } as const;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* An action sheet: a short list of choices about an action the user took,
|
|
47
|
+
* such as saving or deleting a draft.
|
|
48
|
+
*
|
|
49
|
+
* ```html
|
|
50
|
+
* <ui-button (pressed)="open.set(true)">Share</ui-button>
|
|
51
|
+
* <ui-action-sheet [(open)]="open">
|
|
52
|
+
* <ui-action-sheet-title>Share this document</ui-action-sheet-title>
|
|
53
|
+
* <ui-action-sheet-item (pressed)="copyLink()">Copy link</ui-action-sheet-item>
|
|
54
|
+
* <ui-action-sheet-item variant="destructive" (pressed)="remove()">Delete</ui-action-sheet-item>
|
|
55
|
+
* <ui-action-sheet-cancel />
|
|
56
|
+
* </ui-action-sheet>
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* By default it slides up from the bottom of the screen over a dimmed
|
|
60
|
+
* backdrop, with Cancel in a tile of its own below the actions, and takes
|
|
61
|
+
* the screen until it closes.
|
|
62
|
+
*
|
|
63
|
+
* Mark the control that presents it with `[uiActionSheetAnchor]="sheet"`
|
|
64
|
+
* to open it from that control instead, as iOS 26 does: there it's Liquid
|
|
65
|
+
* Glass that grows out of the control, and on Android a Material 3 menu
|
|
66
|
+
* that unfolds beside it. Nothing dims, Cancel gives way to a touch outside,
|
|
67
|
+
* and that touch still reaches what it lands on. Before iOS 26, iPhone
|
|
68
|
+
* action sheets always rose from the bottom, so it does there too.
|
|
69
|
+
*/
|
|
31
70
|
@Component({
|
|
32
71
|
selector: 'ui-action-sheet',
|
|
33
|
-
imports: [LYNX_ELEMENTS],
|
|
72
|
+
imports: [LYNX_ELEMENTS, NgTemplateOutlet, UiPopoverPanel],
|
|
34
73
|
encapsulation: ViewEncapsulation.None,
|
|
35
74
|
template: `
|
|
36
|
-
|
|
75
|
+
<!-- Android's back button asks the overlay to close (requestclose),
|
|
76
|
+
which closes the sheet as Cancel does. -->
|
|
77
|
+
<overlay
|
|
78
|
+
[attr.visible]="overlayVisible()"
|
|
79
|
+
[style]="overlayStyle()"
|
|
80
|
+
(bindrequestclose)="close()"
|
|
81
|
+
>
|
|
37
82
|
<!-- bg-black/50 dims the content behind the sheet. The fadeIn/fadeOut in
|
|
38
83
|
#animateIn/#animateOut already tween this element's opacity over
|
|
39
84
|
DURATION.normal, so the dim animates in lockstep with the panel slide —
|
|
@@ -62,7 +107,9 @@ import { cn } from '../../utils/cn';
|
|
|
62
107
|
class="flex-col rounded-lg border border-border bg-card flex overflow-hidden"
|
|
63
108
|
style="box-shadow: 0 2px 16px rgba(0, 0, 0, 0.15);"
|
|
64
109
|
>
|
|
65
|
-
|
|
110
|
+
@if (!anchored()) {
|
|
111
|
+
<ng-container [ngTemplateOutlet]="actions" />
|
|
112
|
+
}
|
|
66
113
|
</view>
|
|
67
114
|
<!-- Cancel button is slotted separately so it renders outside the
|
|
68
115
|
rounded card — matching the iOS action sheet visual convention
|
|
@@ -71,6 +118,28 @@ import { cn } from '../../utils/cn';
|
|
|
71
118
|
</view>
|
|
72
119
|
</view>
|
|
73
120
|
</overlay>
|
|
121
|
+
@if (anchorElement(); as anchor) {
|
|
122
|
+
<!-- Opened from its control: a popover's panel, which leaves the rest
|
|
123
|
+
of the screen as it was. A touch outside it is the cancel, as
|
|
124
|
+
UIKit drops an action sheet's Cancel when it shows it as a
|
|
125
|
+
popover, so the Cancel tile above stays hidden. -->
|
|
126
|
+
<ui-popover-panel
|
|
127
|
+
#anchoredPanel
|
|
128
|
+
[open]="open()"
|
|
129
|
+
[anchor]="anchor"
|
|
130
|
+
[width]="anchoredWidth()"
|
|
131
|
+
[glassId]="glassId"
|
|
132
|
+
(dismissed)="close()"
|
|
133
|
+
(closed)="closed.emit()"
|
|
134
|
+
>
|
|
135
|
+
<view [class]="anchoredClass()">
|
|
136
|
+
<ng-container [ngTemplateOutlet]="actions" />
|
|
137
|
+
</view>
|
|
138
|
+
</ui-popover-panel>
|
|
139
|
+
}
|
|
140
|
+
<!-- One copy of the title and items, for whichever form the sheet takes:
|
|
141
|
+
Angular projects content once, so it can't be in both. -->
|
|
142
|
+
<ng-template #actions><ng-content /></ng-template>
|
|
74
143
|
`,
|
|
75
144
|
})
|
|
76
145
|
export class UiActionSheet implements OnDestroy {
|
|
@@ -79,6 +148,57 @@ export class UiActionSheet implements OnDestroy {
|
|
|
79
148
|
|
|
80
149
|
readonly closed = output<void>();
|
|
81
150
|
|
|
151
|
+
readonly design = injectGlassDesign();
|
|
152
|
+
readonly #anchor = signal<PopoverAnchor | null>(null);
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The id the anchored sheet's glass and a glass control share (see
|
|
156
|
+
* UiGlass's `glassId`), unique to the sheet, so it takes over the
|
|
157
|
+
* control's glass as it grows out of it.
|
|
158
|
+
*/
|
|
159
|
+
readonly glassId = `ui-action-sheet-${++sheetCount}`;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The control the sheet opens from, on a design that opens it there: iOS
|
|
163
|
+
* 26's glass and Android's Material. iPhone action sheets rose from the
|
|
164
|
+
* bottom edge before iOS 26 whatever presented them, so `frosted` keeps
|
|
165
|
+
* the bottom sheet.
|
|
166
|
+
*/
|
|
167
|
+
protected readonly anchorElement = computed(() =>
|
|
168
|
+
this.design() === 'frosted' ? null : this.#anchor(),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
/** Whether the sheet opens from its control rather than from the bottom. */
|
|
172
|
+
readonly anchored = computed(() => this.anchorElement() !== null);
|
|
173
|
+
|
|
174
|
+
// Optional: only there while the sheet is anchored.
|
|
175
|
+
readonly anchoredPanel = viewChild<UiPopoverPanel>('anchoredPanel');
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Whether the sheet covers its control now: on iOS 26 it grows out of the
|
|
179
|
+
* control and shrinks back into it, which hides meanwhile.
|
|
180
|
+
*/
|
|
181
|
+
readonly coversAnchor = computed(
|
|
182
|
+
() => this.anchoredPanel()?.coversAnchor() ?? false,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* How the sheet's title and items draw: as today's bottom sheet, as
|
|
187
|
+
* iOS 26's glass popover, or as a Material 3 menu.
|
|
188
|
+
*/
|
|
189
|
+
readonly look = computed<'sheet' | 'glass' | 'material'>(() => {
|
|
190
|
+
if (!this.anchored()) return 'sheet';
|
|
191
|
+
return this.design() === 'material' ? 'material' : 'glass';
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
protected readonly anchoredWidth = computed(() =>
|
|
195
|
+
this.look() === 'material' ? ANCHORED_WIDTH.material : ANCHORED_WIDTH.glass,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
protected readonly anchoredClass = computed(() =>
|
|
199
|
+
cn('flex-col flex', this.look() === 'material' ? 'py-2' : 'py-1.5'),
|
|
200
|
+
);
|
|
201
|
+
|
|
82
202
|
protected readonly overlayVisible = signal(false);
|
|
83
203
|
/**
|
|
84
204
|
* `width`/`height`/`top`/`left` are web-only necessities: on web
|
|
@@ -98,7 +218,7 @@ export class UiActionSheet implements OnDestroy {
|
|
|
98
218
|
readonly panelRef = viewChild.required<ElementRef>('panel');
|
|
99
219
|
#backdropAnim?: AnimationHandle;
|
|
100
220
|
#panelAnim?: AnimationHandle;
|
|
101
|
-
#
|
|
221
|
+
#sheetOpen = false;
|
|
102
222
|
// The open and close sequences' pending timers, so ngOnDestroy can stop
|
|
103
223
|
// them.
|
|
104
224
|
readonly #timers = new Set<ReturnType<typeof setTimeout>>();
|
|
@@ -108,17 +228,20 @@ export class UiActionSheet implements OnDestroy {
|
|
|
108
228
|
* constructor: Angular's test build keeps a function for a constructor that
|
|
109
229
|
* nothing calls, which an app's coverage report would show as uncovered.
|
|
110
230
|
*
|
|
111
|
-
* `#
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
231
|
+
* `#sheetOpen` says the bottom sheet is open or opening, so only a sheet
|
|
232
|
+
* that opened closes. Without it, the first run, with `open` false, would
|
|
233
|
+
* call #doClose() and emit `closed` before the action sheet ever opened,
|
|
234
|
+
* and so would an anchor arriving or leaving while it's closed.
|
|
115
235
|
*/
|
|
116
236
|
protected readonly openOrClose = effect(() => {
|
|
117
|
-
|
|
237
|
+
// An anchored sheet opens in its popover panel instead, which follows
|
|
238
|
+
// `open` itself.
|
|
239
|
+
const isOpen = this.open() && !this.anchored();
|
|
118
240
|
if (isOpen) {
|
|
119
|
-
this.#
|
|
241
|
+
this.#sheetOpen = true;
|
|
120
242
|
this.#doOpen();
|
|
121
|
-
} else if (this.#
|
|
243
|
+
} else if (this.#sheetOpen) {
|
|
244
|
+
this.#sheetOpen = false;
|
|
122
245
|
this.#doClose();
|
|
123
246
|
}
|
|
124
247
|
});
|
|
@@ -139,6 +262,21 @@ export class UiActionSheet implements OnDestroy {
|
|
|
139
262
|
this.open.set(false);
|
|
140
263
|
}
|
|
141
264
|
|
|
265
|
+
/**
|
|
266
|
+
* @internal Called by the `uiActionSheetAnchor` directive.
|
|
267
|
+
*/
|
|
268
|
+
setAnchor(anchor: PopoverAnchor | null): void {
|
|
269
|
+
this.#anchor.set(anchor);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @internal A touch began on the anchor, which opens the sheet rather than
|
|
274
|
+
* landing outside it (see UiPopoverPanel.touchedAnchor).
|
|
275
|
+
*/
|
|
276
|
+
touchedAnchor(): void {
|
|
277
|
+
this.anchoredPanel()?.touchedAnchor();
|
|
278
|
+
}
|
|
279
|
+
|
|
142
280
|
/**
|
|
143
281
|
* No-op tap handler for the panel. `catchtap` (vs `bindtap`) already stops
|
|
144
282
|
* the tap from bubbling to the backdrop — Lynx controls propagation via the
|
|
@@ -229,28 +367,73 @@ export class UiActionSheet implements OnDestroy {
|
|
|
229
367
|
}
|
|
230
368
|
}
|
|
231
369
|
|
|
370
|
+
/** How an action sheet's title and items draw; see UiActionSheet.look. */
|
|
371
|
+
type ActionSheetLook = 'sheet' | 'glass' | 'material';
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* The look of the sheet a title or item is in. Today's bottom sheet when
|
|
375
|
+
* it's in none.
|
|
376
|
+
*/
|
|
377
|
+
const lookOf = (sheet: UiActionSheet | null) =>
|
|
378
|
+
computed<ActionSheetLook>(() => sheet?.look() ?? 'sheet');
|
|
379
|
+
|
|
380
|
+
// The bottom sheet's tiles; iOS 26's popover, whose rows are 44pt tall with
|
|
381
|
+
// the text in the middle, as UIKit's action sheets keep it in every form;
|
|
382
|
+
// and Material 3's menu, with 48dp rows and the text at the start.
|
|
383
|
+
const TITLE_CONTAINER: Readonly<Record<ActionSheetLook, string>> = {
|
|
384
|
+
sheet: 'items-center px-4 py-3 flex justify-center',
|
|
385
|
+
glass: 'items-center px-4 pb-1 pt-2 flex justify-center',
|
|
386
|
+
material: 'items-center px-3 pb-1 pt-2 flex',
|
|
387
|
+
};
|
|
388
|
+
const TITLE_TEXT: Readonly<Record<ActionSheetLook, string>> = {
|
|
389
|
+
sheet: 'text-sm text-muted-foreground',
|
|
390
|
+
glass: 'text-center text-[13px] text-glass-muted-foreground',
|
|
391
|
+
material: 'text-[13px] text-glass-muted-foreground',
|
|
392
|
+
};
|
|
393
|
+
const ITEM_CONTAINER: Readonly<Record<ActionSheetLook, string>> = {
|
|
394
|
+
sheet: 'items-center border-t border-border px-4 py-3 flex justify-center',
|
|
395
|
+
glass:
|
|
396
|
+
'min-h-[44px] items-center px-4 py-[11px] flex justify-center relative',
|
|
397
|
+
material: 'min-h-[48px] items-center px-3 py-3 flex relative',
|
|
398
|
+
};
|
|
399
|
+
const ITEM_TEXT: Readonly<Record<ActionSheetLook, string>> = {
|
|
400
|
+
sheet: 'text-base',
|
|
401
|
+
glass: 'text-center text-[17px]',
|
|
402
|
+
material: 'text-[16px]',
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* The action sheet's title, muted, above its items. A screen reader reads
|
|
407
|
+
* it as a heading.
|
|
408
|
+
*/
|
|
232
409
|
@Component({
|
|
233
410
|
selector: 'ui-action-sheet-title',
|
|
234
411
|
imports: [LYNX_ELEMENTS],
|
|
235
412
|
encapsulation: ViewEncapsulation.None,
|
|
236
413
|
template: `
|
|
237
414
|
<view [class]="containerClass()">
|
|
238
|
-
<text [class]="textClass()"
|
|
415
|
+
<text [class]="textClass()" accessibility-traits="header"
|
|
416
|
+
><ng-content
|
|
417
|
+
/></text>
|
|
239
418
|
</view>
|
|
240
419
|
`,
|
|
241
420
|
})
|
|
242
421
|
export class UiActionSheetTitle {
|
|
422
|
+
readonly #look = lookOf(inject(UiActionSheet, { optional: true }));
|
|
423
|
+
|
|
243
424
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
244
425
|
|
|
245
426
|
protected readonly containerClass = computed(() =>
|
|
246
|
-
cn(
|
|
427
|
+
cn(TITLE_CONTAINER[this.#look()], this.userClass()),
|
|
247
428
|
);
|
|
248
429
|
|
|
249
|
-
protected readonly textClass = computed(() =>
|
|
250
|
-
cn('text-sm text-muted-foreground'),
|
|
251
|
-
);
|
|
430
|
+
protected readonly textClass = computed(() => TITLE_TEXT[this.#look()]);
|
|
252
431
|
}
|
|
253
432
|
|
|
433
|
+
/**
|
|
434
|
+
* One of the action sheet's choices: a tap emits `pressed` and closes the
|
|
435
|
+
* sheet. A screen reader reads it as a button.
|
|
436
|
+
*/
|
|
254
437
|
@Component({
|
|
255
438
|
selector: 'ui-action-sheet-item',
|
|
256
439
|
imports: [LYNX_ELEMENTS],
|
|
@@ -267,12 +450,23 @@ export class UiActionSheetTitle {
|
|
|
267
450
|
(bindmouseleave)="onPressCancel()"
|
|
268
451
|
(bindtap)="onTap()"
|
|
269
452
|
>
|
|
270
|
-
|
|
453
|
+
@if (look() !== 'sheet') {
|
|
454
|
+
<!-- A menu row's pressed highlight (see UiMenuItem's highlightClass):
|
|
455
|
+
rows in a popover light up rather than shrink. -->
|
|
456
|
+
<view
|
|
457
|
+
[class]="highlightClass()"
|
|
458
|
+
[style.opacity]="held() ? highlightOpacity() : 0"
|
|
459
|
+
></view>
|
|
460
|
+
}
|
|
461
|
+
<text [class]="textClass()" accessibility-traits="button"
|
|
462
|
+
><ng-content
|
|
463
|
+
/></text>
|
|
271
464
|
</view>
|
|
272
465
|
`,
|
|
273
466
|
})
|
|
274
467
|
export class UiActionSheetItem {
|
|
275
468
|
readonly #sheet = inject(UiActionSheet);
|
|
469
|
+
protected readonly look = lookOf(this.#sheet);
|
|
276
470
|
|
|
277
471
|
readonly variant = input<'default' | 'destructive'>('default');
|
|
278
472
|
readonly userClass = input<string>('', { alias: 'class' });
|
|
@@ -283,21 +477,42 @@ export class UiActionSheetItem {
|
|
|
283
477
|
readonly containerRef = viewChild.required<ElementRef>('container');
|
|
284
478
|
#pressAnim?: AnimationHandle;
|
|
285
479
|
|
|
286
|
-
|
|
480
|
+
/** Whether a finger (or the mouse) is down on a row in a popover. */
|
|
481
|
+
protected readonly held = signal(false);
|
|
482
|
+
|
|
483
|
+
// As UiMenuItem's: iOS 26's inset capsule, and Material's full-width
|
|
484
|
+
// state layer.
|
|
485
|
+
protected readonly highlightClass = computed(() =>
|
|
287
486
|
cn(
|
|
288
|
-
'
|
|
289
|
-
this.
|
|
487
|
+
'bg-glass-foreground absolute bottom-0 top-0',
|
|
488
|
+
this.look() === 'glass'
|
|
489
|
+
? 'rounded-full left-[10px] right-[10px]'
|
|
490
|
+
: 'left-0 right-0',
|
|
290
491
|
),
|
|
291
492
|
);
|
|
493
|
+
protected readonly highlightOpacity = computed(() =>
|
|
494
|
+
this.look() === 'glass' ? 0.08 : 0.1,
|
|
495
|
+
);
|
|
292
496
|
|
|
293
|
-
protected readonly
|
|
294
|
-
cn(
|
|
295
|
-
'text-base',
|
|
296
|
-
this.variant() === 'destructive' ? 'text-destructive' : 'text-primary',
|
|
297
|
-
),
|
|
497
|
+
protected readonly containerClass = computed(() =>
|
|
498
|
+
cn(ITEM_CONTAINER[this.look()], this.userClass()),
|
|
298
499
|
);
|
|
299
500
|
|
|
501
|
+
protected readonly textClass = computed(() => {
|
|
502
|
+
const destructive = this.variant() === 'destructive';
|
|
503
|
+
return cn(
|
|
504
|
+
ITEM_TEXT[this.look()],
|
|
505
|
+
destructive && 'text-destructive',
|
|
506
|
+
!destructive &&
|
|
507
|
+
(this.look() === 'sheet' ? 'text-primary' : 'text-glass-foreground'),
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
|
|
300
511
|
protected onPressStart(): void {
|
|
512
|
+
if (this.look() !== 'sheet') {
|
|
513
|
+
this.held.set(true);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
301
516
|
this.#pressAnim?.cancel();
|
|
302
517
|
this.#pressAnim = pressDown(
|
|
303
518
|
this.containerRef().nativeElement,
|
|
@@ -306,11 +521,13 @@ export class UiActionSheetItem {
|
|
|
306
521
|
}
|
|
307
522
|
|
|
308
523
|
protected onPressEnd(): void {
|
|
524
|
+
this.held.set(false);
|
|
309
525
|
this.#pressAnim?.cancel();
|
|
310
526
|
this.#pressAnim = pressRelease(this.containerRef().nativeElement);
|
|
311
527
|
}
|
|
312
528
|
|
|
313
529
|
protected onPressCancel(): void {
|
|
530
|
+
this.held.set(false);
|
|
314
531
|
this.#pressAnim?.cancel();
|
|
315
532
|
this.#pressAnim = pressRelease(this.containerRef().nativeElement);
|
|
316
533
|
}
|
|
@@ -321,6 +538,11 @@ export class UiActionSheetItem {
|
|
|
321
538
|
}
|
|
322
539
|
}
|
|
323
540
|
|
|
541
|
+
/**
|
|
542
|
+
* The action sheet's Cancel, in a tile of its own below the actions: a tap
|
|
543
|
+
* closes the sheet. An action sheet opened from its control has none: a
|
|
544
|
+
* touch outside it cancels.
|
|
545
|
+
*/
|
|
324
546
|
@Component({
|
|
325
547
|
selector: 'ui-action-sheet-cancel',
|
|
326
548
|
imports: [LYNX_ELEMENTS],
|
|
@@ -338,7 +560,9 @@ export class UiActionSheetItem {
|
|
|
338
560
|
(bindmouseleave)="onPressCancel()"
|
|
339
561
|
(bindtap)="onTap()"
|
|
340
562
|
>
|
|
341
|
-
<text [class]="textClass()">{{
|
|
563
|
+
<text [class]="textClass()" accessibility-traits="button">{{
|
|
564
|
+
label()
|
|
565
|
+
}}</text>
|
|
342
566
|
</view>
|
|
343
567
|
`,
|
|
344
568
|
})
|
|
@@ -385,3 +609,57 @@ export class UiActionSheetCancel {
|
|
|
385
609
|
this.#sheet.close();
|
|
386
610
|
}
|
|
387
611
|
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Marks the control that presents an action sheet, which then opens from it
|
|
615
|
+
* instead of from the bottom of the screen: Liquid Glass that grows out of
|
|
616
|
+
* it on iOS 26, and a Material 3 menu beside it on Android.
|
|
617
|
+
*
|
|
618
|
+
* ```html
|
|
619
|
+
* <ui-button [uiActionSheetAnchor]="sheet" (pressed)="open.set(true)">
|
|
620
|
+
* Share
|
|
621
|
+
* </ui-button>
|
|
622
|
+
* <ui-action-sheet #sheet [(open)]="open">…</ui-action-sheet>
|
|
623
|
+
* ```
|
|
624
|
+
*
|
|
625
|
+
* It only says where the sheet opens from: open it as before, from the
|
|
626
|
+
* control's own tap handler. A touch on the control doesn't count as one
|
|
627
|
+
* outside the sheet, so it doesn't close it.
|
|
628
|
+
*/
|
|
629
|
+
@Directive({
|
|
630
|
+
selector: '[uiActionSheetAnchor]',
|
|
631
|
+
host: {
|
|
632
|
+
// Bubbling, from what the touch landed on in the control: Lynx 4.1 ran
|
|
633
|
+
// no capture listener on iOS (see UiPopoverPanel).
|
|
634
|
+
'(bindtouchstart)': 'sheet().touchedAnchor()',
|
|
635
|
+
'(bindmousedown)': 'sheet().touchedAnchor()',
|
|
636
|
+
'[style.visibility]': 'sheet().coversAnchor() ? "hidden" : null',
|
|
637
|
+
},
|
|
638
|
+
})
|
|
639
|
+
export class UiActionSheetAnchor {
|
|
640
|
+
/** The action sheet this control presents. */
|
|
641
|
+
readonly sheet = input.required<UiActionSheet>({
|
|
642
|
+
alias: 'uiActionSheetAnchor',
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
readonly #element =
|
|
646
|
+
inject<ElementRef<PopoverAnchor>>(ElementRef).nativeElement;
|
|
647
|
+
readonly #glass = inject(UiGlass, { self: true, optional: true });
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Points the sheet at this control, and away from it when the directive
|
|
651
|
+
* is destroyed or points at another sheet. A control that's glass itself
|
|
652
|
+
* (`<blur-view ui-glass [uiActionSheetAnchor]>`) shares the sheet's glass
|
|
653
|
+
* id meanwhile, so the sheet takes its glass over (see UiPopoverPanel's
|
|
654
|
+
* coversAnchor).
|
|
655
|
+
*/
|
|
656
|
+
protected readonly register = effect((onCleanup) => {
|
|
657
|
+
const sheet = this.sheet();
|
|
658
|
+
sheet.setAnchor(this.#element);
|
|
659
|
+
this.#glass?.pair(sheet.glassId);
|
|
660
|
+
onCleanup(() => {
|
|
661
|
+
sheet.setAnchor(null);
|
|
662
|
+
this.#glass?.pair(null);
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
}
|