bits-ui 1.0.0-next.5 → 1.0.0-next.7

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.
@@ -3,7 +3,7 @@
3
3
  import type { ActionProps } from "../index.js";
4
4
  import { mergeProps } from "../../../internal/mergeProps.js";
5
5
  import { useId } from "../../../internal/useId.js";
6
- import { useDialogClose } from "../../dialog/dialog.svelte.js";
6
+ import { useAlertDialogAction } from "../../dialog/dialog.svelte.js";
7
7
 
8
8
  let {
9
9
  children,
@@ -13,8 +13,7 @@
13
13
  ...restProps
14
14
  }: ActionProps = $props();
15
15
 
16
- const closeState = useDialogClose({
17
- variant: box.with(() => "action"),
16
+ const actionState = useAlertDialogAction({
18
17
  id: box.with(() => id),
19
18
  ref: box.with(
20
19
  () => ref,
@@ -22,7 +21,7 @@
22
21
  ),
23
22
  });
24
23
 
25
- const mergedProps = $derived(mergeProps(restProps, closeState.props));
24
+ const mergedProps = $derived(mergeProps(restProps, actionState.props));
26
25
  </script>
27
26
 
28
27
  {#if child}
@@ -1,7 +1,10 @@
1
1
  import type { ReadableBox, WritableBox } from "svelte-toolbelt";
2
+ import type { HTMLImgAttributes } from "svelte/elements";
2
3
  import type { AvatarImageLoadingStatus } from "./types.js";
3
4
  import type { ReadableBoxedValues } from "../../internal/box.svelte.js";
4
5
  import type { WithRefProps } from "../../internal/types.js";
6
+ type CrossOrigin = HTMLImgAttributes["crossorigin"];
7
+ type ReferrerPolicy = HTMLImgAttributes["referrerpolicy"];
5
8
  /**
6
9
  * ROOT
7
10
  */
@@ -15,7 +18,7 @@ declare class AvatarRootState {
15
18
  delayMs: AvatarRootStateProps["delayMs"];
16
19
  loadingStatus: AvatarRootStateProps["loadingStatus"];
17
20
  constructor(props: AvatarRootStateProps);
18
- loadImage(src: string): () => void;
21
+ loadImage(src: string, crossorigin?: CrossOrigin, referrerPolicy?: ReferrerPolicy): () => void;
19
22
  props: {
20
23
  readonly id: string;
21
24
  readonly "data-avatar-root": "";
@@ -29,11 +32,11 @@ declare class AvatarRootState {
29
32
  */
30
33
  type AvatarImageStateProps = WithRefProps<ReadableBoxedValues<{
31
34
  src: AvatarImageSrc;
35
+ crossOrigin: CrossOrigin;
36
+ referrerPolicy: ReferrerPolicy;
32
37
  }>>;
33
38
  declare class AvatarImageState {
34
39
  #private;
35
- src: AvatarImageStateProps["src"];
36
- root: AvatarRootState;
37
40
  constructor(props: AvatarImageStateProps, root: AvatarRootState);
38
41
  props: {
39
42
  readonly id: string;
@@ -43,6 +46,8 @@ declare class AvatarImageState {
43
46
  readonly "data-status": AvatarImageLoadingStatus;
44
47
  readonly "data-avatar-image": "";
45
48
  readonly src: AvatarImageSrc;
49
+ readonly crossorigin: "" | "anonymous" | "use-credentials" | null | undefined;
50
+ readonly referrerpolicy: globalThis.ReferrerPolicy | null | undefined;
46
51
  };
47
52
  }
48
53
  /**
@@ -51,7 +56,6 @@ declare class AvatarImageState {
51
56
  type AvatarFallbackStateProps = WithRefProps;
52
57
  declare class AvatarFallbackState {
53
58
  #private;
54
- root: AvatarRootState;
55
59
  constructor(props: AvatarFallbackStateProps, root: AvatarRootState);
56
60
  props: {
57
61
  readonly style: {
@@ -19,13 +19,17 @@ class AvatarRootState {
19
19
  ref: this.#ref,
20
20
  });
21
21
  }
22
- loadImage(src) {
22
+ loadImage(src, crossorigin, referrerPolicy) {
23
23
  let imageTimerId;
24
24
  const image = new Image();
25
25
  image.src = src;
26
+ if (crossorigin)
27
+ image.crossOrigin = crossorigin;
28
+ if (referrerPolicy)
29
+ image.referrerPolicy = referrerPolicy;
26
30
  this.loadingStatus.current = "loading";
27
31
  image.onload = () => {
28
- imageTimerId = setTimeout(() => {
32
+ imageTimerId = window.setTimeout(() => {
29
33
  this.loadingStatus.current = "loaded";
30
34
  }, this.delayMs.current);
31
35
  };
@@ -51,39 +55,47 @@ class AvatarRootState {
51
55
  class AvatarImageState {
52
56
  #id;
53
57
  #ref;
54
- src;
55
- root;
58
+ #crossOrigin;
59
+ #referrerPolicy;
60
+ #src;
61
+ #root;
56
62
  constructor(props, root) {
57
- this.root = root;
58
- this.src = props.src;
63
+ this.#root = root;
64
+ this.#src = props.src;
59
65
  this.#id = props.id;
60
66
  this.#ref = props.ref;
67
+ this.#crossOrigin = props.crossOrigin;
68
+ this.#referrerPolicy = props.referrerPolicy;
61
69
  useRefById({
62
70
  id: this.#id,
63
71
  ref: this.#ref,
64
72
  });
65
73
  $effect.pre(() => {
66
- if (!this.src.current)
74
+ if (!this.#src.current)
67
75
  return;
68
- untrack(() => this.root.loadImage(this.src.current ?? ""));
76
+ // dependency on crossorigin
77
+ this.#crossOrigin.current;
78
+ untrack(() => this.#root.loadImage(this.#src.current ?? "", this.#crossOrigin.current, this.#referrerPolicy.current));
69
79
  });
70
80
  }
71
81
  props = $derived.by(() => ({
72
82
  id: this.#id.current,
73
83
  style: {
74
- display: this.root.loadingStatus.current === "loaded" ? "block" : "none",
84
+ display: this.#root.loadingStatus.current === "loaded" ? "block" : "none",
75
85
  },
76
- "data-status": this.root.loadingStatus.current,
86
+ "data-status": this.#root.loadingStatus.current,
77
87
  [AVATAR_IMAGE_ATTR]: "",
78
- src: this.src.current,
88
+ src: this.#src.current,
89
+ crossorigin: this.#crossOrigin.current,
90
+ referrerpolicy: this.#referrerPolicy.current,
79
91
  }));
80
92
  }
81
93
  class AvatarFallbackState {
82
94
  #id;
83
95
  #ref;
84
- root;
96
+ #root;
85
97
  constructor(props, root) {
86
- this.root = root;
98
+ this.#root = root;
87
99
  this.#id = props.id;
88
100
  this.#ref = props.ref;
89
101
  useRefById({
@@ -93,9 +105,9 @@ class AvatarFallbackState {
93
105
  }
94
106
  props = $derived.by(() => ({
95
107
  style: {
96
- display: this.root.loadingStatus.current === "loaded" ? "none" : undefined,
108
+ display: this.#root.loadingStatus.current === "loaded" ? "none" : undefined,
97
109
  },
98
- "data-status": this.root.loadingStatus.current,
110
+ "data-status": this.#root.loadingStatus.current,
99
111
  [AVATAR_FALLBACK_ATTR]: "",
100
112
  }));
101
113
  }
@@ -5,7 +5,15 @@
5
5
  import { mergeProps } from "../../../internal/mergeProps.js";
6
6
  import { useId } from "../../../internal/useId.js";
7
7
 
8
- let { src, child, id = useId(), ref = $bindable(null), ...restProps }: ImageProps = $props();
8
+ let {
9
+ src,
10
+ child,
11
+ id = useId(),
12
+ ref = $bindable(null),
13
+ crossorigin = "",
14
+ referrerpolicy = undefined,
15
+ ...restProps
16
+ }: ImageProps = $props();
9
17
 
10
18
  const imageState = useAvatarImage({
11
19
  src: box.with(() => src),
@@ -14,6 +22,8 @@
14
22
  () => ref,
15
23
  (v) => (ref = v)
16
24
  ),
25
+ crossOrigin: box.with(() => crossorigin),
26
+ referrerPolicy: box.with(() => referrerpolicy),
17
27
  });
18
28
 
19
29
  const mergedProps = $derived(mergeProps(restProps, imageState.props));
@@ -346,10 +346,10 @@ export declare function useCalendarDay(props: CalendarDayStateProps): {
346
346
  id: import("svelte-toolbelt").ReadableBox<string>;
347
347
  ref: import("svelte-toolbelt").WritableBox<HTMLElement | null>;
348
348
  readonly cell: RangeCalendarCellState;
349
- "__#62@#tabindex": number | undefined;
350
- "__#62@#onclick": (e: MouseEvent) => void;
351
- "__#62@#onmouseenter": () => void;
352
- "__#62@#onfocusin": () => void;
349
+ "__#63@#tabindex": number | undefined;
350
+ "__#63@#onclick": (e: MouseEvent) => void;
351
+ "__#63@#onmouseenter": () => void;
352
+ "__#63@#onfocusin": () => void;
353
353
  snippetProps: {
354
354
  disabled: boolean;
355
355
  unavailable: boolean;
@@ -115,11 +115,11 @@ declare const getDateRangeFieldRootContext: <T extends DateRangeFieldRootState |
115
115
  export declare function useDateRangeFieldRoot(props: DateRangeFieldRootStateProps): DateRangeFieldRootState;
116
116
  export declare function useDateRangeFieldLabel(props: DateRangeFieldLabelStateProps): DateRangeFieldLabelState;
117
117
  export declare function useDateRangeFieldInput(props: Omit<DateRangeFieldInputStateProps, "value">, type: "start" | "end"): {
118
- "__#98@#id": import("svelte-toolbelt").ReadableBox<string>;
119
- "__#98@#ref": WritableBox<HTMLElement | null>;
120
- "__#98@#name": import("svelte-toolbelt").ReadableBox<string>;
118
+ "__#99@#id": import("svelte-toolbelt").ReadableBox<string>;
119
+ "__#99@#ref": WritableBox<HTMLElement | null>;
120
+ "__#99@#name": import("svelte-toolbelt").ReadableBox<string>;
121
121
  root: DateFieldRootState;
122
- "__#98@#ariaDescribedBy": string | undefined;
122
+ "__#99@#ariaDescribedBy": string | undefined;
123
123
  props: {
124
124
  readonly id: string;
125
125
  readonly role: "group";
@@ -38,6 +38,7 @@ declare class DialogRootState {
38
38
  createDescription(props: DialogDescriptionStateProps): DialogDescriptionState;
39
39
  createClose(props: DialogCloseStateProps): DialogCloseState;
40
40
  createCancel(props: AlertDialogCancelStateProps): AlertDialogCancelState;
41
+ createAction(props: DialogActionStateProps): DialogActionState;
41
42
  sharedProps: {
42
43
  readonly "data-state": "open" | "closed";
43
44
  };
@@ -67,6 +68,15 @@ declare class DialogCloseState {
67
68
  readonly onclick: () => void;
68
69
  };
69
70
  }
71
+ type DialogActionStateProps = WithRefProps;
72
+ declare class DialogActionState {
73
+ #private;
74
+ constructor(props: DialogActionStateProps, root: DialogRootState);
75
+ props: {
76
+ readonly "data-state": "open" | "closed";
77
+ readonly id: string;
78
+ };
79
+ }
70
80
  type DialogTitleStateProps = WithRefProps<ReadableBoxedValues<{
71
81
  level: 1 | 2 | 3 | 4 | 5 | 6;
72
82
  }>>;
@@ -136,4 +146,5 @@ export declare function useDialogOverlay(props: DialogOverlayStateProps): Dialog
136
146
  export declare function useDialogDescription(props: DialogDescriptionStateProps): DialogDescriptionState;
137
147
  export declare function useDialogClose(props: DialogCloseStateProps): DialogCloseState;
138
148
  export declare function useAlertDialogCancel(props: AlertDialogCancelStateProps): AlertDialogCancelState;
149
+ export declare function useAlertDialogAction(props: DialogActionStateProps): DialogActionState;
139
150
  export {};
@@ -61,6 +61,9 @@ class DialogRootState {
61
61
  createCancel(props) {
62
62
  return new AlertDialogCancelState(props, this);
63
63
  }
64
+ createAction(props) {
65
+ return new DialogActionState(props, this);
66
+ }
64
67
  sharedProps = $derived.by(() => ({
65
68
  "data-state": getDataOpenClosed(this.open.current),
66
69
  }));
@@ -122,6 +125,26 @@ class DialogCloseState {
122
125
  ...this.#root.sharedProps,
123
126
  }));
124
127
  }
128
+ class DialogActionState {
129
+ #id;
130
+ #ref;
131
+ #root;
132
+ #attr = $derived.by(() => this.#root.attrs.action);
133
+ constructor(props, root) {
134
+ this.#id = props.id;
135
+ this.#ref = props.ref;
136
+ this.#root = root;
137
+ useRefById({
138
+ id: this.#id,
139
+ ref: this.#ref,
140
+ });
141
+ }
142
+ props = $derived.by(() => ({
143
+ id: this.#id.current,
144
+ [this.#attr]: "",
145
+ ...this.#root.sharedProps,
146
+ }));
147
+ }
125
148
  class DialogTitleState {
126
149
  #id;
127
150
  #ref;
@@ -275,3 +298,6 @@ export function useDialogClose(props) {
275
298
  export function useAlertDialogCancel(props) {
276
299
  return getDialogRootContext().createCancel(props);
277
300
  }
301
+ export function useAlertDialogAction(props) {
302
+ return getDialogRootContext().createAction(props);
303
+ }
@@ -36,3 +36,4 @@ export * as Toggle from "./toggle/index.js";
36
36
  export * as ToggleGroup from "./toggle-group/index.js";
37
37
  export * as Toolbar from "./toolbar/index.js";
38
38
  export * as Tooltip from "./tooltip/index.js";
39
+ export { default as Portal } from "./utilities/portal/portal.svelte";
@@ -36,3 +36,4 @@ export * as Toggle from "./toggle/index.js";
36
36
  export * as ToggleGroup from "./toggle-group/index.js";
37
37
  export * as Toolbar from "./toolbar/index.js";
38
38
  export * as Tooltip from "./tooltip/index.js";
39
+ export { default as Portal } from "./utilities/portal/portal.svelte";
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { getAllContexts, mount, unmount, untrack } from "svelte";
3
+ import { DEV } from "esm-env";
3
4
  import PortalConsumer from "./portal-consumer.svelte";
4
5
  import type { PortalProps } from "./types.js";
5
6
  import { isBrowser } from "../../../internal/is.js";
@@ -16,16 +17,20 @@
16
17
  if (typeof to === "string") {
17
18
  localTarget = document.querySelector(to);
18
19
  if (localTarget === null) {
19
- throw new Error(`Target element "${to}" not found.`);
20
+ if (DEV) {
21
+ throw new Error(`Target element "${to}" not found.`);
22
+ }
20
23
  }
21
24
  } else if (to instanceof HTMLElement || to instanceof DocumentFragment) {
22
25
  localTarget = to;
23
26
  } else {
24
- throw new TypeError(
25
- `Unknown portal target type: ${
26
- to === null ? "null" : typeof to
27
- }. Allowed types: string (CSS selector) or HTMLElement.`
28
- );
27
+ if (DEV) {
28
+ throw new TypeError(
29
+ `Unknown portal target type: ${
30
+ to === null ? "null" : typeof to
31
+ }. Allowed types: string (query selector), HTMLElement, or DocumentFragment.`
32
+ );
33
+ }
29
34
  }
30
35
 
31
36
  return localTarget;
@@ -7,10 +7,13 @@ export type PortalProps = {
7
7
  */
8
8
  to?: HTMLElement | string | DocumentFragment;
9
9
  /**
10
- * Disable portaling and render the component inline
10
+ * Disable portalling and render the component inline
11
11
  *
12
12
  * @defaultValue false
13
13
  */
14
14
  disabled?: boolean;
15
+ /**
16
+ * The children content to render within the portal.
17
+ */
15
18
  children?: Snippet;
16
19
  };
@@ -1,5 +1,5 @@
1
1
  import parse from "style-to-object";
2
- import { camelCase, pascalCase } from "scule";
2
+ import { camelCase, pascalCase } from "./strings.js";
3
3
  export function cssToStyleObj(css) {
4
4
  if (!css)
5
5
  return {};
@@ -0,0 +1,3 @@
1
+ export declare function pascalCase(str?: string): string;
2
+ export declare function camelCase(str?: string): string;
3
+ export declare function kebabCase(str?: string): string;
@@ -0,0 +1,70 @@
1
+ const NUMBER_CHAR_RE = /\d/;
2
+ const STR_SPLITTERS = ["-", "_", "/", "."];
3
+ function isUppercase(char = "") {
4
+ if (NUMBER_CHAR_RE.test(char))
5
+ return undefined;
6
+ return char !== char.toLowerCase();
7
+ }
8
+ function splitByCase(str) {
9
+ const parts = [];
10
+ let buff = "";
11
+ let previousUpper;
12
+ let previousSplitter;
13
+ for (const char of str) {
14
+ // Splitter
15
+ const isSplitter = STR_SPLITTERS.includes(char);
16
+ if (isSplitter === true) {
17
+ parts.push(buff);
18
+ buff = "";
19
+ previousUpper = undefined;
20
+ continue;
21
+ }
22
+ const isUpper = isUppercase(char);
23
+ if (previousSplitter === false) {
24
+ // Case rising edge
25
+ if (previousUpper === false && isUpper === true) {
26
+ parts.push(buff);
27
+ buff = char;
28
+ previousUpper = isUpper;
29
+ continue;
30
+ }
31
+ // Case falling edge
32
+ if (previousUpper === true && isUpper === false && buff.length > 1) {
33
+ const lastChar = buff.at(-1);
34
+ parts.push(buff.slice(0, Math.max(0, buff.length - 1)));
35
+ buff = lastChar + char;
36
+ previousUpper = isUpper;
37
+ continue;
38
+ }
39
+ }
40
+ // Normal char
41
+ buff += char;
42
+ previousUpper = isUpper;
43
+ previousSplitter = isSplitter;
44
+ }
45
+ parts.push(buff);
46
+ return parts;
47
+ }
48
+ export function pascalCase(str) {
49
+ if (!str)
50
+ return "";
51
+ return splitByCase(str)
52
+ .map((p) => upperFirst(p))
53
+ .join("");
54
+ }
55
+ export function camelCase(str) {
56
+ return lowerFirst(pascalCase(str || ""));
57
+ }
58
+ export function kebabCase(str) {
59
+ return str
60
+ ? splitByCase(str)
61
+ .map((p) => p.toLowerCase())
62
+ .join("-")
63
+ : "";
64
+ }
65
+ function upperFirst(str) {
66
+ return str ? str[0].toUpperCase() + str.slice(1) : "";
67
+ }
68
+ function lowerFirst(str) {
69
+ return str ? str[0].toLowerCase() + str.slice(1) : "";
70
+ }
@@ -1,4 +1,4 @@
1
- import styleToCSS from "style-object-to-css-string";
1
+ import { styleToCSS } from "./styleToCSS.js";
2
2
  export function styleToString(style = {}) {
3
3
  return styleToCSS(style).replace("\n", " ");
4
4
  }
@@ -0,0 +1 @@
1
+ export declare function styleToCSS(styleObj: object): string;
@@ -0,0 +1,23 @@
1
+ function createParser(matcher, replacer) {
2
+ const regex = RegExp(matcher, "g");
3
+ return (str) => {
4
+ // throw an error if not a string
5
+ if (typeof str !== "string") {
6
+ throw new TypeError(`expected an argument of type string, but got ${typeof str}`);
7
+ }
8
+ // if no match between string and matcher
9
+ if (!str.match(regex))
10
+ return str;
11
+ // executes the replacer function for each match
12
+ return str.replace(regex, replacer);
13
+ };
14
+ }
15
+ const camelToKebab = createParser(/[A-Z]/, (match) => `-${match.toLowerCase()}`);
16
+ export function styleToCSS(styleObj) {
17
+ if (!styleObj || typeof styleObj !== "object" || Array.isArray(styleObj)) {
18
+ throw new TypeError(`expected an argument of type object, but got ${typeof styleObj}`);
19
+ }
20
+ return Object.keys(styleObj)
21
+ .map((property) => `${camelToKebab(property)}: ${styleObj[property]};`)
22
+ .join("\n");
23
+ }
package/dist/types.d.ts CHANGED
@@ -36,3 +36,4 @@ export type * from "./bits/toggle/types.js";
36
36
  export type * from "./bits/toggle-group/types.js";
37
37
  export type * from "./bits/toolbar/types.js";
38
38
  export type * from "./bits/tooltip/types.js";
39
+ export type { PortalProps } from "./bits/utilities/portal/types.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bits-ui",
3
- "version": "1.0.0-next.5",
3
+ "version": "1.0.0-next.7",
4
4
  "license": "MIT",
5
5
  "repository": "github:huntabyte/bits-ui",
6
6
  "funding": "https://github.com/sponsors/huntabyte",
@@ -51,10 +51,7 @@
51
51
  "@internationalized/date": "^3.5.4",
52
52
  "clsx": "^2.1.1",
53
53
  "esm-env": "^1.0.0",
54
- "nanoid": "^5.0.7",
55
54
  "runed": "^0.15.2",
56
- "scule": "^1.3.0",
57
- "style-object-to-css-string": "^1.1.3",
58
55
  "style-to-object": "^1.0.6",
59
56
  "svelte-toolbelt": "^0.3.1"
60
57
  },
@@ -1,10 +0,0 @@
1
- declare module "style-object-to-css-string" {
2
- export default objToString;
3
- /**
4
- * Translate a style object into a CSS string
5
- * @param {object} styleObj - The object to run a particular parser against
6
- * @returns {string} - The CSS string
7
- */
8
- // eslint-disable-next-line ts/no-explicit-any
9
- declare function objToString(styleObj: any): string;
10
- }