@heyramzi/ui 3.7.1 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +1 -0
- package/dist/charts/motion.js +2 -7
- package/dist/components/file-drop-zone/files.js +111 -0
- package/dist/components/file-drop-zone/index.js +2 -0
- package/package.json +1 -1
- package/scripts/svelte-dts.mjs +2 -0
- package/scripts/sync-shadcn.mjs +0 -1
- package/src/charts/motion.ts +2 -7
- package/src/components/file-drop-zone/file-drop-zone.svelte +348 -0
- package/src/components/file-drop-zone/file-drop-zone.svelte.d.ts +43 -0
- package/src/components/file-drop-zone/file-drop-zone.test.ts +148 -0
- package/src/components/file-drop-zone/files.test.ts +105 -0
- package/src/components/file-drop-zone/files.ts +144 -0
- package/src/components/file-drop-zone/index.ts +14 -0
- package/src/components/table-view/table-view.svelte +4 -1
- package/src/components/table-view/table-view.svelte.d.ts +2 -0
- package/src/shadcn/alert-dialog/alert-dialog-cancel.svelte +3 -0
- package/src/shadcn/calendar/calendar-month-select.svelte +2 -2
- package/src/shadcn/calendar/calendar-year-select.svelte +2 -2
- package/src/shadcn/calendar/calendar.svelte +40 -30
- package/src/shadcn/calendar/calendar.svelte.d.ts +0 -1
- package/src/shadcn/command/command-dialog.svelte +4 -0
- package/src/shadcn/command/command-dialog.svelte.d.ts +2 -0
- package/src/shadcn/dialog/dialog-content.svelte +4 -1
- package/src/shadcn/dialog/dialog-content.svelte.d.ts +2 -0
- package/src/shadcn/dialog/dialog-footer.svelte +4 -1
- package/src/shadcn/dialog/dialog-footer.svelte.d.ts +2 -0
- package/src/shadcn/input/input.svelte +1 -1
- package/src/shadcn/input/input.svelte.d.ts +1 -1
- package/src/shadcn/input/secret-input.svelte +17 -3
- package/src/shadcn/input/secret-input.svelte.d.ts +6 -0
- package/src/shadcn/input-group/input-group-input.svelte.d.ts +1 -1
- package/src/shadcn/input-otp/input-otp-group.svelte +1 -0
- package/src/shadcn/input-otp/input-otp-slot.svelte +5 -1
- package/src/shadcn/input-otp/input-otp.svelte +2 -0
- package/src/shadcn/input-otp/input-otp.svelte.d.ts +1 -1
- package/src/shadcn/message-scroller/message-scroller-button.svelte +9 -3
- package/src/shadcn/message-scroller/message-scroller-button.svelte.d.ts +5 -0
- package/src/shadcn/pagination/pagination-next.svelte +6 -2
- package/src/shadcn/pagination/pagination-next.svelte.d.ts +5 -1
- package/src/shadcn/pagination/pagination-previous.svelte +6 -2
- package/src/shadcn/pagination/pagination-previous.svelte.d.ts +5 -1
- package/src/shadcn/sheet/close-label.test.svelte +20 -0
- package/src/shadcn/sheet/close-label.test.svelte.d.ts +6 -0
- package/src/shadcn/sheet/close-label.test.ts +44 -0
- package/src/shadcn/sheet/sheet-content.svelte +4 -1
- package/src/shadcn/sheet/sheet-content.svelte.d.ts +2 -0
- package/src/shadcn/sidebar/sidebar-input.svelte.d.ts +1 -1
- package/src/shadcn/sidebar/sidebar-rail.svelte +7 -3
- package/src/shadcn/sidebar/sidebar-rail.svelte.d.ts +5 -1
- package/src/shadcn/sidebar/sidebar-trigger.svelte +4 -1
- package/src/shadcn/sidebar/sidebar-trigger.svelte.d.ts +2 -0
- package/src/thread/index.ts +1 -0
- package/src/thread/thread-events.svelte +32 -3
- package/src/thread/types.ts +10 -0
- package/src/work/task-gantt.svelte +4 -1
- package/src/work/task-gantt.svelte.d.ts +2 -0
- package/scripts/lib/svelte-modules.d.mts +0 -4
|
@@ -14,12 +14,15 @@
|
|
|
14
14
|
portalProps,
|
|
15
15
|
children,
|
|
16
16
|
showCloseButton = true,
|
|
17
|
+
closeLabel = "Close",
|
|
17
18
|
overlayClass,
|
|
18
19
|
...restProps
|
|
19
20
|
}: WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
|
|
20
21
|
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof DialogPortal>>;
|
|
21
22
|
children: Snippet;
|
|
22
23
|
showCloseButton?: boolean;
|
|
24
|
+
/** What a screen reader calls the built-in close button, in the app's language. */
|
|
25
|
+
closeLabel?: string;
|
|
23
26
|
/** Classes for the backdrop this dialog mounts, which is its sibling in the
|
|
24
27
|
* portal and so cannot be reached from the content's own class. */
|
|
25
28
|
overlayClass?: string;
|
|
@@ -62,7 +65,7 @@
|
|
|
62
65
|
{...props}
|
|
63
66
|
>
|
|
64
67
|
<X aria-hidden="true" size={16} />
|
|
65
|
-
<span class="sr-only">
|
|
68
|
+
<span class="sr-only">{closeLabel}</span>
|
|
66
69
|
</Button>
|
|
67
70
|
{/snippet}
|
|
68
71
|
</DialogPrimitive.Close>
|
|
@@ -7,6 +7,8 @@ type $$ComponentProps = WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
|
|
|
7
7
|
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof DialogPortal>>;
|
|
8
8
|
children: Snippet;
|
|
9
9
|
showCloseButton?: boolean;
|
|
10
|
+
/** What a screen reader calls the built-in close button, in the app's language. */
|
|
11
|
+
closeLabel?: string;
|
|
10
12
|
/** Classes for the backdrop this dialog mounts, which is its sibling in the
|
|
11
13
|
* portal and so cannot be reached from the content's own class. */
|
|
12
14
|
overlayClass?: string;
|
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
class: className,
|
|
10
10
|
children,
|
|
11
11
|
showCloseButton = false,
|
|
12
|
+
closeLabel = "Close",
|
|
12
13
|
band = false,
|
|
13
14
|
...restProps
|
|
14
15
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
15
16
|
showCloseButton?: boolean;
|
|
17
|
+
/** The close button's text, in the app's language. */
|
|
18
|
+
closeLabel?: string;
|
|
16
19
|
/**
|
|
17
20
|
* Draw the footer as a band: a hairline on top and a quiet surface out to both edges
|
|
18
21
|
* and the bottom corners. It cancels the content's `--dialog-pad` with a negative
|
|
@@ -43,7 +46,7 @@
|
|
|
43
46
|
{#if showCloseButton}
|
|
44
47
|
<DialogPrimitive.Close>
|
|
45
48
|
{#snippet child({ props })}
|
|
46
|
-
<Button variant="outline" {...props}>
|
|
49
|
+
<Button variant="outline" {...props}>{closeLabel}</Button>
|
|
47
50
|
{/snippet}
|
|
48
51
|
</DialogPrimitive.Close>
|
|
49
52
|
{/if}
|
|
@@ -2,6 +2,8 @@ import { type WithElementRef } from "../../lib/utils.js";
|
|
|
2
2
|
import type { HTMLAttributes } from "svelte/elements";
|
|
3
3
|
type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
4
4
|
showCloseButton?: boolean;
|
|
5
|
+
/** The close button's text, in the app's language. */
|
|
6
|
+
closeLabel?: string;
|
|
5
7
|
/**
|
|
6
8
|
* Draw the footer as a band: a hairline on top and a quiet surface out to both edges
|
|
7
9
|
* and the bottom corners. It cancels the content's `--dialog-pad` with a negative
|
|
@@ -3,7 +3,7 @@ import { type WithElementRef } from "../../lib/utils.js";
|
|
|
3
3
|
type InputType = Exclude<HTMLInputTypeAttribute, "file">;
|
|
4
4
|
type Props = WithElementRef<Omit<HTMLInputAttributes, "type"> & ({
|
|
5
5
|
type: "file";
|
|
6
|
-
files?: FileList;
|
|
6
|
+
files?: FileList | null;
|
|
7
7
|
} | {
|
|
8
8
|
type?: InputType;
|
|
9
9
|
files?: undefined;
|
|
@@ -14,12 +14,26 @@
|
|
|
14
14
|
variant?: "lock" | "obfuscate";
|
|
15
15
|
value?: string;
|
|
16
16
|
class?: string;
|
|
17
|
+
/** The reveal button's name while the value is hidden, in the app's language. */
|
|
18
|
+
revealLabel?: string;
|
|
19
|
+
/** The same button's name while the value shows. */
|
|
20
|
+
hideLabel?: string;
|
|
21
|
+
/** The name of the `obfuscate` box. */
|
|
22
|
+
maskedLabel?: string;
|
|
17
23
|
};
|
|
18
24
|
|
|
19
25
|
// =========================
|
|
20
26
|
// PROPS
|
|
21
27
|
// =========================
|
|
22
|
-
let {
|
|
28
|
+
let {
|
|
29
|
+
variant = "lock",
|
|
30
|
+
value = $bindable(""),
|
|
31
|
+
class: className,
|
|
32
|
+
revealLabel = "Reveal value",
|
|
33
|
+
hideLabel = "Hide value",
|
|
34
|
+
maskedLabel = "Encrypted value",
|
|
35
|
+
...rest
|
|
36
|
+
}: Props = $props();
|
|
23
37
|
|
|
24
38
|
// =========================
|
|
25
39
|
// STATE
|
|
@@ -46,7 +60,7 @@
|
|
|
46
60
|
"border-input bg-muted/30 text-muted-foreground flex h-8 w-full items-center gap-2 rounded-lg border px-2.5 py-1 font-mono text-sm",
|
|
47
61
|
className,
|
|
48
62
|
)}
|
|
49
|
-
aria-label=
|
|
63
|
+
aria-label={maskedLabel}
|
|
50
64
|
>
|
|
51
65
|
<LockKey aria-hidden="true" class="text-muted-foreground/70 size-3.5 shrink-0" />
|
|
52
66
|
<span class="truncate">{masked || "••••••••"}</span>
|
|
@@ -61,7 +75,7 @@
|
|
|
61
75
|
/>
|
|
62
76
|
<button
|
|
63
77
|
type="button"
|
|
64
|
-
aria-label={revealed ?
|
|
78
|
+
aria-label={revealed ? hideLabel : revealLabel}
|
|
65
79
|
class="text-muted-foreground hover:text-foreground absolute inset-y-0 right-0 flex w-8 items-center justify-center rounded-r-lg transition-colors"
|
|
66
80
|
onclick={() => (revealed = !revealed)}
|
|
67
81
|
>
|
|
@@ -3,6 +3,12 @@ type Props = Omit<HTMLInputAttributes, "type" | "value" | "class" | "files"> & {
|
|
|
3
3
|
variant?: "lock" | "obfuscate";
|
|
4
4
|
value?: string;
|
|
5
5
|
class?: string;
|
|
6
|
+
/** The reveal button's name while the value is hidden, in the app's language. */
|
|
7
|
+
revealLabel?: string;
|
|
8
|
+
/** The same button's name while the value shows. */
|
|
9
|
+
hideLabel?: string;
|
|
10
|
+
/** The name of the `obfuscate` box. */
|
|
11
|
+
maskedLabel?: string;
|
|
6
12
|
};
|
|
7
13
|
declare const $$Component: import("svelte").Component<Props, {}, "value">;
|
|
8
14
|
type $$Component = ReturnType<typeof $$Component>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
declare const $$Component: import("svelte").Component<(Omit<import("svelte/elements").HTMLInputAttributes, "type"> & ({
|
|
2
2
|
type: "file";
|
|
3
|
-
files?: FileList;
|
|
3
|
+
files?: FileList | null;
|
|
4
4
|
} | {
|
|
5
5
|
type?: "number" | "hidden" | "color" | "text" | "search" | "submit" | "reset" | "button" | "checkbox" | "radio" | (string & {}) | "tel" | "url" | "email" | "date" | "image" | "time" | "month" | "datetime-local" | "password" | "range" | "week";
|
|
6
6
|
files?: undefined;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
let {
|
|
6
6
|
ref = $bindable(null),
|
|
7
|
+
inputRef = $bindable(null),
|
|
7
8
|
class: className,
|
|
8
9
|
value = $bindable(""),
|
|
9
10
|
...restProps
|
|
@@ -12,6 +13,7 @@
|
|
|
12
13
|
|
|
13
14
|
<InputOTPPrimitive.Root
|
|
14
15
|
bind:ref
|
|
16
|
+
bind:inputRef
|
|
15
17
|
bind:value
|
|
16
18
|
data-slot="input-otp"
|
|
17
19
|
class={cn(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { PinInput as InputOTPPrimitive } from "bits-ui";
|
|
2
|
-
declare const $$Component: import("svelte").Component<InputOTPPrimitive.RootProps, {}, "value" | "ref">;
|
|
2
|
+
declare const $$Component: import("svelte").Component<InputOTPPrimitive.RootProps, {}, "value" | "ref" | "inputRef">;
|
|
3
3
|
type $$Component = ReturnType<typeof $$Component>;
|
|
4
4
|
export default $$Component;
|
|
@@ -14,12 +14,20 @@
|
|
|
14
14
|
variant = "outline",
|
|
15
15
|
size = "sm",
|
|
16
16
|
onclick,
|
|
17
|
+
label,
|
|
17
18
|
...restProps
|
|
18
19
|
}: MessageScrollerPrimitive.ButtonProps & {
|
|
19
20
|
variant?: ButtonVariant;
|
|
20
21
|
size?: ButtonSize;
|
|
22
|
+
/**
|
|
23
|
+
* What a screen reader calls the icon-only button, in the app's language.
|
|
24
|
+
* Defaults to "Scroll to end", or "Scroll to start" when `direction="start"`.
|
|
25
|
+
*/
|
|
26
|
+
label?: string;
|
|
21
27
|
} = $props();
|
|
22
28
|
|
|
29
|
+
const srLabel = $derived(label ?? (direction === "end" ? "Scroll to end" : "Scroll to start"));
|
|
30
|
+
|
|
23
31
|
// The primitive blurs the button and it turns inert at the end, dropping focus to the page. Focus goes to the thread instead.
|
|
24
32
|
function handleClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
|
25
33
|
onclick?.(event);
|
|
@@ -54,9 +62,7 @@
|
|
|
54
62
|
{@render children()}
|
|
55
63
|
{:else}
|
|
56
64
|
<ArrowDown aria-hidden="true" />
|
|
57
|
-
<span class="sr-only">
|
|
58
|
-
{direction === "end" ? "Scroll to end" : "Scroll to start"}
|
|
59
|
-
</span>
|
|
65
|
+
<span class="sr-only">{srLabel}</span>
|
|
60
66
|
{/if}
|
|
61
67
|
</Button>
|
|
62
68
|
{/if}
|
|
@@ -3,6 +3,11 @@ import type { ButtonSize, ButtonVariant } from "../button/index.js";
|
|
|
3
3
|
type $$ComponentProps = MessageScrollerPrimitive.ButtonProps & {
|
|
4
4
|
variant?: ButtonVariant;
|
|
5
5
|
size?: ButtonSize;
|
|
6
|
+
/**
|
|
7
|
+
* What a screen reader calls the icon-only button, in the app's language.
|
|
8
|
+
* Defaults to "Scroll to end", or "Scroll to start" when `direction="start"`.
|
|
9
|
+
*/
|
|
10
|
+
label?: string;
|
|
6
11
|
};
|
|
7
12
|
declare const $$Component: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
8
13
|
type $$Component = ReturnType<typeof $$Component>;
|
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
let {
|
|
8
8
|
ref = $bindable(null),
|
|
9
9
|
class: className,
|
|
10
|
+
label = "Next",
|
|
10
11
|
...restProps
|
|
11
|
-
}: PaginationPrimitive.NextButtonProps
|
|
12
|
+
}: PaginationPrimitive.NextButtonProps & {
|
|
13
|
+
/** The text beside the arrow from `sm` up. Name the button with `aria-label`. */
|
|
14
|
+
label?: string;
|
|
15
|
+
} = $props();
|
|
12
16
|
</script>
|
|
13
17
|
|
|
14
18
|
<PaginationPrimitive.NextButton
|
|
@@ -17,7 +21,7 @@
|
|
|
17
21
|
class={cn(buttonVariants({ variant: "ghost", size: "default" }), "pr-2!", className)}
|
|
18
22
|
{...restProps}
|
|
19
23
|
>
|
|
20
|
-
<span class="hidden sm:block">
|
|
24
|
+
<span class="hidden sm:block">{label}</span>
|
|
21
25
|
<CaretRight
|
|
22
26
|
aria-hidden="true"
|
|
23
27
|
data-icon="inline-end"
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { Pagination as PaginationPrimitive } from "bits-ui";
|
|
2
|
-
|
|
2
|
+
type $$ComponentProps = PaginationPrimitive.NextButtonProps & {
|
|
3
|
+
/** The text beside the arrow from `sm` up. Name the button with `aria-label`. */
|
|
4
|
+
label?: string;
|
|
5
|
+
};
|
|
6
|
+
declare const $$Component: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
3
7
|
type $$Component = ReturnType<typeof $$Component>;
|
|
4
8
|
export default $$Component;
|
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
let {
|
|
8
8
|
ref = $bindable(null),
|
|
9
9
|
class: className,
|
|
10
|
+
label = "Previous",
|
|
10
11
|
...restProps
|
|
11
|
-
}: PaginationPrimitive.PrevButtonProps
|
|
12
|
+
}: PaginationPrimitive.PrevButtonProps & {
|
|
13
|
+
/** The text beside the arrow from `sm` up. Name the button with `aria-label`. */
|
|
14
|
+
label?: string;
|
|
15
|
+
} = $props();
|
|
12
16
|
</script>
|
|
13
17
|
|
|
14
18
|
<PaginationPrimitive.PrevButton
|
|
@@ -26,5 +30,5 @@
|
|
|
26
30
|
data-icon="inline-start"
|
|
27
31
|
class="cn-rtl-flip"
|
|
28
32
|
/>
|
|
29
|
-
<span class="hidden sm:block">
|
|
33
|
+
<span class="hidden sm:block">{label}</span>
|
|
30
34
|
</PaginationPrimitive.PrevButton>
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { Pagination as PaginationPrimitive } from "bits-ui";
|
|
2
|
-
|
|
2
|
+
type $$ComponentProps = PaginationPrimitive.PrevButtonProps & {
|
|
3
|
+
/** The text beside the arrow from `sm` up. Name the button with `aria-label`. */
|
|
4
|
+
label?: string;
|
|
5
|
+
};
|
|
6
|
+
declare const $$Component: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
3
7
|
type $$Component = ReturnType<typeof $$Component>;
|
|
4
8
|
export default $$Component;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// An open Sheet and Dialog, for the close-label test.
|
|
3
|
+
import * as Sheet from "./index.js";
|
|
4
|
+
import * as Dialog from "../dialog/index.js";
|
|
5
|
+
|
|
6
|
+
let { closeLabel }: { closeLabel?: string } = $props();
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<Sheet.Root open>
|
|
10
|
+
<Sheet.Content {closeLabel}>
|
|
11
|
+
<Sheet.Title>Sheet</Sheet.Title>
|
|
12
|
+
</Sheet.Content>
|
|
13
|
+
</Sheet.Root>
|
|
14
|
+
|
|
15
|
+
<Dialog.Root open>
|
|
16
|
+
<Dialog.Content {closeLabel}>
|
|
17
|
+
<Dialog.Title>Dialog</Dialog.Title>
|
|
18
|
+
<Dialog.Footer showCloseButton {closeLabel} />
|
|
19
|
+
</Dialog.Content>
|
|
20
|
+
</Dialog.Root>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { flushSync, mount, unmount } from "svelte";
|
|
2
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
3
|
+
import Overlays from "./close-label.test.svelte";
|
|
4
|
+
import SecretInput from "../input/secret-input.svelte";
|
|
5
|
+
|
|
6
|
+
// A French app's Assistant panel announced "Close" (TSK-2735): each built-in label takes a prop.
|
|
7
|
+
function closeNames(closeLabel?: string): string[] {
|
|
8
|
+
const app = mount(Overlays, { target: document.body, props: closeLabel ? { closeLabel } : {} });
|
|
9
|
+
flushSync();
|
|
10
|
+
const names = [
|
|
11
|
+
...document.querySelectorAll(
|
|
12
|
+
'[data-slot="sheet-close"], [data-slot="dialog-close"], [data-slot="dialog-footer"] button',
|
|
13
|
+
),
|
|
14
|
+
].map((b) => b.textContent?.trim() ?? "");
|
|
15
|
+
void unmount(app);
|
|
16
|
+
return names;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("built-in close buttons", () => {
|
|
20
|
+
it("say Close by default", () => {
|
|
21
|
+
expect(closeNames()).toEqual(["Close", "Close", "Close"]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("say the caller's closeLabel", () => {
|
|
25
|
+
expect(closeNames("Fermer")).toEqual(["Fermer", "Fermer", "Fermer"]);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("SecretInput", () => {
|
|
30
|
+
it("names its reveal button in the caller's words", () => {
|
|
31
|
+
const target = document.createElement("div");
|
|
32
|
+
const app = mount(SecretInput, {
|
|
33
|
+
target,
|
|
34
|
+
props: { revealLabel: "Afficher", hideLabel: "Masquer" },
|
|
35
|
+
});
|
|
36
|
+
flushSync();
|
|
37
|
+
const button = target.querySelector("button");
|
|
38
|
+
expect(button?.getAttribute("aria-label")).toBe("Afficher");
|
|
39
|
+
button?.click();
|
|
40
|
+
flushSync();
|
|
41
|
+
expect(button?.getAttribute("aria-label")).toBe("Masquer");
|
|
42
|
+
void unmount(app);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
class: className,
|
|
20
20
|
side = "right",
|
|
21
21
|
showCloseButton = true,
|
|
22
|
+
closeLabel = "Close",
|
|
22
23
|
floating,
|
|
23
24
|
portalProps,
|
|
24
25
|
children,
|
|
@@ -27,6 +28,8 @@
|
|
|
27
28
|
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof SheetPortal>>;
|
|
28
29
|
side?: Side;
|
|
29
30
|
showCloseButton?: boolean;
|
|
31
|
+
/** What a screen reader calls the built-in close button, in the app's language. */
|
|
32
|
+
closeLabel?: string;
|
|
30
33
|
/**
|
|
31
34
|
* From `sm` up a left or right sheet floats 8px off the edge, like SidePanel;
|
|
32
35
|
* `floating={false}` keeps it flush. A phone stays full bleed for the width.
|
|
@@ -82,7 +85,7 @@
|
|
|
82
85
|
{#snippet child({ props })}
|
|
83
86
|
<Button variant="ghost" class="absolute top-3 right-2" size="sm" icon {...props}>
|
|
84
87
|
<X aria-hidden="true" size={16} />
|
|
85
|
-
<span class="sr-only">
|
|
88
|
+
<span class="sr-only">{closeLabel}</span>
|
|
86
89
|
</Button>
|
|
87
90
|
{/snippet}
|
|
88
91
|
</SheetPrimitive.Close>
|
|
@@ -9,6 +9,8 @@ type $$ComponentProps = WithoutChildrenOrChild<SheetPrimitive.ContentProps> & {
|
|
|
9
9
|
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof SheetPortal>>;
|
|
10
10
|
side?: Side;
|
|
11
11
|
showCloseButton?: boolean;
|
|
12
|
+
/** What a screen reader calls the built-in close button, in the app's language. */
|
|
13
|
+
closeLabel?: string;
|
|
12
14
|
/**
|
|
13
15
|
* From `sm` up a left or right sheet floats 8px off the edge, like SidePanel;
|
|
14
16
|
* `floating={false}` keeps it flush. A phone stays full bleed for the width.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
declare const $$Component: import("svelte").Component<(Omit<import("svelte/elements").HTMLInputAttributes, "type"> & ({
|
|
2
2
|
type: "file";
|
|
3
|
-
files?: FileList;
|
|
3
|
+
files?: FileList | null;
|
|
4
4
|
} | {
|
|
5
5
|
type?: "number" | "hidden" | "color" | "text" | "search" | "submit" | "reset" | "button" | "checkbox" | "radio" | (string & {}) | "tel" | "url" | "email" | "date" | "image" | "time" | "month" | "datetime-local" | "password" | "range" | "week";
|
|
6
6
|
files?: undefined;
|
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
ref = $bindable(null),
|
|
8
8
|
class: className,
|
|
9
9
|
children,
|
|
10
|
+
label = "Toggle Sidebar",
|
|
10
11
|
...restProps
|
|
11
|
-
}: WithElementRef<HTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
|
|
12
|
+
}: WithElementRef<HTMLAttributes<HTMLButtonElement>, HTMLButtonElement> & {
|
|
13
|
+
/** The rail's name and hover tooltip, in the app's language. */
|
|
14
|
+
label?: string;
|
|
15
|
+
} = $props();
|
|
12
16
|
|
|
13
17
|
const sidebar = useSidebar();
|
|
14
18
|
</script>
|
|
@@ -17,10 +21,10 @@
|
|
|
17
21
|
bind:this={ref}
|
|
18
22
|
data-sidebar="rail"
|
|
19
23
|
data-slot="sidebar-rail"
|
|
20
|
-
aria-label=
|
|
24
|
+
aria-label={label}
|
|
21
25
|
tabindex={-1}
|
|
22
26
|
onclick={sidebar.toggle}
|
|
23
|
-
title=
|
|
27
|
+
title={label}
|
|
24
28
|
class={cn(
|
|
25
29
|
"hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-[background-color,transform,inset] duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex",
|
|
26
30
|
"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { type WithElementRef } from "../../lib/utils.js";
|
|
2
2
|
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
-
|
|
3
|
+
type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLButtonElement>, HTMLButtonElement> & {
|
|
4
|
+
/** The rail's name and hover tooltip, in the app's language. */
|
|
5
|
+
label?: string;
|
|
6
|
+
};
|
|
7
|
+
declare const $$Component: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
4
8
|
type $$Component = ReturnType<typeof $$Component>;
|
|
5
9
|
export default $$Component;
|
|
@@ -9,9 +9,12 @@
|
|
|
9
9
|
ref = $bindable(null),
|
|
10
10
|
class: className,
|
|
11
11
|
onclick,
|
|
12
|
+
label = "Toggle Sidebar",
|
|
12
13
|
...restProps
|
|
13
14
|
}: ComponentProps<typeof Button> & {
|
|
14
15
|
onclick?: (e: MouseEvent) => void;
|
|
16
|
+
/** What a screen reader calls the icon-only button, in the app's language. */
|
|
17
|
+
label?: string;
|
|
15
18
|
} = $props();
|
|
16
19
|
|
|
17
20
|
const sidebar = useSidebar();
|
|
@@ -35,5 +38,5 @@
|
|
|
35
38
|
{...restProps}
|
|
36
39
|
>
|
|
37
40
|
<SidebarSimple aria-hidden="true" size={16} />
|
|
38
|
-
<span class="sr-only">
|
|
41
|
+
<span class="sr-only">{label}</span>
|
|
39
42
|
</Button>
|
|
@@ -2,6 +2,8 @@ import { Button } from "../button/index.js";
|
|
|
2
2
|
import type { ComponentProps } from "svelte";
|
|
3
3
|
type $$ComponentProps = ComponentProps<typeof Button> & {
|
|
4
4
|
onclick?: (e: MouseEvent) => void;
|
|
5
|
+
/** What a screen reader calls the icon-only button, in the app's language. */
|
|
6
|
+
label?: string;
|
|
5
7
|
};
|
|
6
8
|
declare const $$Component: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
7
9
|
type $$Component = ReturnType<typeof $$Component>;
|
package/src/thread/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as TraceUI from "../shadcn/trace/index.js";
|
|
3
|
+
import { TagChip } from "../components/chip/index.js";
|
|
3
4
|
import type { ThreadEvent } from "./types.js";
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
@@ -8,6 +9,16 @@
|
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
let { events, when }: Props = $props();
|
|
12
|
+
|
|
13
|
+
const TOKEN = "{record}";
|
|
14
|
+
|
|
15
|
+
// The sentence around the chip. No token puts the chip at the end, so an older event with a
|
|
16
|
+
// record and a plain title still reads in order.
|
|
17
|
+
function split(event: ThreadEvent): [string, string] {
|
|
18
|
+
const at = event.title.indexOf(TOKEN);
|
|
19
|
+
if (at < 0) return [`${event.title} `, ""];
|
|
20
|
+
return [event.title.slice(0, at), event.title.slice(at + TOKEN.length)];
|
|
21
|
+
}
|
|
11
22
|
</script>
|
|
12
23
|
|
|
13
24
|
<!-- ps-1 centres each 16px icon in the 24px avatar column the comments use. -->
|
|
@@ -19,9 +30,27 @@
|
|
|
19
30
|
<TraceUI.Icon class="mt-px"><Icon size={14} /></TraceUI.Icon>
|
|
20
31
|
{/if}
|
|
21
32
|
<!-- Wraps, not truncates: a status name can be a sentence, and a touch screen has no hover title. -->
|
|
22
|
-
<TraceUI.Title class="flex-1 whitespace-normal text-pretty [overflow-wrap:anywhere]"
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
<TraceUI.Title class="flex-1 whitespace-normal text-pretty [overflow-wrap:anywhere]">
|
|
34
|
+
{#if event.record}
|
|
35
|
+
{@const record = event.record}
|
|
36
|
+
{@const [before, after] = split(event)}
|
|
37
|
+
{before}{#if record.href}<a
|
|
38
|
+
href={record.href}
|
|
39
|
+
class="rounded-sm align-middle outline-none focus-visible:shadow-[var(--ring-focus)]"
|
|
40
|
+
><TagChip>
|
|
41
|
+
{#snippet icon()}{#if record.icon}{@const RecordIcon = record.icon}<RecordIcon
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
/>{/if}{/snippet}{record.label}</TagChip
|
|
44
|
+
></a
|
|
45
|
+
>{:else}<TagChip class="align-middle">
|
|
46
|
+
{#snippet icon()}{#if record.icon}{@const RecordIcon = record.icon}<RecordIcon
|
|
47
|
+
aria-hidden="true"
|
|
48
|
+
/>{/if}{/snippet}{record.label}</TagChip
|
|
49
|
+
>{/if}{after}
|
|
50
|
+
{:else}
|
|
51
|
+
{event.title}
|
|
52
|
+
{/if}
|
|
53
|
+
</TraceUI.Title>
|
|
25
54
|
<TraceUI.Description>
|
|
26
55
|
<time datetime={event.at}>{when(event)}</time>
|
|
27
56
|
</TraceUI.Description>
|
package/src/thread/types.ts
CHANGED
|
@@ -57,6 +57,16 @@ export type ThreadEvent = {
|
|
|
57
57
|
/** ISO timestamp. */
|
|
58
58
|
at: string;
|
|
59
59
|
icon?: PhosphorIcon;
|
|
60
|
+
/** A record the sentence names, drawn as a chip inside it. Write `{record}` in `title` where
|
|
61
|
+
it goes; without the token it ends the sentence. */
|
|
62
|
+
record?: ThreadEventRecord;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/** A linked record inside an activity line: a task, a doc, a thread. */
|
|
66
|
+
export type ThreadEventRecord = {
|
|
67
|
+
label: string;
|
|
68
|
+
href?: string;
|
|
69
|
+
icon?: PhosphorIcon;
|
|
60
70
|
};
|
|
61
71
|
|
|
62
72
|
/** The thread in time order: a conversation, or a run of events between two of them. */
|
|
@@ -73,6 +73,8 @@
|
|
|
73
73
|
/** Width of the task column in px. It never takes more than 40% of a narrow screen. */
|
|
74
74
|
labelWidth?: number;
|
|
75
75
|
labelHeading?: string;
|
|
76
|
+
/** What a screen reader calls the list of bars. */
|
|
77
|
+
timelineLabel?: string;
|
|
76
78
|
locale?: string;
|
|
77
79
|
/** Drops the card chrome (border, radius, fill) so the chart sits flat on the page. */
|
|
78
80
|
flat?: boolean;
|
|
@@ -105,6 +107,7 @@
|
|
|
105
107
|
rowHeight = 40,
|
|
106
108
|
labelWidth = 240,
|
|
107
109
|
labelHeading = "Task",
|
|
110
|
+
timelineLabel = "Timeline",
|
|
108
111
|
locale = "en-US",
|
|
109
112
|
flat = false,
|
|
110
113
|
class: className,
|
|
@@ -227,7 +230,7 @@
|
|
|
227
230
|
draggingId={drag?.id ?? null}
|
|
228
231
|
/>
|
|
229
232
|
|
|
230
|
-
<div role="list" aria-label=
|
|
233
|
+
<div role="list" aria-label={timelineLabel}>
|
|
231
234
|
{#each rows as row, index (row.kind === "group" ? `g:${row.group.id}` : row.id)}
|
|
232
235
|
{#if row.kind === "group"}
|
|
233
236
|
<GanttGroupRow {row} {index} {layout} staggerCap={STAGGER_CAP} />
|
|
@@ -50,6 +50,8 @@ interface Props<T> {
|
|
|
50
50
|
/** Width of the task column in px. It never takes more than 40% of a narrow screen. */
|
|
51
51
|
labelWidth?: number;
|
|
52
52
|
labelHeading?: string;
|
|
53
|
+
/** What a screen reader calls the list of bars. */
|
|
54
|
+
timelineLabel?: string;
|
|
53
55
|
locale?: string;
|
|
54
56
|
/** Drops the card chrome (border, radius, fill) so the chart sits flat on the page. */
|
|
55
57
|
flat?: boolean;
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/** Deletes every `x.svelte.js` that sits beside an `x.svelte.ts` under `src`. */
|
|
2
|
-
export function cleanSvelteModules(src: any): void;
|
|
3
|
-
/** Writes a type-free `x.svelte.js` beside each `x.svelte.ts` under `src`, and returns how many. */
|
|
4
|
-
export function writeSvelteModules(src: any): Promise<number>;
|