contain-css-svelte 1.1.20 → 1.2.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/dist/Card.svelte +15 -6
- package/dist/Card.svelte.d.ts +4 -5
- package/dist/controls/Button.svelte +19 -5
- package/dist/controls/Button.svelte.d.ts +4 -4
- package/dist/controls/ButtonLink.svelte +17 -14
- package/dist/controls/ButtonLink.svelte.d.ts +4 -4
- package/dist/controls/Checkbox.svelte +15 -8
- package/dist/controls/Checkbox.svelte.d.ts +4 -4
- package/dist/controls/CircleButton.svelte +14 -7
- package/dist/controls/CircleButton.svelte.d.ts +4 -4
- package/dist/controls/Input.svelte +14 -3
- package/dist/controls/Input.svelte.d.ts +4 -3
- package/dist/controls/Option.svelte +4 -3
- package/dist/controls/Option.svelte.d.ts +3 -2
- package/dist/controls/RadioButton.svelte +14 -7
- package/dist/controls/RadioButton.svelte.d.ts +4 -4
- package/dist/controls/Select.svelte +15 -3
- package/dist/controls/Select.svelte.d.ts +4 -3
- package/dist/controls/Slider.svelte +5 -11
- package/dist/controls/Slider.svelte.d.ts +3 -3
- package/dist/controls/TabItem.svelte +24 -5
- package/dist/controls/TabItem.svelte.d.ts +4 -4
- package/dist/controls/Toggle.svelte +21 -5
- package/dist/controls/Toggle.svelte.d.ts +4 -4
- package/dist/dropdowns/DropdownMenu.svelte +18 -6
- package/dist/dropdowns/DropdownMenu.svelte.d.ts +4 -4
- package/dist/layout/Accordion.svelte +17 -2
- package/dist/layout/Accordion.svelte.d.ts +5 -3
- package/dist/layout/Bar.svelte +13 -7
- package/dist/layout/Bar.svelte.d.ts +4 -4
- package/dist/layout/Column.svelte.d.ts +3 -3
- package/dist/layout/ColumnContainer.svelte +12 -9
- package/dist/layout/ColumnContainer.svelte.d.ts +4 -4
- package/dist/layout/Columns.svelte +4 -3
- package/dist/layout/Columns.svelte.d.ts +7 -3
- package/dist/layout/Container.svelte +23 -8
- package/dist/layout/Container.svelte.d.ts +7 -5
- package/dist/layout/DataList.svelte +15 -7
- package/dist/layout/DataList.svelte.d.ts +5 -4
- package/dist/layout/DataListItem.svelte +14 -8
- package/dist/layout/DataListItem.svelte.d.ts +4 -4
- package/dist/layout/Fieldset.svelte +16 -8
- package/dist/layout/Fieldset.svelte.d.ts +6 -3
- package/dist/layout/Form.svelte +17 -8
- package/dist/layout/Form.svelte.d.ts +8 -6
- package/dist/layout/FormItem.svelte +12 -7
- package/dist/layout/FormItem.svelte.d.ts +4 -3
- package/dist/layout/FormProvider.svelte +3 -0
- package/dist/layout/GridLayout.svelte +14 -9
- package/dist/layout/GridLayout.svelte.d.ts +4 -4
- package/dist/layout/GridRow.svelte +6 -10
- package/dist/layout/GridRow.svelte.d.ts +3 -2
- package/dist/layout/Hero.svelte +14 -8
- package/dist/layout/Hero.svelte.d.ts +4 -4
- package/dist/layout/Inline.svelte +16 -15
- package/dist/layout/Inline.svelte.d.ts +4 -4
- package/dist/layout/MenuList.svelte +13 -6
- package/dist/layout/MenuList.svelte.d.ts +4 -4
- package/dist/layout/Page.svelte +14 -8
- package/dist/layout/Page.svelte.d.ts +11 -3
- package/dist/layout/ResponsiveText.svelte +4 -0
- package/dist/layout/Row.svelte.d.ts +3 -3
- package/dist/layout/RowContainer.svelte +12 -9
- package/dist/layout/RowContainer.svelte.d.ts +4 -4
- package/dist/layout/Sidebar.svelte +17 -6
- package/dist/layout/Sidebar.svelte.d.ts +12 -4
- package/dist/layout/SplitPane.svelte +8 -14
- package/dist/layout/SplitPane.svelte.d.ts +16 -2
- package/dist/layout/Stack.svelte +16 -15
- package/dist/layout/Stack.svelte.d.ts +4 -4
- package/dist/layout/TabBar.svelte +12 -2
- package/dist/layout/TabBar.svelte.d.ts +17 -10
- package/dist/layout/Table.svelte +5 -1
- package/dist/layout/Tile.svelte +40 -27
- package/dist/layout/Tile.svelte.d.ts +32 -10
- package/dist/misc/Code.svelte +2 -0
- package/dist/misc/Progress.svelte +25 -8
- package/dist/misc/Progress.svelte.d.ts +15 -8
- package/dist/misc/Tag.svelte +97 -44
- package/dist/misc/Tag.svelte.d.ts +17 -5
- package/dist/overlays/Dialog.svelte +18 -5
- package/dist/overlays/Dialog.svelte.d.ts +5 -3
- package/dist/overlays/Tooltip.svelte +21 -3
- package/dist/overlays/Tooltip.svelte.d.ts +5 -3
- package/dist/styleProps.d.ts +67 -0
- package/dist/styleProps.js +76 -0
- package/dist/types.d.ts +40 -1
- package/dist/types.js +1 -1
- package/dist/typography/Text.svelte +4 -11
- package/dist/typography/TextLayout.svelte +4 -7
- package/dist/typography/TextLayout.svelte.d.ts +4 -3
- package/dist/util.d.ts +60 -3
- package/dist/util.js +101 -11
- package/package.json +1 -1
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
<script lang="ts">import CircleButton from "../controls/CircleButton.svelte";
|
|
2
|
-
import { copyCSSVariables,
|
|
3
|
-
|
|
2
|
+
import { copyCSSVariables, splitProps } from "../util";
|
|
3
|
+
import { COLOR_VARS, PADDING_VARS, RADIUS_VARS, TYPOGRAPHY_CONTAINER_VARS, } from "../styleProps";
|
|
4
|
+
let { open = true, modal = true, dismissible = false, children, onClose, onclose: oncloseProp = null, class: className, ...restProps } = $props();
|
|
4
5
|
let onclose = $derived(oncloseProp || onClose);
|
|
5
|
-
|
|
6
|
+
/* The <section> is the variable carrier that copyCSSVariables reads through,
|
|
7
|
+
so it takes the style while the <dialog> takes the attributes -- a
|
|
8
|
+
caller's `aria-*` and `data-*` belong on the dialog itself. */
|
|
9
|
+
/* The shorthands this component's own CSS backs, one group per mixin
|
|
10
|
+
it includes. The Props type is derived from this same array, so what
|
|
11
|
+
the component accepts and what it emits cannot drift apart. */
|
|
12
|
+
const DIALOG_VARS = [
|
|
13
|
+
...COLOR_VARS,
|
|
14
|
+
...PADDING_VARS,
|
|
15
|
+
...RADIUS_VARS,
|
|
16
|
+
...TYPOGRAPHY_CONTAINER_VARS,
|
|
17
|
+
];
|
|
18
|
+
const el = $derived(splitProps(restProps, "dialog", DIALOG_VARS));
|
|
6
19
|
// Handle backdrop click to close modal (click outside behavior)
|
|
7
20
|
// Clicks on the dialog element itself (the backdrop) close the dialog
|
|
8
21
|
function handleBackdropClick(event) {
|
|
@@ -52,9 +65,9 @@ let dialogElement;
|
|
|
52
65
|
let ref;
|
|
53
66
|
</script>
|
|
54
67
|
|
|
55
|
-
<section {style}>
|
|
68
|
+
<section style={el.style}>
|
|
56
69
|
<div class="variable-placeholder" bind:this={ref}></div>
|
|
57
|
-
<dialog bind:this={dialogElement} {onclose} {...
|
|
70
|
+
<dialog bind:this={dialogElement} {onclose} class={className} {...el.attrs}>
|
|
58
71
|
<div class="close-bar">
|
|
59
72
|
<div class="close-button">
|
|
60
73
|
<CircleButton onclick={onclose}>×</CircleButton>
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { type StyleProps } from "../styleProps";
|
|
1
2
|
import type { Snippet } from "svelte";
|
|
2
|
-
type
|
|
3
|
+
import type { HTMLDialogAttributes } from "svelte/elements";
|
|
4
|
+
import type { ContainProps } from "../types";
|
|
5
|
+
declare const Dialog: import("svelte").Component<ContainProps<HTMLDialogAttributes, {
|
|
3
6
|
open?: boolean;
|
|
4
7
|
modal?: boolean;
|
|
5
8
|
dismissible?: boolean;
|
|
6
9
|
children?: Snippet;
|
|
7
10
|
onclose?: (() => void) | null;
|
|
8
11
|
onClose?: (() => void) | null;
|
|
9
|
-
}
|
|
10
|
-
declare const Dialog: import("svelte").Component<PropsType, {}, "">;
|
|
12
|
+
}, StyleProps<readonly ["bg", "fg", "padding", "borderRadius", "fontFamily", "fontSize", "fontWeight", "lineHeight", "letterSpacing", "textAlign", "textTransform", "textDecoration", "fontVariant", "lineWidth"]>>, {}, "">;
|
|
11
13
|
type Dialog = ReturnType<typeof Dialog>;
|
|
12
14
|
export default Dialog;
|
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
<script lang="ts">import { onDestroy, tick } from "svelte";
|
|
2
|
+
import { elementProps } from "../util";
|
|
3
|
+
import { COLOR_VARS, PADDING_VARS, RADIUS_VARS, TYPOGRAPHY_CONTAINER_VARS, } from "../styleProps";
|
|
2
4
|
let tooltipDiv = $state();
|
|
3
5
|
let targetDiv = $state();
|
|
4
6
|
let tooltipMeasurementDiv = $state();
|
|
5
|
-
let { tooltipText = "", vertical = "bottom", horizontal = "right", children, tooltip, block = false, } = $props();
|
|
7
|
+
let { tooltipText = "", vertical = "bottom", horizontal = "right", children, tooltip, block = false, class: className, ...restProps } = $props();
|
|
8
|
+
/* The wrapper is the element a caller is pointing at -- the tooltip itself
|
|
9
|
+
is a popover the component owns and positions. */
|
|
10
|
+
/* The shorthands this component's own CSS backs, one group per mixin
|
|
11
|
+
it includes. The Props type is derived from this same array, so what
|
|
12
|
+
the component accepts and what it emits cannot drift apart. */
|
|
13
|
+
const TOOLTIP_VARS = [
|
|
14
|
+
...COLOR_VARS,
|
|
15
|
+
...PADDING_VARS,
|
|
16
|
+
...RADIUS_VARS,
|
|
17
|
+
...TYPOGRAPHY_CONTAINER_VARS,
|
|
18
|
+
];
|
|
19
|
+
/* The variables land on the wrapper and reach the popover by inheritance:
|
|
20
|
+
the tooltip is a descendant of it, and custom properties inherit. */
|
|
21
|
+
const el = $derived(elementProps(restProps, "tooltip", TOOLTIP_VARS));
|
|
6
22
|
// svelte-ignore state_referenced_locally
|
|
7
23
|
let renderedVertical = $state(vertical);
|
|
8
24
|
// svelte-ignore state_referenced_locally
|
|
@@ -206,11 +222,12 @@ async function showPopover() {
|
|
|
206
222
|
{#if block}
|
|
207
223
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
208
224
|
<div
|
|
209
|
-
class="tooltip-wrapper"
|
|
225
|
+
class={["tooltip-wrapper", className]}
|
|
210
226
|
onmouseenter={() => showPopover()}
|
|
211
227
|
onmouseleave={() => hidePopover()}
|
|
212
228
|
onfocusin={() => showPopover()}
|
|
213
229
|
onfocusout={() => hidePopover()}
|
|
230
|
+
{...el}
|
|
214
231
|
>
|
|
215
232
|
<div class="tooltip-target" bind:this={targetDiv}>
|
|
216
233
|
{@render children?.()}
|
|
@@ -241,11 +258,12 @@ async function showPopover() {
|
|
|
241
258
|
{:else}
|
|
242
259
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
243
260
|
<span
|
|
244
|
-
class="tooltip-wrapper"
|
|
261
|
+
class={["tooltip-wrapper", className]}
|
|
245
262
|
onmouseenter={() => showPopover()}
|
|
246
263
|
onmouseleave={() => hidePopover()}
|
|
247
264
|
onfocusin={() => showPopover()}
|
|
248
265
|
onfocusout={() => hidePopover()}
|
|
266
|
+
{...el}
|
|
249
267
|
>
|
|
250
268
|
<span class="tooltip-target" bind:this={targetDiv}>
|
|
251
269
|
{@render children?.()}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
2
|
+
import type { ContainProps } from "../types";
|
|
3
|
+
import { type StyleProps } from "../styleProps";
|
|
4
|
+
declare const Tooltip: import("svelte").Component<ContainProps<HTMLAttributes<HTMLElement>, {
|
|
2
5
|
tooltipText?: string;
|
|
3
6
|
vertical?: string;
|
|
4
7
|
horizontal?: string;
|
|
5
8
|
children?: import("svelte").Snippet;
|
|
6
9
|
tooltip?: import("svelte").Snippet;
|
|
7
10
|
block?: boolean;
|
|
8
|
-
}
|
|
9
|
-
declare const Tooltip: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
}, StyleProps<readonly ["bg", "fg", "padding", "borderRadius", "fontFamily", "fontSize", "fontWeight", "lineHeight", "letterSpacing", "textAlign", "textTransform", "textDecoration", "fontVariant", "lineWidth"]>>, {}, "">;
|
|
10
12
|
type Tooltip = ReturnType<typeof Tooltip>;
|
|
11
13
|
export default Tooltip;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The style-prop shorthands, grouped to mirror the SCSS mixins that make them
|
|
3
|
+
* readable.
|
|
4
|
+
*
|
|
5
|
+
* The problem this solves: which shorthands a component accepted used to be a
|
|
6
|
+
* hand-maintained list in each component, drifting from the mixins its CSS
|
|
7
|
+
* actually included. Tag exposed `fontSize` while the other 23 components
|
|
8
|
+
* whose CSS includes a typography mixin exposed none of it -- `--button-font-
|
|
9
|
+
* weight` resolved perfectly well, there was just no prop for it. In the other
|
|
10
|
+
* direction, FormItem injected three variables no stylesheet read at all.
|
|
11
|
+
*
|
|
12
|
+
* So each group below names one mixin, and a component's prop list is the
|
|
13
|
+
* union of the groups for the mixins its own CSS includes. "Which shorthands
|
|
14
|
+
* does this component take" stops being a decision and becomes a consequence.
|
|
15
|
+
*
|
|
16
|
+
* The prop types are derived from these same arrays via `StyleProps<>`, so the
|
|
17
|
+
* type and the runtime list cannot disagree -- adding a name to the array is
|
|
18
|
+
* what adds the prop.
|
|
19
|
+
*
|
|
20
|
+
* A shorthand is only safe when the element the component renders has no HTML
|
|
21
|
+
* attribute of that name: `ContainProps` resolves the clash by dropping the
|
|
22
|
+
* attribute, which is right for a deprecated or meaningless one and wrong for
|
|
23
|
+
* `<progress max>` or `<option label>`. That is why the size group is applied
|
|
24
|
+
* per component rather than being folded into the others, and why Progress's
|
|
25
|
+
* accessible-name prop is `progressLabel`.
|
|
26
|
+
*/
|
|
27
|
+
/** Every style prop takes a CSS value, or null/undefined to leave it unset. */
|
|
28
|
+
export type StyleProps<T extends readonly string[]> = {
|
|
29
|
+
[K in T[number]]?: string | null;
|
|
30
|
+
};
|
|
31
|
+
/** `@include color-props(<prefix>, ...)` */
|
|
32
|
+
export declare const COLOR_VARS: readonly ["bg", "fg"];
|
|
33
|
+
/** `@include padding-props(...)`, and the box-props family that includes it */
|
|
34
|
+
export declare const PADDING_VARS: readonly ["padding"];
|
|
35
|
+
/** The border-radius the box-props family sets alongside padding */
|
|
36
|
+
export declare const RADIUS_VARS: readonly ["borderRadius"];
|
|
37
|
+
/** `@include margin-props(<prefix>, ...)` */
|
|
38
|
+
export declare const MARGIN_VARS: readonly ["marginBlock", "marginInline"];
|
|
39
|
+
/** `@include gap-props(<prefix>, ...)` */
|
|
40
|
+
export declare const GAP_VARS: readonly ["gap"];
|
|
41
|
+
/**
|
|
42
|
+
* `@include typography-props(...)` and its `-bare` / `-bold` variants.
|
|
43
|
+
*
|
|
44
|
+
* `fontSize` was the only one of these ever exposed as a prop, and only on
|
|
45
|
+
* Tag. The rest resolved in CSS the whole time.
|
|
46
|
+
*/
|
|
47
|
+
export declare const TYPOGRAPHY_VARS: readonly ["fontFamily", "fontSize", "fontWeight", "lineHeight", "letterSpacing", "textAlign", "textTransform", "textDecoration", "fontVariant"];
|
|
48
|
+
/**
|
|
49
|
+
* `@include typography-container-props(...)`, which is typography-props plus
|
|
50
|
+
* the readable measure it imposes on prose children.
|
|
51
|
+
*/
|
|
52
|
+
export declare const TYPOGRAPHY_CONTAINER_VARS: readonly ["fontFamily", "fontSize", "fontWeight", "lineHeight", "letterSpacing", "textAlign", "textTransform", "textDecoration", "fontVariant", "lineWidth"];
|
|
53
|
+
/**
|
|
54
|
+
* `@include box-size-props(<prefix>, ...)`.
|
|
55
|
+
*
|
|
56
|
+
* Deliberately NOT part of any other group: `width` and `height` are real HTML
|
|
57
|
+
* attributes on `<input>`, `<img>`, `<canvas>` and `<iframe>`, so a component
|
|
58
|
+
* rendering one of those must not shadow them. Applied per component.
|
|
59
|
+
*/
|
|
60
|
+
export declare const SIZE_VARS: readonly ["width", "minWidth", "maxWidth", "height", "minHeight", "maxHeight"];
|
|
61
|
+
export type ColorStyleProps = StyleProps<typeof COLOR_VARS>;
|
|
62
|
+
export type PaddingStyleProps = StyleProps<typeof PADDING_VARS>;
|
|
63
|
+
export type RadiusStyleProps = StyleProps<typeof RADIUS_VARS>;
|
|
64
|
+
export type GapStyleProps = StyleProps<typeof GAP_VARS>;
|
|
65
|
+
export type TypographyStyleProps = StyleProps<typeof TYPOGRAPHY_VARS>;
|
|
66
|
+
export type TypographyContainerStyleProps = StyleProps<typeof TYPOGRAPHY_CONTAINER_VARS>;
|
|
67
|
+
export type SizeStyleProps = StyleProps<typeof SIZE_VARS>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The style-prop shorthands, grouped to mirror the SCSS mixins that make them
|
|
3
|
+
* readable.
|
|
4
|
+
*
|
|
5
|
+
* The problem this solves: which shorthands a component accepted used to be a
|
|
6
|
+
* hand-maintained list in each component, drifting from the mixins its CSS
|
|
7
|
+
* actually included. Tag exposed `fontSize` while the other 23 components
|
|
8
|
+
* whose CSS includes a typography mixin exposed none of it -- `--button-font-
|
|
9
|
+
* weight` resolved perfectly well, there was just no prop for it. In the other
|
|
10
|
+
* direction, FormItem injected three variables no stylesheet read at all.
|
|
11
|
+
*
|
|
12
|
+
* So each group below names one mixin, and a component's prop list is the
|
|
13
|
+
* union of the groups for the mixins its own CSS includes. "Which shorthands
|
|
14
|
+
* does this component take" stops being a decision and becomes a consequence.
|
|
15
|
+
*
|
|
16
|
+
* The prop types are derived from these same arrays via `StyleProps<>`, so the
|
|
17
|
+
* type and the runtime list cannot disagree -- adding a name to the array is
|
|
18
|
+
* what adds the prop.
|
|
19
|
+
*
|
|
20
|
+
* A shorthand is only safe when the element the component renders has no HTML
|
|
21
|
+
* attribute of that name: `ContainProps` resolves the clash by dropping the
|
|
22
|
+
* attribute, which is right for a deprecated or meaningless one and wrong for
|
|
23
|
+
* `<progress max>` or `<option label>`. That is why the size group is applied
|
|
24
|
+
* per component rather than being folded into the others, and why Progress's
|
|
25
|
+
* accessible-name prop is `progressLabel`.
|
|
26
|
+
*/
|
|
27
|
+
/** `@include color-props(<prefix>, ...)` */
|
|
28
|
+
export const COLOR_VARS = ["bg", "fg"];
|
|
29
|
+
/** `@include padding-props(...)`, and the box-props family that includes it */
|
|
30
|
+
export const PADDING_VARS = ["padding"];
|
|
31
|
+
/** The border-radius the box-props family sets alongside padding */
|
|
32
|
+
export const RADIUS_VARS = ["borderRadius"];
|
|
33
|
+
/** `@include margin-props(<prefix>, ...)` */
|
|
34
|
+
export const MARGIN_VARS = ["marginBlock", "marginInline"];
|
|
35
|
+
/** `@include gap-props(<prefix>, ...)` */
|
|
36
|
+
export const GAP_VARS = ["gap"];
|
|
37
|
+
/**
|
|
38
|
+
* `@include typography-props(...)` and its `-bare` / `-bold` variants.
|
|
39
|
+
*
|
|
40
|
+
* `fontSize` was the only one of these ever exposed as a prop, and only on
|
|
41
|
+
* Tag. The rest resolved in CSS the whole time.
|
|
42
|
+
*/
|
|
43
|
+
export const TYPOGRAPHY_VARS = [
|
|
44
|
+
"fontFamily",
|
|
45
|
+
"fontSize",
|
|
46
|
+
"fontWeight",
|
|
47
|
+
"lineHeight",
|
|
48
|
+
"letterSpacing",
|
|
49
|
+
"textAlign",
|
|
50
|
+
"textTransform",
|
|
51
|
+
"textDecoration",
|
|
52
|
+
"fontVariant",
|
|
53
|
+
];
|
|
54
|
+
/**
|
|
55
|
+
* `@include typography-container-props(...)`, which is typography-props plus
|
|
56
|
+
* the readable measure it imposes on prose children.
|
|
57
|
+
*/
|
|
58
|
+
export const TYPOGRAPHY_CONTAINER_VARS = [
|
|
59
|
+
...TYPOGRAPHY_VARS,
|
|
60
|
+
"lineWidth",
|
|
61
|
+
];
|
|
62
|
+
/**
|
|
63
|
+
* `@include box-size-props(<prefix>, ...)`.
|
|
64
|
+
*
|
|
65
|
+
* Deliberately NOT part of any other group: `width` and `height` are real HTML
|
|
66
|
+
* attributes on `<input>`, `<img>`, `<canvas>` and `<iframe>`, so a component
|
|
67
|
+
* rendering one of those must not shadow them. Applied per component.
|
|
68
|
+
*/
|
|
69
|
+
export const SIZE_VARS = [
|
|
70
|
+
"width",
|
|
71
|
+
"minWidth",
|
|
72
|
+
"maxWidth",
|
|
73
|
+
"height",
|
|
74
|
+
"minHeight",
|
|
75
|
+
"maxHeight",
|
|
76
|
+
];
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Common CSS variable props that can be passed to components.
|
|
3
|
-
* These are converted to CSS custom properties via
|
|
3
|
+
* These are converted to CSS custom properties via elementProps().
|
|
4
4
|
*/
|
|
5
5
|
/** Base style props supported by most components */
|
|
6
6
|
export interface BaseStyleProps {
|
|
@@ -88,3 +88,42 @@ export interface DropdownMenuStyleProps extends BaseStyleProps {
|
|
|
88
88
|
triggerFg?: string | null;
|
|
89
89
|
triggerPadding?: string | null;
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Props that let a caller set CSS custom properties directly on a component.
|
|
93
|
+
*
|
|
94
|
+
* NOTE: writing `<Tag --tag-bg="red">` in a template does NOT go through this
|
|
95
|
+
* type -- Svelte intercepts `--*` attributes on components itself and renders
|
|
96
|
+
* `<svelte-css-wrapper style="display: contents; --tag-bg: red;">` around the
|
|
97
|
+
* component. This type covers the remaining path, an object spread
|
|
98
|
+
* (`<Tag {...{"--tag-bg": "red"}}>`), which Svelte cannot detect statically and
|
|
99
|
+
* therefore passes through as an ordinary prop.
|
|
100
|
+
*/
|
|
101
|
+
export type CssVarProps = {
|
|
102
|
+
[key: `--${string}`]: string | number | null | undefined;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* The standard prop type for a Contain CSS component.
|
|
106
|
+
*
|
|
107
|
+
* Composes the three things every component's props are made of, and -- the
|
|
108
|
+
* reason this exists rather than a bare `&` chain -- removes the element
|
|
109
|
+
* attributes that the style props and the component's own props shadow.
|
|
110
|
+
* `width`, `height` and `align` are real HTML attributes, so intersecting
|
|
111
|
+
* `BaseStyleProps` with `HTMLInputAttributes` the old way produced
|
|
112
|
+
* `(string | null) & (string | number | undefined)` for `width`: a type neither
|
|
113
|
+
* side meant, which silently accepted and rejected the wrong values.
|
|
114
|
+
*
|
|
115
|
+
* @typeParam Attributes - the element's attribute type, e.g. `HTMLButtonAttributes`
|
|
116
|
+
* @typeParam Own - the component's own props (snippets, variant booleans, ...)
|
|
117
|
+
* @typeParam Style - the component's style props, e.g. `BaseStyleProps`
|
|
118
|
+
* @typeParam Owned - attributes the component controls and a caller must not
|
|
119
|
+
* set, e.g. `"type"` on Slider's range input. Spreading rest props last means
|
|
120
|
+
* a caller's value would otherwise win over the component's own.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* type Props = ContainProps<
|
|
124
|
+
* HTMLButtonAttributes,
|
|
125
|
+
* { primary?: boolean; children?: Snippet },
|
|
126
|
+
* BaseStyleProps
|
|
127
|
+
* >;
|
|
128
|
+
*/
|
|
129
|
+
export type ContainProps<Attributes, Own = {}, Style = {}, Owned extends keyof Attributes | string = never> = Own & Style & Omit<Attributes, keyof Own | keyof Style | Owned> & CssVarProps;
|
package/dist/types.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
<script lang="ts">import {
|
|
2
|
-
let { children, primary = false, secondary = false, info = false, success = false, warning = false, danger = false, muted = false, bold = false, italic = false, color, amount,
|
|
1
|
+
<script lang="ts">import { elementProps } from "../util";
|
|
2
|
+
let { children, primary = false, secondary = false, info = false, success = false, warning = false, danger = false, muted = false, bold = false, italic = false, color, amount, class: className, ...restProps } = $props();
|
|
3
3
|
// Choose one tone deterministically; explicit color takes precedence.
|
|
4
4
|
const tone = $derived(danger
|
|
5
5
|
? "danger"
|
|
@@ -30,17 +30,10 @@ const foreground = $derived.by(() => {
|
|
|
30
30
|
return mixed;
|
|
31
31
|
return `var(--${tone}-text-fg, ${mixed})`;
|
|
32
32
|
});
|
|
33
|
-
const
|
|
34
|
-
const style = $derived(injectVars(restProps, "text", []) + (inlineStyle ?? ""));
|
|
33
|
+
const el = $derived(elementProps(restProps, "text"));
|
|
35
34
|
</script>
|
|
36
35
|
|
|
37
|
-
<span
|
|
38
|
-
{...elementProps}
|
|
39
|
-
{style}
|
|
40
|
-
style:color={foreground}
|
|
41
|
-
class:bold
|
|
42
|
-
class:italic
|
|
43
|
-
>
|
|
36
|
+
<span class={className} class:bold class:italic style:color={foreground} {...el}>
|
|
44
37
|
{@render children?.()}
|
|
45
38
|
</span>
|
|
46
39
|
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
<script lang="ts">import {
|
|
2
|
-
let { id = "",
|
|
3
|
-
const
|
|
4
|
-
"marginBlock",
|
|
5
|
-
"marginInline",
|
|
6
|
-
]));
|
|
1
|
+
<script lang="ts">import { elementProps } from "../util";
|
|
2
|
+
let { id = "", children, class: className, ...restProps } = $props();
|
|
3
|
+
const el = $derived(elementProps(restProps, "text-layout", ["marginBlock", "marginInline"]));
|
|
7
4
|
</script>
|
|
8
5
|
|
|
9
|
-
<div {id} {
|
|
6
|
+
<div {id} class={className} {...el}>
|
|
10
7
|
{@render children?.()}
|
|
11
8
|
</div>
|
|
12
9
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
2
|
+
import type { ContainProps, MarginStyleProps } from "../types";
|
|
3
|
+
type Props = ContainProps<HTMLAttributes<HTMLDivElement>, {
|
|
3
4
|
id?: string;
|
|
4
5
|
children?: import("svelte").Snippet;
|
|
5
|
-
}
|
|
6
|
+
}, MarginStyleProps>;
|
|
6
7
|
declare const TextLayout: import("svelte").Component<Props, {}, "">;
|
|
7
8
|
type TextLayout = ReturnType<typeof TextLayout>;
|
|
8
9
|
export default TextLayout;
|
package/dist/util.d.ts
CHANGED
|
@@ -1,4 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Split a component's rest props into a `style` string and the attributes that
|
|
3
|
+
* belong on an element, keeping the two separate.
|
|
4
|
+
*
|
|
5
|
+
* Use this for a component that renders a wrapper around a control (Toggle,
|
|
6
|
+
* RadioButton, Checkbox, FormItem): the CSS variables and the caller's `style`
|
|
7
|
+
* belong on the wrapper, because that is what the component's own CSS targets
|
|
8
|
+
* and what a caller expects to position, while `aria-*`, `name`, `required`
|
|
9
|
+
* and `data-*` belong on the control itself. {@link elementProps} is the
|
|
10
|
+
* single-element case and is what most components want.
|
|
11
|
+
*
|
|
12
|
+
* @returns `style` for the wrapper, `attrs` (never containing `style`) for the
|
|
13
|
+
* control.
|
|
14
|
+
*/
|
|
15
|
+
export declare function splitProps(props: Record<string, any>, prefix: string, varList?: readonly string[], extraVars?: string): {
|
|
16
|
+
style: string;
|
|
17
|
+
attrs: Record<string, unknown>;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Split a component's rest props into the attributes that belong on its root
|
|
21
|
+
* element, with the component's CSS variables and the caller's own `style`
|
|
22
|
+
* merged into a single `style` attribute.
|
|
23
|
+
*
|
|
24
|
+
* This is the one place the library's attribute pass-through contract lives.
|
|
25
|
+
* It exists because spreading raw rest props gets three things wrong at once:
|
|
26
|
+
*
|
|
27
|
+
* 1. **Style props leaked as attributes.** `bg`, `fg`, `padding` and friends are
|
|
28
|
+
* read for their CSS variables but were then spread too, rendering invalid
|
|
29
|
+
* `<button bg="red">`. Names in `varList` are consumed here, never emitted.
|
|
30
|
+
* 2. **The caller's `style` clobbered the variables.** `<Tag bg="red" style="margin:0">`
|
|
31
|
+
* dropped `--tag-bg` entirely, because the spread's `style` overwrote the
|
|
32
|
+
* component's. Both are concatenated instead, caller last so their
|
|
33
|
+
* declarations win without erasing the variables.
|
|
34
|
+
* 3. **Custom properties leaked as attributes.** A spread `{"--tag-bg": "red"}`
|
|
35
|
+
* rendered a literal `--tag-bg="red"` attribute. They become declarations.
|
|
36
|
+
*
|
|
37
|
+
* `class` is deliberately NOT handled here. Merging it in the template as
|
|
38
|
+
* `class={["tag", className]}` keeps the component's own class literal in the
|
|
39
|
+
* markup, which is what lets the compiler's CSS pruning see that `.tag` is used.
|
|
40
|
+
*
|
|
41
|
+
* @param props - the component's rest props. Pass style props that were
|
|
42
|
+
* destructured out back in explicitly (`{ bg, fg, ...restProps }`) or their
|
|
43
|
+
* variables will not be emitted.
|
|
44
|
+
* @param prefix - CSS variable prefix, e.g. `"tag"` for `--tag-bg`
|
|
45
|
+
* @param varList - prop names to convert into `--prefix-name` declarations
|
|
46
|
+
* @param extraVars - variables the component computes itself, e.g.
|
|
47
|
+
* `--slider-percent`. Placed after the prop variables and before the
|
|
48
|
+
* caller's `style`, so a caller can still override it.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* const el = $derived(elementProps(restProps, "tag", ["bg", "fg", "padding"]));
|
|
52
|
+
* // <span class={["tag", className]} class:primary {...el}>
|
|
53
|
+
*/
|
|
54
|
+
export declare function elementProps(props: Record<string, any>, prefix: string, varList?: readonly string[], extraVars?: string): Record<string, unknown>;
|
|
55
|
+
/**
|
|
56
|
+
* Build only the CSS variable string, for a component that has no element to
|
|
57
|
+
* spread attributes onto (Dialog, which portals its content and copies
|
|
58
|
+
* variables across).
|
|
59
|
+
*/
|
|
60
|
+
export declare function injectVars(props: Record<string, any>, prefix: string, varList: readonly string[]): string;
|
|
4
61
|
export declare function copyCSSVariables(origin: HTMLElement, target: HTMLElement, variableList?: string[]): void;
|
package/dist/util.js
CHANGED
|
@@ -1,19 +1,109 @@
|
|
|
1
1
|
import { cssProperties } from "./cssprops";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Build the `--prefix-name` CSS variable declarations for a component.
|
|
4
|
+
*
|
|
5
|
+
* Kept separate from {@link elementProps} only so a component that composes its
|
|
6
|
+
* style string by hand (Dialog, which also copies variables across a portal)
|
|
7
|
+
* can reuse the naming rules. Prefer `elementProps` everywhere else.
|
|
8
|
+
*/
|
|
9
|
+
function buildVars(props, prefix, varList) {
|
|
3
10
|
let cssVars = "";
|
|
4
|
-
varList
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
for (const name of varList) {
|
|
12
|
+
const value = props[name];
|
|
13
|
+
// `0` is a meaningful length, so it is NOT skipped -- the old `if (value)`
|
|
14
|
+
// test dropped it. `false` still is: Form and Fieldset type `border` as
|
|
15
|
+
// `string | boolean`, where `false` means "no border", not the declaration
|
|
16
|
+
// `--form-border: false`.
|
|
17
|
+
if (value == null || value === "" || value === false)
|
|
18
|
+
continue;
|
|
19
|
+
cssVars += `--${prefix}-${toKebabCase(name)}: ${value};`;
|
|
20
|
+
}
|
|
21
|
+
for (const key in props) {
|
|
22
|
+
// Custom properties arriving via an object spread -- see CssVarProps.
|
|
23
|
+
if (key.startsWith("--"))
|
|
24
|
+
cssVars += `${key}: ${props[key]};`;
|
|
14
25
|
}
|
|
15
26
|
return cssVars;
|
|
16
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Split a component's rest props into a `style` string and the attributes that
|
|
30
|
+
* belong on an element, keeping the two separate.
|
|
31
|
+
*
|
|
32
|
+
* Use this for a component that renders a wrapper around a control (Toggle,
|
|
33
|
+
* RadioButton, Checkbox, FormItem): the CSS variables and the caller's `style`
|
|
34
|
+
* belong on the wrapper, because that is what the component's own CSS targets
|
|
35
|
+
* and what a caller expects to position, while `aria-*`, `name`, `required`
|
|
36
|
+
* and `data-*` belong on the control itself. {@link elementProps} is the
|
|
37
|
+
* single-element case and is what most components want.
|
|
38
|
+
*
|
|
39
|
+
* @returns `style` for the wrapper, `attrs` (never containing `style`) for the
|
|
40
|
+
* control.
|
|
41
|
+
*/
|
|
42
|
+
export function splitProps(props, prefix, varList = [], extraVars = "") {
|
|
43
|
+
const attrs = {};
|
|
44
|
+
for (const key in props) {
|
|
45
|
+
if (key.startsWith("--"))
|
|
46
|
+
continue; // becomes a declaration, not an attribute
|
|
47
|
+
if (key === "style")
|
|
48
|
+
continue; // returned separately
|
|
49
|
+
if (varList.includes(key))
|
|
50
|
+
continue; // a style prop, not an attribute
|
|
51
|
+
attrs[key] = props[key];
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
style: buildVars(props, prefix, varList) + extraVars + (props.style ?? ""),
|
|
55
|
+
attrs,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Split a component's rest props into the attributes that belong on its root
|
|
60
|
+
* element, with the component's CSS variables and the caller's own `style`
|
|
61
|
+
* merged into a single `style` attribute.
|
|
62
|
+
*
|
|
63
|
+
* This is the one place the library's attribute pass-through contract lives.
|
|
64
|
+
* It exists because spreading raw rest props gets three things wrong at once:
|
|
65
|
+
*
|
|
66
|
+
* 1. **Style props leaked as attributes.** `bg`, `fg`, `padding` and friends are
|
|
67
|
+
* read for their CSS variables but were then spread too, rendering invalid
|
|
68
|
+
* `<button bg="red">`. Names in `varList` are consumed here, never emitted.
|
|
69
|
+
* 2. **The caller's `style` clobbered the variables.** `<Tag bg="red" style="margin:0">`
|
|
70
|
+
* dropped `--tag-bg` entirely, because the spread's `style` overwrote the
|
|
71
|
+
* component's. Both are concatenated instead, caller last so their
|
|
72
|
+
* declarations win without erasing the variables.
|
|
73
|
+
* 3. **Custom properties leaked as attributes.** A spread `{"--tag-bg": "red"}`
|
|
74
|
+
* rendered a literal `--tag-bg="red"` attribute. They become declarations.
|
|
75
|
+
*
|
|
76
|
+
* `class` is deliberately NOT handled here. Merging it in the template as
|
|
77
|
+
* `class={["tag", className]}` keeps the component's own class literal in the
|
|
78
|
+
* markup, which is what lets the compiler's CSS pruning see that `.tag` is used.
|
|
79
|
+
*
|
|
80
|
+
* @param props - the component's rest props. Pass style props that were
|
|
81
|
+
* destructured out back in explicitly (`{ bg, fg, ...restProps }`) or their
|
|
82
|
+
* variables will not be emitted.
|
|
83
|
+
* @param prefix - CSS variable prefix, e.g. `"tag"` for `--tag-bg`
|
|
84
|
+
* @param varList - prop names to convert into `--prefix-name` declarations
|
|
85
|
+
* @param extraVars - variables the component computes itself, e.g.
|
|
86
|
+
* `--slider-percent`. Placed after the prop variables and before the
|
|
87
|
+
* caller's `style`, so a caller can still override it.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* const el = $derived(elementProps(restProps, "tag", ["bg", "fg", "padding"]));
|
|
91
|
+
* // <span class={["tag", className]} class:primary {...el}>
|
|
92
|
+
*/
|
|
93
|
+
export function elementProps(props, prefix, varList = [], extraVars = "") {
|
|
94
|
+
const { style, attrs } = splitProps(props, prefix, varList, extraVars);
|
|
95
|
+
if (style)
|
|
96
|
+
attrs.style = style;
|
|
97
|
+
return attrs;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Build only the CSS variable string, for a component that has no element to
|
|
101
|
+
* spread attributes onto (Dialog, which portals its content and copies
|
|
102
|
+
* variables across).
|
|
103
|
+
*/
|
|
104
|
+
export function injectVars(props, prefix, varList) {
|
|
105
|
+
return buildVars(props, prefix, varList);
|
|
106
|
+
}
|
|
17
107
|
function toKebabCase(str) {
|
|
18
108
|
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
19
109
|
}
|