@mystravil/hardpoint 0.1.0 → 0.2.1
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/CHANGELOG.md +42 -18
- package/LICENSE +25 -25
- package/README.md +67 -64
- package/dist/base.css +625 -346
- package/dist/components/AppShell.svelte +698 -698
- package/dist/components/Button.svelte +174 -174
- package/dist/components/Checkbox.svelte +235 -198
- package/dist/components/Checkbox.svelte.d.ts +9 -2
- package/dist/components/CheckboxGroup.svelte +207 -99
- package/dist/components/CheckboxGroup.svelte.d.ts +23 -4
- package/dist/components/Combobox.svelte +901 -824
- package/dist/components/Combobox.svelte.d.ts +10 -1
- package/dist/components/Dialog.svelte +359 -356
- package/dist/components/Disclosure.svelte +201 -201
- package/dist/components/EmptyState.svelte +88 -88
- package/dist/components/ErrorState.svelte +135 -135
- package/dist/components/ImageViewer.svelte +764 -0
- package/dist/components/ImageViewer.svelte.d.ts +55 -0
- package/dist/components/KeyValueList.svelte +114 -114
- package/dist/components/ListDetail.svelte +218 -0
- package/dist/components/ListDetail.svelte.d.ts +37 -0
- package/dist/components/LoadRegion.svelte +219 -219
- package/dist/components/MediaFrame.svelte +110 -110
- package/dist/components/MediaImage.svelte +256 -256
- package/dist/components/Menu.svelte +258 -258
- package/dist/components/MenuCheckboxItem.svelte +119 -119
- package/dist/components/MenuGroup.svelte +50 -50
- package/dist/components/MenuItem.svelte +94 -94
- package/dist/components/MenuRadioGroup.svelte +62 -62
- package/dist/components/MenuRadioItem.svelte +110 -110
- package/dist/components/MenuSeparator.svelte +28 -28
- package/dist/components/Meter.svelte +283 -0
- package/dist/components/Meter.svelte.d.ts +66 -0
- package/dist/components/NativeSelect.svelte +18 -2
- package/dist/components/NativeSelect.svelte.d.ts +6 -0
- package/dist/components/Notice.svelte +262 -0
- package/dist/components/Notice.svelte.d.ts +30 -0
- package/dist/components/Popover.svelte +222 -222
- package/dist/components/Progress.svelte +157 -157
- package/dist/components/RadioGroup.svelte +440 -0
- package/dist/components/RadioGroup.svelte.d.ts +50 -0
- package/dist/components/Select.svelte +471 -405
- package/dist/components/Select.svelte.d.ts +6 -0
- package/dist/components/Skeleton.svelte +149 -149
- package/dist/components/Spinner.svelte +118 -117
- package/dist/components/Stat.svelte +253 -253
- package/dist/components/Steps.svelte +189 -189
- package/dist/components/Switch.svelte +222 -222
- package/dist/components/Table.svelte +504 -504
- package/dist/components/Tabs.svelte +246 -246
- package/dist/components/Tag.svelte +40 -40
- package/dist/components/TextArea.svelte +272 -0
- package/dist/components/TextArea.svelte.d.ts +28 -0
- package/dist/components/TextField.svelte +256 -256
- package/dist/components/Timeline.svelte +156 -156
- package/dist/components/Toaster.svelte +411 -411
- package/dist/components/Tooltip.svelte +200 -200
- package/dist/components/TooltipGroup.svelte +46 -46
- package/dist/components/internal/Scrim.svelte +52 -52
- package/dist/components/internal/Sheet.svelte +263 -263
- package/dist/components/internal/groups.d.ts +14 -0
- package/dist/components/internal/groups.js +23 -0
- package/dist/fonts.css +71 -71
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/tokens.css +283 -279
- package/package.json +81 -76
|
@@ -1,200 +1,200 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
import { Tooltip as BitsTooltip } from 'bits-ui';
|
|
4
|
-
|
|
5
|
-
export type TooltipTriggerSnippetProps = {
|
|
6
|
-
/**
|
|
7
|
-
* Spread onto the control the tooltip belongs to. Its own handlers may
|
|
8
|
-
* come before or after. A control with its own description writes
|
|
9
|
-
* `aria-describedby={[props['aria-describedby'], ownId].filter(Boolean).join(' ')}`
|
|
10
|
-
* after the spread, so it keeps both.
|
|
11
|
-
*/
|
|
12
|
-
props: Record<string, unknown>;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Attributes and handlers not listed here go to the tooltip element (Bits
|
|
17
|
-
* UI `Tooltip.Content`), among them `side` (default `top`), `align`
|
|
18
|
-
* (default `center`), `sideOffset` (default 8) and `onEscapeKeydown`.
|
|
19
|
-
*/
|
|
20
|
-
export type TooltipProps = Omit<BitsTooltip.ContentProps, 'child' | 'children' | 'forceMount' | 'ref'> & {
|
|
21
|
-
open?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
* The tooltip, as written. It never opens on touch, so it is never the
|
|
24
|
-
* only path to its text: the control shows the same text, or carries it
|
|
25
|
-
* in `aria-label`.
|
|
26
|
-
*/
|
|
27
|
-
text: string;
|
|
28
|
-
/** The control the tooltip belongs to: spread `props` onto it. */
|
|
29
|
-
trigger: Snippet<[TooltipTriggerSnippetProps]>;
|
|
30
|
-
/**
|
|
31
|
-
* Milliseconds a pointer rests on the trigger before the tooltip opens
|
|
32
|
-
* (default 700, Bits UI's, or the TooltipGroup's). Keyboard focus opens
|
|
33
|
-
* it at once.
|
|
34
|
-
*/
|
|
35
|
-
delayDuration?: number;
|
|
36
|
-
/** The tooltip element. */
|
|
37
|
-
ref?: HTMLElement | null;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Bits UI keeps one tooltip open at a time per provider, and each Tooltip
|
|
41
|
-
// outside a TooltipGroup has its own provider, so the rule spans them here:
|
|
42
|
-
// the close function of the open tooltip, if any.
|
|
43
|
-
let closeOpen: (() => void) | null = null;
|
|
44
|
-
</script>
|
|
45
|
-
|
|
46
|
-
<script lang="ts">
|
|
47
|
-
import { createAttachmentKey } from 'svelte/attachments';
|
|
48
|
-
import { pop } from '../motion/index.js';
|
|
49
|
-
import { inTooltipGroup } from './TooltipGroup.svelte';
|
|
50
|
-
|
|
51
|
-
let {
|
|
52
|
-
open = $bindable(false),
|
|
53
|
-
text,
|
|
54
|
-
trigger,
|
|
55
|
-
delayDuration,
|
|
56
|
-
ref = $bindable(null),
|
|
57
|
-
class: className,
|
|
58
|
-
...rest
|
|
59
|
-
}: TooltipProps = $props();
|
|
60
|
-
|
|
61
|
-
const listen = createAttachmentKey();
|
|
62
|
-
const capture = createAttachmentKey();
|
|
63
|
-
|
|
64
|
-
// Inside a TooltipGroup the group's provider is shared, so the next
|
|
65
|
-
// tooltip in the row skips the delay; outside one, the tooltip has its own.
|
|
66
|
-
const grouped = inTooltipGroup();
|
|
67
|
-
|
|
68
|
-
// Opening this tooltip closes any other one, for example one a resting
|
|
69
|
-
// pointer opened when keyboard focus opens this.
|
|
70
|
-
const close = () => {
|
|
71
|
-
open = false;
|
|
72
|
-
};
|
|
73
|
-
$effect(() => {
|
|
74
|
-
if (!open) return;
|
|
75
|
-
if (closeOpen !== close) closeOpen?.();
|
|
76
|
-
closeOpen = close;
|
|
77
|
-
return () => {
|
|
78
|
-
if (closeOpen === close) closeOpen = null;
|
|
79
|
-
};
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// The control, kept to compare its name with the text.
|
|
83
|
-
let control = $state<Element | null>(null);
|
|
84
|
-
const captureControl = (node: Element) => {
|
|
85
|
-
control = node;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// The usual tooltip repeats the control's name: an icon button's
|
|
89
|
-
// aria-label, or the whole of a shortened name. Describing the control by
|
|
90
|
-
// the same text would make screen readers read it twice.
|
|
91
|
-
function repeatsName(node: Element | null): boolean {
|
|
92
|
-
if (!node) return false;
|
|
93
|
-
const name = node.getAttribute('aria-label') ?? node.textContent ?? '';
|
|
94
|
-
return name.trim() === text.trim();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// A tooltip's control has its own action, so the app passes its own
|
|
98
|
-
// onclick beside the spread. Bits UI's handlers move into an attachment,
|
|
99
|
-
// so the spread order cannot drop either set: the control's onclick still
|
|
100
|
-
// runs, and the tooltip still closes on a press and opens on hover and
|
|
101
|
-
// focus. Bits UI's `disabled: false` and `tabindex: 0` are left out, so
|
|
102
|
-
// they never undo a disabled control (a disabled link Button keeps
|
|
103
|
-
// tabindex -1); the control is focusable on its own.
|
|
104
|
-
function triggerProps(props: Record<string, unknown>): Record<string, unknown> {
|
|
105
|
-
const { disabled: _disabled, tabindex: _tabindex, 'aria-describedby': describedBy, ...attrs } = props;
|
|
106
|
-
const listeners: [string, EventListener][] = [];
|
|
107
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
108
|
-
if (key.startsWith('on') && typeof value === 'function') {
|
|
109
|
-
listeners.push([key.slice(2), value as EventListener]);
|
|
110
|
-
delete attrs[key];
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (describedBy && !repeatsName(control)) attrs['aria-describedby'] = describedBy;
|
|
114
|
-
return {
|
|
115
|
-
...attrs,
|
|
116
|
-
[capture]: captureControl,
|
|
117
|
-
[listen]: (node: Element) => {
|
|
118
|
-
for (const [type, fn] of listeners) node.addEventListener(type, fn);
|
|
119
|
-
return () => {
|
|
120
|
-
for (const [type, fn] of listeners) node.removeEventListener(type, fn);
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
</script>
|
|
126
|
-
|
|
127
|
-
<!--
|
|
128
|
-
Each tooltip outside a TooltipGroup has its own Bits UI provider, so an app
|
|
129
|
-
needs no shared wrapper. A resting mouse pointer opens it after the delay;
|
|
130
|
-
keyboard focus opens it at once, but only focus that shows the focus
|
|
131
|
-
contour, so focus returned to the trigger after a mouse press opens
|
|
132
|
-
nothing. Touch never opens it: a tap runs the control's own action.
|
|
133
|
-
Escape, moving the pointer away, blur and a press on the trigger close it.
|
|
134
|
-
While open, Bits UI points the trigger's aria-describedby at the tooltip,
|
|
135
|
-
unless the text repeats the control's name. The root carries the delay
|
|
136
|
-
and the focus rule, so they hold inside a group too.
|
|
137
|
-
-->
|
|
138
|
-
{#snippet tooltip()}
|
|
139
|
-
<BitsTooltip.Root bind:open {delayDuration} ignoreNonKeyboardFocus>
|
|
140
|
-
<BitsTooltip.Trigger>
|
|
141
|
-
{#snippet child({ props })}
|
|
142
|
-
{@render trigger({ props: triggerProps(props) })}
|
|
143
|
-
{/snippet}
|
|
144
|
-
</BitsTooltip.Trigger>
|
|
145
|
-
|
|
146
|
-
<BitsTooltip.Portal>
|
|
147
|
-
<BitsTooltip.Content forceMount bind:ref side="top" align="center" sideOffset={8} collisionPadding={8} {...rest}>
|
|
148
|
-
{#snippet child({ wrapperProps, props, open: shown })}
|
|
149
|
-
{#if shown}
|
|
150
|
-
<div {...wrapperProps}>
|
|
151
|
-
<div {...props} role="tooltip" class={['hp-tooltip', className]} transition:pop>{text}</div>
|
|
152
|
-
</div>
|
|
153
|
-
{/if}
|
|
154
|
-
{/snippet}
|
|
155
|
-
</BitsTooltip.Content>
|
|
156
|
-
</BitsTooltip.Portal>
|
|
157
|
-
</BitsTooltip.Root>
|
|
158
|
-
{/snippet}
|
|
159
|
-
|
|
160
|
-
{#if grouped}
|
|
161
|
-
{@render tooltip()}
|
|
162
|
-
{:else}
|
|
163
|
-
<BitsTooltip.Provider>
|
|
164
|
-
{@render tooltip()}
|
|
165
|
-
</BitsTooltip.Provider>
|
|
166
|
-
{/if}
|
|
167
|
-
|
|
168
|
-
<style>
|
|
169
|
-
/* On the tooltip layer (tokens.css), over popovers, menus and dialogs.
|
|
170
|
-
Bits UI copies the z-index to the positioned wrapper. A black box with
|
|
171
|
-
the control boundary, like the popover, as wide as its text up to
|
|
172
|
-
--tooltip-width (an app
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
position: relative;
|
|
178
|
-
z-index: var(--layer-tooltip);
|
|
179
|
-
box-sizing: border-box;
|
|
180
|
-
width: max-content;
|
|
181
|
-
max-width: min(var(--tooltip-width), var(--bits-tooltip-content-available-width));
|
|
182
|
-
padding: var(--space-1) var(--space-2);
|
|
183
|
-
border: 1px solid var(--border-control);
|
|
184
|
-
background: var(--canvas);
|
|
185
|
-
color: var(--text);
|
|
186
|
-
font-family: var(--sans);
|
|
187
|
-
font-size: var(--type-body);
|
|
188
|
-
line-height: 24px;
|
|
189
|
-
overflow-wrap: break-word;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/* 16px where a coarse pointer reads it (keyboard focus on a tablet), the
|
|
193
|
-
dense 14px on fine pointers. */
|
|
194
|
-
@media (pointer: fine) {
|
|
195
|
-
.hp-tooltip {
|
|
196
|
-
font-size: var(--type-dense);
|
|
197
|
-
line-height: 20px;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
</style>
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { Tooltip as BitsTooltip } from 'bits-ui';
|
|
4
|
+
|
|
5
|
+
export type TooltipTriggerSnippetProps = {
|
|
6
|
+
/**
|
|
7
|
+
* Spread onto the control the tooltip belongs to. Its own handlers may
|
|
8
|
+
* come before or after. A control with its own description writes
|
|
9
|
+
* `aria-describedby={[props['aria-describedby'], ownId].filter(Boolean).join(' ')}`
|
|
10
|
+
* after the spread, so it keeps both.
|
|
11
|
+
*/
|
|
12
|
+
props: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Attributes and handlers not listed here go to the tooltip element (Bits
|
|
17
|
+
* UI `Tooltip.Content`), among them `side` (default `top`), `align`
|
|
18
|
+
* (default `center`), `sideOffset` (default 8) and `onEscapeKeydown`.
|
|
19
|
+
*/
|
|
20
|
+
export type TooltipProps = Omit<BitsTooltip.ContentProps, 'child' | 'children' | 'forceMount' | 'ref'> & {
|
|
21
|
+
open?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* The tooltip, as written. It never opens on touch, so it is never the
|
|
24
|
+
* only path to its text: the control shows the same text, or carries it
|
|
25
|
+
* in `aria-label`.
|
|
26
|
+
*/
|
|
27
|
+
text: string;
|
|
28
|
+
/** The control the tooltip belongs to: spread `props` onto it. */
|
|
29
|
+
trigger: Snippet<[TooltipTriggerSnippetProps]>;
|
|
30
|
+
/**
|
|
31
|
+
* Milliseconds a pointer rests on the trigger before the tooltip opens
|
|
32
|
+
* (default 700, Bits UI's, or the TooltipGroup's). Keyboard focus opens
|
|
33
|
+
* it at once.
|
|
34
|
+
*/
|
|
35
|
+
delayDuration?: number;
|
|
36
|
+
/** The tooltip element. */
|
|
37
|
+
ref?: HTMLElement | null;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Bits UI keeps one tooltip open at a time per provider, and each Tooltip
|
|
41
|
+
// outside a TooltipGroup has its own provider, so the rule spans them here:
|
|
42
|
+
// the close function of the open tooltip, if any.
|
|
43
|
+
let closeOpen: (() => void) | null = null;
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<script lang="ts">
|
|
47
|
+
import { createAttachmentKey } from 'svelte/attachments';
|
|
48
|
+
import { pop } from '../motion/index.js';
|
|
49
|
+
import { inTooltipGroup } from './TooltipGroup.svelte';
|
|
50
|
+
|
|
51
|
+
let {
|
|
52
|
+
open = $bindable(false),
|
|
53
|
+
text,
|
|
54
|
+
trigger,
|
|
55
|
+
delayDuration,
|
|
56
|
+
ref = $bindable(null),
|
|
57
|
+
class: className,
|
|
58
|
+
...rest
|
|
59
|
+
}: TooltipProps = $props();
|
|
60
|
+
|
|
61
|
+
const listen = createAttachmentKey();
|
|
62
|
+
const capture = createAttachmentKey();
|
|
63
|
+
|
|
64
|
+
// Inside a TooltipGroup the group's provider is shared, so the next
|
|
65
|
+
// tooltip in the row skips the delay; outside one, the tooltip has its own.
|
|
66
|
+
const grouped = inTooltipGroup();
|
|
67
|
+
|
|
68
|
+
// Opening this tooltip closes any other one, for example one a resting
|
|
69
|
+
// pointer opened when keyboard focus opens this.
|
|
70
|
+
const close = () => {
|
|
71
|
+
open = false;
|
|
72
|
+
};
|
|
73
|
+
$effect(() => {
|
|
74
|
+
if (!open) return;
|
|
75
|
+
if (closeOpen !== close) closeOpen?.();
|
|
76
|
+
closeOpen = close;
|
|
77
|
+
return () => {
|
|
78
|
+
if (closeOpen === close) closeOpen = null;
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// The control, kept to compare its name with the text.
|
|
83
|
+
let control = $state<Element | null>(null);
|
|
84
|
+
const captureControl = (node: Element) => {
|
|
85
|
+
control = node;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// The usual tooltip repeats the control's name: an icon button's
|
|
89
|
+
// aria-label, or the whole of a shortened name. Describing the control by
|
|
90
|
+
// the same text would make screen readers read it twice.
|
|
91
|
+
function repeatsName(node: Element | null): boolean {
|
|
92
|
+
if (!node) return false;
|
|
93
|
+
const name = node.getAttribute('aria-label') ?? node.textContent ?? '';
|
|
94
|
+
return name.trim() === text.trim();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// A tooltip's control has its own action, so the app passes its own
|
|
98
|
+
// onclick beside the spread. Bits UI's handlers move into an attachment,
|
|
99
|
+
// so the spread order cannot drop either set: the control's onclick still
|
|
100
|
+
// runs, and the tooltip still closes on a press and opens on hover and
|
|
101
|
+
// focus. Bits UI's `disabled: false` and `tabindex: 0` are left out, so
|
|
102
|
+
// they never undo a disabled control (a disabled link Button keeps
|
|
103
|
+
// tabindex -1); the control is focusable on its own.
|
|
104
|
+
function triggerProps(props: Record<string, unknown>): Record<string, unknown> {
|
|
105
|
+
const { disabled: _disabled, tabindex: _tabindex, 'aria-describedby': describedBy, ...attrs } = props;
|
|
106
|
+
const listeners: [string, EventListener][] = [];
|
|
107
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
108
|
+
if (key.startsWith('on') && typeof value === 'function') {
|
|
109
|
+
listeners.push([key.slice(2), value as EventListener]);
|
|
110
|
+
delete attrs[key];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (describedBy && !repeatsName(control)) attrs['aria-describedby'] = describedBy;
|
|
114
|
+
return {
|
|
115
|
+
...attrs,
|
|
116
|
+
[capture]: captureControl,
|
|
117
|
+
[listen]: (node: Element) => {
|
|
118
|
+
for (const [type, fn] of listeners) node.addEventListener(type, fn);
|
|
119
|
+
return () => {
|
|
120
|
+
for (const [type, fn] of listeners) node.removeEventListener(type, fn);
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<!--
|
|
128
|
+
Each tooltip outside a TooltipGroup has its own Bits UI provider, so an app
|
|
129
|
+
needs no shared wrapper. A resting mouse pointer opens it after the delay;
|
|
130
|
+
keyboard focus opens it at once, but only focus that shows the focus
|
|
131
|
+
contour, so focus returned to the trigger after a mouse press opens
|
|
132
|
+
nothing. Touch never opens it: a tap runs the control's own action.
|
|
133
|
+
Escape, moving the pointer away, blur and a press on the trigger close it.
|
|
134
|
+
While open, Bits UI points the trigger's aria-describedby at the tooltip,
|
|
135
|
+
unless the text repeats the control's name. The root carries the delay
|
|
136
|
+
and the focus rule, so they hold inside a group too.
|
|
137
|
+
-->
|
|
138
|
+
{#snippet tooltip()}
|
|
139
|
+
<BitsTooltip.Root bind:open {delayDuration} ignoreNonKeyboardFocus>
|
|
140
|
+
<BitsTooltip.Trigger>
|
|
141
|
+
{#snippet child({ props })}
|
|
142
|
+
{@render trigger({ props: triggerProps(props) })}
|
|
143
|
+
{/snippet}
|
|
144
|
+
</BitsTooltip.Trigger>
|
|
145
|
+
|
|
146
|
+
<BitsTooltip.Portal>
|
|
147
|
+
<BitsTooltip.Content forceMount bind:ref side="top" align="center" sideOffset={8} collisionPadding={8} {...rest}>
|
|
148
|
+
{#snippet child({ wrapperProps, props, open: shown })}
|
|
149
|
+
{#if shown}
|
|
150
|
+
<div {...wrapperProps}>
|
|
151
|
+
<div {...props} role="tooltip" class={['hp-tooltip', className]} transition:pop>{text}</div>
|
|
152
|
+
</div>
|
|
153
|
+
{/if}
|
|
154
|
+
{/snippet}
|
|
155
|
+
</BitsTooltip.Content>
|
|
156
|
+
</BitsTooltip.Portal>
|
|
157
|
+
</BitsTooltip.Root>
|
|
158
|
+
{/snippet}
|
|
159
|
+
|
|
160
|
+
{#if grouped}
|
|
161
|
+
{@render tooltip()}
|
|
162
|
+
{:else}
|
|
163
|
+
<BitsTooltip.Provider>
|
|
164
|
+
{@render tooltip()}
|
|
165
|
+
</BitsTooltip.Provider>
|
|
166
|
+
{/if}
|
|
167
|
+
|
|
168
|
+
<style>
|
|
169
|
+
/* On the tooltip layer (tokens.css), over popovers, menus and dialogs.
|
|
170
|
+
Bits UI copies the z-index to the positioned wrapper. A black box with
|
|
171
|
+
the control boundary, like the popover, as wide as its text up to
|
|
172
|
+
--tooltip-width, 280px unless set (an app sets it with a class on the
|
|
173
|
+
Tooltip and a rule in its stylesheet; the default is the var()
|
|
174
|
+
fallback, so any rule wins whatever its specificity) and never wider
|
|
175
|
+
than the space beside the trigger. */
|
|
176
|
+
.hp-tooltip {
|
|
177
|
+
position: relative;
|
|
178
|
+
z-index: var(--layer-tooltip);
|
|
179
|
+
box-sizing: border-box;
|
|
180
|
+
width: max-content;
|
|
181
|
+
max-width: min(var(--tooltip-width, 280px), var(--bits-tooltip-content-available-width));
|
|
182
|
+
padding: var(--space-1) var(--space-2);
|
|
183
|
+
border: 1px solid var(--border-control);
|
|
184
|
+
background: var(--canvas);
|
|
185
|
+
color: var(--text);
|
|
186
|
+
font-family: var(--sans);
|
|
187
|
+
font-size: var(--type-body);
|
|
188
|
+
line-height: 24px;
|
|
189
|
+
overflow-wrap: break-word;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* 16px where a coarse pointer reads it (keyboard focus on a tablet), the
|
|
193
|
+
dense 14px on fine pointers. */
|
|
194
|
+
@media (pointer: fine) {
|
|
195
|
+
.hp-tooltip {
|
|
196
|
+
font-size: var(--type-dense);
|
|
197
|
+
line-height: 20px;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
</style>
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import { createContext, type Snippet } from 'svelte';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Wraps a row of controls whose tooltips belong together, such as a
|
|
6
|
-
* toolbar. The group renders no element of its own: it sets up one Bits UI
|
|
7
|
-
* `Tooltip.Provider` that every `Tooltip` inside it shares, so once one
|
|
8
|
-
* tooltip in the row has opened, the next opens at once while the pointer
|
|
9
|
-
* moves along the row. Lay out the controls with your own markup inside it.
|
|
10
|
-
*/
|
|
11
|
-
export type TooltipGroupProps = {
|
|
12
|
-
/** The controls and their tooltips. */
|
|
13
|
-
children: Snippet;
|
|
14
|
-
/**
|
|
15
|
-
* Milliseconds a pointer rests on a control before the first tooltip
|
|
16
|
-
* opens (default 700, Bits UI's, as for a Tooltip outside a group). A
|
|
17
|
-
* Tooltip's own `delayDuration` wins over it.
|
|
18
|
-
*/
|
|
19
|
-
delayDuration?: number;
|
|
20
|
-
/**
|
|
21
|
-
* Milliseconds after a tooltip in the group closes during which the next
|
|
22
|
-
* one opens without the delay (default 300, Bits UI's). 0 makes every
|
|
23
|
-
* tooltip wait the full delay.
|
|
24
|
-
*/
|
|
25
|
-
skipDelayDuration?: number;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const [, setGroup, hasGroup] = createContext<true>();
|
|
29
|
-
|
|
30
|
-
/** A Tooltip inside a group uses the group's provider instead of its own. */
|
|
31
|
-
export function inTooltipGroup(): boolean {
|
|
32
|
-
return hasGroup();
|
|
33
|
-
}
|
|
34
|
-
</script>
|
|
35
|
-
|
|
36
|
-
<script lang="ts">
|
|
37
|
-
import { Tooltip } from 'bits-ui';
|
|
38
|
-
|
|
39
|
-
let { children, delayDuration, skipDelayDuration }: TooltipGroupProps = $props();
|
|
40
|
-
|
|
41
|
-
setGroup(true);
|
|
42
|
-
</script>
|
|
43
|
-
|
|
44
|
-
<Tooltip.Provider {delayDuration} {skipDelayDuration}>
|
|
45
|
-
{@render children()}
|
|
46
|
-
</Tooltip.Provider>
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { createContext, type Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wraps a row of controls whose tooltips belong together, such as a
|
|
6
|
+
* toolbar. The group renders no element of its own: it sets up one Bits UI
|
|
7
|
+
* `Tooltip.Provider` that every `Tooltip` inside it shares, so once one
|
|
8
|
+
* tooltip in the row has opened, the next opens at once while the pointer
|
|
9
|
+
* moves along the row. Lay out the controls with your own markup inside it.
|
|
10
|
+
*/
|
|
11
|
+
export type TooltipGroupProps = {
|
|
12
|
+
/** The controls and their tooltips. */
|
|
13
|
+
children: Snippet;
|
|
14
|
+
/**
|
|
15
|
+
* Milliseconds a pointer rests on a control before the first tooltip
|
|
16
|
+
* opens (default 700, Bits UI's, as for a Tooltip outside a group). A
|
|
17
|
+
* Tooltip's own `delayDuration` wins over it.
|
|
18
|
+
*/
|
|
19
|
+
delayDuration?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Milliseconds after a tooltip in the group closes during which the next
|
|
22
|
+
* one opens without the delay (default 300, Bits UI's). 0 makes every
|
|
23
|
+
* tooltip wait the full delay.
|
|
24
|
+
*/
|
|
25
|
+
skipDelayDuration?: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const [, setGroup, hasGroup] = createContext<true>();
|
|
29
|
+
|
|
30
|
+
/** A Tooltip inside a group uses the group's provider instead of its own. */
|
|
31
|
+
export function inTooltipGroup(): boolean {
|
|
32
|
+
return hasGroup();
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<script lang="ts">
|
|
37
|
+
import { Tooltip } from 'bits-ui';
|
|
38
|
+
|
|
39
|
+
let { children, delayDuration, skipDelayDuration }: TooltipGroupProps = $props();
|
|
40
|
+
|
|
41
|
+
setGroup(true);
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<Tooltip.Provider {delayDuration} {skipDelayDuration}>
|
|
45
|
+
{@render children()}
|
|
46
|
+
</Tooltip.Provider>
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
/**
|
|
3
|
-
* The z-index token an overlay sits on (tokens.css, layer scale).
|
|
4
|
-
* `dialog`: dialogs, their sheets and the shell drawer. `popover`:
|
|
5
|
-
* popovers, menus, select lists and comboboxes, and their sheets on
|
|
6
|
-
* compact widths, so they open over a dialog.
|
|
7
|
-
*/
|
|
8
|
-
export type OverlayLayer = 'dialog' | 'popover';
|
|
9
|
-
|
|
10
|
-
export type ScrimProps = {
|
|
11
|
-
/** Shown while true. Pass the Bits UI content's `open`. */
|
|
12
|
-
open: boolean;
|
|
13
|
-
layer: OverlayLayer;
|
|
14
|
-
};
|
|
15
|
-
</script>
|
|
16
|
-
|
|
17
|
-
<script lang="ts">
|
|
18
|
-
import { fade } from '../../motion/index.js';
|
|
19
|
-
|
|
20
|
-
let { open, layer }: ScrimProps = $props();
|
|
21
|
-
|
|
22
|
-
// A press on the scrim keeps focus where it is (on the trigger, or
|
|
23
|
-
// inside a dialog). The press still reaches Bits UI as an outside
|
|
24
|
-
// interaction, which closes the overlay.
|
|
25
|
-
function keepFocus(e: PointerEvent) {
|
|
26
|
-
e.preventDefault();
|
|
27
|
-
}
|
|
28
|
-
</script>
|
|
29
|
-
|
|
30
|
-
<!--
|
|
31
|
-
Dims the page behind a dialog or sheet. Render it just before the panel,
|
|
32
|
-
on the same layer, so the panel paints over it. It fades with the panel's
|
|
33
|
-
timing (--dur-sheet in, --dur-sheet-exit out).
|
|
34
|
-
-->
|
|
35
|
-
{#if open}
|
|
36
|
-
<div class="hp-scrim" data-layer={layer} aria-hidden="true" onpointerdown={keepFocus} transition:fade></div>
|
|
37
|
-
{/if}
|
|
38
|
-
|
|
39
|
-
<style>
|
|
40
|
-
/* While a floating list is open Bits UI sets pointer-events: none on
|
|
41
|
-
<body>; the scrim takes presses again, so a tap on it closes. */
|
|
42
|
-
.hp-scrim {
|
|
43
|
-
position: fixed;
|
|
44
|
-
inset: 0;
|
|
45
|
-
z-index: var(--layer-dialog);
|
|
46
|
-
background: var(--scrim);
|
|
47
|
-
pointer-events: auto;
|
|
48
|
-
}
|
|
49
|
-
.hp-scrim[data-layer='popover'] {
|
|
50
|
-
z-index: var(--layer-popover);
|
|
51
|
-
}
|
|
52
|
-
</style>
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/**
|
|
3
|
+
* The z-index token an overlay sits on (tokens.css, layer scale).
|
|
4
|
+
* `dialog`: dialogs, their sheets and the shell drawer. `popover`:
|
|
5
|
+
* popovers, menus, select lists and comboboxes, and their sheets on
|
|
6
|
+
* compact widths, so they open over a dialog.
|
|
7
|
+
*/
|
|
8
|
+
export type OverlayLayer = 'dialog' | 'popover';
|
|
9
|
+
|
|
10
|
+
export type ScrimProps = {
|
|
11
|
+
/** Shown while true. Pass the Bits UI content's `open`. */
|
|
12
|
+
open: boolean;
|
|
13
|
+
layer: OverlayLayer;
|
|
14
|
+
};
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
import { fade } from '../../motion/index.js';
|
|
19
|
+
|
|
20
|
+
let { open, layer }: ScrimProps = $props();
|
|
21
|
+
|
|
22
|
+
// A press on the scrim keeps focus where it is (on the trigger, or
|
|
23
|
+
// inside a dialog). The press still reaches Bits UI as an outside
|
|
24
|
+
// interaction, which closes the overlay.
|
|
25
|
+
function keepFocus(e: PointerEvent) {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<!--
|
|
31
|
+
Dims the page behind a dialog or sheet. Render it just before the panel,
|
|
32
|
+
on the same layer, so the panel paints over it. It fades with the panel's
|
|
33
|
+
timing (--dur-sheet in, --dur-sheet-exit out).
|
|
34
|
+
-->
|
|
35
|
+
{#if open}
|
|
36
|
+
<div class="hp-scrim" data-layer={layer} aria-hidden="true" onpointerdown={keepFocus} transition:fade></div>
|
|
37
|
+
{/if}
|
|
38
|
+
|
|
39
|
+
<style>
|
|
40
|
+
/* While a floating list is open Bits UI sets pointer-events: none on
|
|
41
|
+
<body>; the scrim takes presses again, so a tap on it closes. */
|
|
42
|
+
.hp-scrim {
|
|
43
|
+
position: fixed;
|
|
44
|
+
inset: 0;
|
|
45
|
+
z-index: var(--layer-dialog);
|
|
46
|
+
background: var(--scrim);
|
|
47
|
+
pointer-events: auto;
|
|
48
|
+
}
|
|
49
|
+
.hp-scrim[data-layer='popover'] {
|
|
50
|
+
z-index: var(--layer-popover);
|
|
51
|
+
}
|
|
52
|
+
</style>
|