@blockle/blocks-react 3.0.1 → 3.1.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.
Files changed (66) hide show
  1. package/README.md +138 -0
  2. package/dist/components/feedback/Alert/Alert.d.ts +6 -0
  3. package/dist/components/feedback/Alert/Alert.d.ts.map +1 -1
  4. package/dist/components/feedback/Alert/Alert.js +2 -0
  5. package/dist/components/feedback/Toast/Toast.css.d.ts +10 -1
  6. package/dist/components/feedback/Toast/Toast.css.d.ts.map +1 -1
  7. package/dist/components/feedback/Toast/Toast.css.js +37 -10
  8. package/dist/components/feedback/Toast/ToastProvider.d.ts +9 -1
  9. package/dist/components/feedback/Toast/ToastProvider.d.ts.map +1 -1
  10. package/dist/components/feedback/Toast/ToastProvider.js +47 -24
  11. package/dist/components/form/Checkbox/Checkbox.d.ts +0 -1
  12. package/dist/components/form/Checkbox/Checkbox.d.ts.map +1 -1
  13. package/dist/components/form/Checkbox/Checkbox.js +0 -2
  14. package/dist/components/form/Checkbox/checkbox.css.d.ts.map +1 -1
  15. package/dist/components/form/Checkbox/checkbox.css.js +1 -0
  16. package/dist/components/form/Radio/Radio.d.ts +0 -1
  17. package/dist/components/form/Radio/Radio.d.ts.map +1 -1
  18. package/dist/components/form/Radio/Radio.js +1 -11
  19. package/dist/components/form/Radio/radio.css.d.ts.map +1 -1
  20. package/dist/components/form/Radio/radio.css.js +1 -0
  21. package/dist/components/form/Select/Select.d.ts +2 -1
  22. package/dist/components/form/Select/Select.d.ts.map +1 -1
  23. package/dist/components/form/Select/Select.js +2 -1
  24. package/dist/components/form/Switch/Switch.d.ts +5 -1
  25. package/dist/components/form/Switch/Switch.d.ts.map +1 -1
  26. package/dist/components/form/Switch/Switch.js +19 -29
  27. package/dist/components/form/Switch/switch.css.d.ts.map +1 -1
  28. package/dist/components/form/Switch/switch.css.js +1 -0
  29. package/dist/components/form/TextInput/TextInput.d.ts +0 -1
  30. package/dist/components/form/TextInput/TextInput.d.ts.map +1 -1
  31. package/dist/components/form/TextInput/TextInput.js +0 -2
  32. package/dist/components/overlay/Dialog/Dialog.d.ts +4 -1
  33. package/dist/components/overlay/Dialog/Dialog.d.ts.map +1 -1
  34. package/dist/components/overlay/Dialog/Dialog.js +19 -46
  35. package/dist/components/overlay/Drawer/Drawer.d.ts +9 -2
  36. package/dist/components/overlay/Drawer/Drawer.d.ts.map +1 -1
  37. package/dist/components/overlay/Drawer/Drawer.js +25 -44
  38. package/dist/components/overlay/Drawer/drawer.css.d.ts.map +1 -1
  39. package/dist/components/overlay/Drawer/drawer.css.js +4 -8
  40. package/dist/components/overlay/Popover/Popover.d.ts +15 -3
  41. package/dist/components/overlay/Popover/Popover.d.ts.map +1 -1
  42. package/dist/components/overlay/Popover/Popover.js +35 -11
  43. package/dist/components/overlay/Popover/popover-utils.d.ts +38 -1
  44. package/dist/components/overlay/Popover/popover-utils.d.ts.map +1 -1
  45. package/dist/components/overlay/Popover/popover-utils.js +92 -41
  46. package/dist/components/overlay/Tooltip/Tooltip.d.ts +8 -0
  47. package/dist/components/overlay/Tooltip/Tooltip.d.ts.map +1 -1
  48. package/dist/components/overlay/Tooltip/Tooltip.js +3 -2
  49. package/dist/components/overlay/requestCloseReason.d.ts +6 -0
  50. package/dist/components/overlay/requestCloseReason.d.ts.map +1 -0
  51. package/dist/components/overlay/useModalOverlay.d.ts +35 -0
  52. package/dist/components/overlay/useModalOverlay.d.ts.map +1 -0
  53. package/dist/components/overlay/useModalOverlay.js +85 -0
  54. package/dist/hooks/useClickOutside/useClickOutside.d.ts.map +1 -1
  55. package/dist/hooks/useClickOutside/useClickOutside.js +27 -7
  56. package/dist/hooks/useModalDialog/useModalDialog.d.ts +13 -0
  57. package/dist/hooks/useModalDialog/useModalDialog.d.ts.map +1 -0
  58. package/dist/hooks/useModalDialog/useModalDialog.js +39 -0
  59. package/dist/index.d.ts +3 -2
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/testUtils/browser/pointer.d.ts +18 -0
  62. package/dist/testUtils/browser/pointer.d.ts.map +1 -0
  63. package/package.json +2 -2
  64. package/dist/components/overlay/Dialog/dialogHelper.d.ts +0 -7
  65. package/dist/components/overlay/Dialog/dialogHelper.d.ts.map +0 -1
  66. package/dist/components/overlay/Dialog/dialogHelper.js +0 -22
package/README.md CHANGED
@@ -1 +1,139 @@
1
1
  # @blockle/blocks-react
2
+
3
+ ## Testing
4
+
5
+ `Dialog` and `Drawer` open with `HTMLDialogElement.showModal()` and close with `close()`. jsdom does not implement these, so in a jsdom test an open `Dialog` stays a closed `<dialog>`, and Testing Library cannot find it, or anything in it, by role.
6
+
7
+ Add this polyfill to your Vitest or Jest setup file. It only defines the methods jsdom is missing:
8
+
9
+ ```ts
10
+ const DialogPrototype = HTMLDialogElement.prototype;
11
+
12
+ if (!DialogPrototype.showModal) {
13
+ DialogPrototype.showModal = function showModal() {
14
+ this.setAttribute('open', '');
15
+ };
16
+ }
17
+
18
+ if (!DialogPrototype.show) {
19
+ DialogPrototype.show = function show() {
20
+ this.setAttribute('open', '');
21
+ };
22
+ }
23
+
24
+ if (!DialogPrototype.close) {
25
+ DialogPrototype.close = function close() {
26
+ if (!this.hasAttribute('open')) {
27
+ return;
28
+ }
29
+
30
+ this.removeAttribute('open');
31
+ this.dispatchEvent(new Event('close'));
32
+ };
33
+ }
34
+ ```
35
+
36
+ With it, an open dialog can be found with `getByRole('dialog')`:
37
+
38
+ ```tsx
39
+ render(
40
+ <Dialog open onRequestClose={() => {}} aria-label="Settings">
41
+ <button type="button">Save</button>
42
+ </Dialog>,
43
+ );
44
+
45
+ screen.getByRole('dialog', { name: 'Settings' });
46
+ screen.getByRole('button', { name: 'Save' });
47
+ ```
48
+
49
+ Under the polyfill the dialog is open but not modal: it is not in the top layer, and nothing outside it is made inert, so content behind it can still be queried and clicked. Test modal behaviour, focus trapping and clicks outside in a real browser, for example with Vitest Browser Mode or Playwright.
50
+
51
+ ### Popover
52
+
53
+ `Popover` opens with `showPopover()` and closes with `hidePopover()`. jsdom does not implement the Popover API, and its default stylesheet hides every `[popover]` element that is not open, so Testing Library treats an open popover's content as hidden and `getByRole` finds nothing in it.
54
+
55
+ Add this polyfill to your test setup. It only defines the methods jsdom is missing, and marks an open popover with a `data-popover-open` attribute that a test stylesheet shows:
56
+
57
+ ```ts
58
+ const PopoverPrototype = HTMLElement.prototype;
59
+
60
+ if (!PopoverPrototype.showPopover) {
61
+ PopoverPrototype.showPopover = function showPopover() {
62
+ this.setAttribute('data-popover-open', '');
63
+ };
64
+ }
65
+
66
+ if (!PopoverPrototype.hidePopover) {
67
+ PopoverPrototype.hidePopover = function hidePopover() {
68
+ this.removeAttribute('data-popover-open');
69
+ };
70
+ }
71
+
72
+ if (!PopoverPrototype.togglePopover) {
73
+ PopoverPrototype.togglePopover = function togglePopover(force?: boolean) {
74
+ return this.toggleAttribute('data-popover-open', force);
75
+ };
76
+ }
77
+
78
+ const popoverStyle = document.createElement('style');
79
+ popoverStyle.textContent = '[popover][data-popover-open] { display: block; }';
80
+ document.head.append(popoverStyle);
81
+ ```
82
+
83
+ With it, an open popover's content can be found by role:
84
+
85
+ ```tsx
86
+ render(
87
+ <Popover open anchorElement={anchorRef} onRequestClose={() => {}}>
88
+ <button type="button">Inside</button>
89
+ </Popover>,
90
+ );
91
+
92
+ screen.getByRole('button', { name: 'Inside' });
93
+ ```
94
+
95
+ Other libraries may check for `showPopover` and behave differently once it exists; dnd-kit, for example, drags differently. If a global polyfill breaks such tests, apply it only inside the `describe` that opens a popover, and remove what it added afterwards:
96
+
97
+ ```ts
98
+ import { afterAll, beforeAll, describe } from 'vitest';
99
+
100
+ describe('with a popover open', () => {
101
+ const polyfills: Record<string, (this: HTMLElement, force?: boolean) => unknown> = {
102
+ showPopover() {
103
+ this.setAttribute('data-popover-open', '');
104
+ },
105
+ hidePopover() {
106
+ this.removeAttribute('data-popover-open');
107
+ },
108
+ togglePopover(force?: boolean) {
109
+ return this.toggleAttribute('data-popover-open', force);
110
+ },
111
+ };
112
+ const added: string[] = [];
113
+ const popoverStyle = document.createElement('style');
114
+
115
+ beforeAll(() => {
116
+ for (const [method, polyfill] of Object.entries(polyfills)) {
117
+ if (!(method in HTMLElement.prototype)) {
118
+ Reflect.set(HTMLElement.prototype, method, polyfill);
119
+ added.push(method);
120
+ }
121
+ }
122
+
123
+ popoverStyle.textContent = '[popover][data-popover-open] { display: block; }';
124
+ document.head.append(popoverStyle);
125
+ });
126
+
127
+ afterAll(() => {
128
+ for (const method of added) {
129
+ Reflect.deleteProperty(HTMLElement.prototype, method);
130
+ }
131
+
132
+ popoverStyle.remove();
133
+ });
134
+
135
+ // Tests that open a popover
136
+ });
137
+ ```
138
+
139
+ Where the Popover API is missing and no polyfill is applied, `Popover` warns when it opens or closes instead of throwing. Its content then stays hidden under jsdom's default styles. Test positioning, the top layer and clicks outside in a real browser.
@@ -4,6 +4,12 @@ export type AlertProps = {
4
4
  children?: React.ReactNode;
5
5
  intent?: AlertTheme['variants']['intent'];
6
6
  onRequestClose?: () => void;
7
+ /**
8
+ * Accessible name of the close button, rendered when `onRequestClose` is set.
9
+ * Pass your own wording to translate it.
10
+ * @default 'Close'
11
+ */
12
+ closeLabel?: string;
7
13
  } & HTMLElementProps<HTMLDivElement>;
8
14
  export declare const Alert: React.FC<AlertProps>;
9
15
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"Alert.d.ts","sourceRoot":"","sources":["../../../../src/components/feedback/Alert/Alert.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAO9B,KAAK,UAAU,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;AAE3C,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC1C,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;AAErC,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,UAAU,CAwDtC,CAAC"}
1
+ {"version":3,"file":"Alert.d.ts","sourceRoot":"","sources":["../../../../src/components/feedback/Alert/Alert.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAO9B,KAAK,UAAU,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;AAE3C,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC1C,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;AAErC,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,UAAU,CA0DtC,CAAC"}
@@ -9,6 +9,7 @@ const Alert = ({
9
9
  intent = "info",
10
10
  children,
11
11
  onRequestClose,
12
+ closeLabel = "Close",
12
13
  className,
13
14
  ...restProps
14
15
  }) => {
@@ -33,6 +34,7 @@ const Alert = ({
33
34
  variant: "ghost",
34
35
  size: "small",
35
36
  className: closeButton,
37
+ "aria-label": closeLabel,
36
38
  onClick: () => onRequestClose?.(),
37
39
  children: /* @__PURE__ */ jsxs(
38
40
  "svg",
@@ -1,2 +1,11 @@
1
- export declare const container: string;
1
+ export declare const stack: string;
2
+ export declare const toast: string;
3
+ export declare const placement: {
4
+ top: string;
5
+ 'top-start': string;
6
+ 'top-end': string;
7
+ bottom: string;
8
+ 'bottom-start': string;
9
+ 'bottom-end': string;
10
+ };
2
11
  //# sourceMappingURL=Toast.css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Toast.css.d.ts","sourceRoot":"","sources":["../../../../src/components/feedback/Toast/Toast.css.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,SAAS,QASpB,CAAC"}
1
+ {"version":3,"file":"Toast.css.d.ts","sourceRoot":"","sources":["../../../../src/components/feedback/Toast/Toast.css.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,KAAK,QAYhB,CAAC;AAEH,eAAO,MAAM,KAAK,QAOhB,CAAC;AAmBH,eAAO,MAAM,SAAS;;;;;;;CAOrB,CAAC"}
@@ -1,22 +1,49 @@
1
1
  import { setFileScope, endFileScope } from "@vanilla-extract/css/fileScope";
2
2
  import { layers } from "@blockle/blocks-core";
3
- import { style, keyframes } from "@vanilla-extract/css";
3
+ import { style } from "@vanilla-extract/css";
4
4
  setFileScope("src/components/feedback/Toast/Toast.css.ts", "@blockle/blocks-react");
5
- const toastEnter = keyframes({
6
- from: { opacity: 0, scale: 0.9, translate: "0 20%" },
7
- to: { opacity: 1, scale: 1, translate: "0 0" }
5
+ const stack = style({
6
+ "@layer": {
7
+ [layers.molecule]: {
8
+ position: "fixed",
9
+ display: "flex",
10
+ flexDirection: "column",
11
+ width: "100%",
12
+ boxSizing: "border-box",
13
+ // Let clicks through the stack's empty space, but not through a toast
14
+ pointerEvents: "none"
15
+ }
16
+ }
8
17
  });
9
- const container = style({
18
+ const toast = style({
10
19
  "@layer": {
11
20
  [layers.molecule]: {
12
- animationName: toastEnter,
13
- animationDuration: "240ms",
14
- animationTimingFunction: "ease-out",
15
- height: "fit-content"
21
+ height: "fit-content",
22
+ pointerEvents: "auto"
16
23
  }
17
24
  }
18
25
  });
26
+ const top = {
27
+ insetBlockStart: 0,
28
+ // Newest toast nearest the top edge
29
+ flexDirection: "column-reverse"
30
+ };
31
+ const bottom = { insetBlockEnd: 0 };
32
+ const center = { insetInline: 0, marginInline: "auto" };
33
+ const start = { insetInlineStart: 0 };
34
+ const end = { insetInlineEnd: 0 };
35
+ const placementStyle = (...rules) => style({ "@layer": { [layers.molecule]: Object.assign({}, ...rules) } });
36
+ const placement = {
37
+ top: placementStyle(top, center),
38
+ "top-start": placementStyle(top, start),
39
+ "top-end": placementStyle(top, end),
40
+ bottom: placementStyle(bottom, center),
41
+ "bottom-start": placementStyle(bottom, start),
42
+ "bottom-end": placementStyle(bottom, end)
43
+ };
19
44
  endFileScope();
20
45
  export {
21
- container
46
+ placement,
47
+ stack,
48
+ toast
22
49
  };
@@ -1,6 +1,14 @@
1
+ import { ToastPlacement } from '@blockle/blocks-core';
2
+ export type { ToastPlacement };
3
+ type ToastPosition = 'bottom-left' | 'bottom-center' | 'bottom-right' | 'top-left' | 'top-center' | 'top-right';
1
4
  export type ToastProviderProps = {
2
5
  children?: React.ReactNode;
3
- position?: 'bottom-left' | 'bottom-center' | 'bottom-right' | 'top-left' | 'top-center' | 'top-right';
6
+ /** Where the toasts sit on the screen. Defaults to `bottom` */
7
+ placement?: ToastPlacement;
8
+ /**
9
+ * @deprecated Use `placement`, which wins when both are passed
10
+ */
11
+ position?: ToastPosition;
4
12
  };
5
13
  export declare const ToastProvider: React.FC<ToastProviderProps>;
6
14
  //# sourceMappingURL=ToastProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToastProvider.d.ts","sourceRoot":"","sources":["../../../../src/components/feedback/Toast/ToastProvider.tsx"],"names":[],"mappings":"AA2BA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EACL,aAAa,GACb,eAAe,GACf,cAAc,GACd,UAAU,GACV,YAAY,GACZ,WAAW,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAwFtD,CAAC"}
1
+ {"version":3,"file":"ToastProvider.d.ts","sourceRoot":"","sources":["../../../../src/components/feedback/Toast/ToastProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA4BvE,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B,KAAK,aAAa,GACd,aAAa,GACb,eAAe,GACf,cAAc,GACd,UAAU,GACV,YAAY,GACZ,WAAW,CAAC;AAWhB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,+DAA+D;IAC/D,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;OAEG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAoGtD,CAAC"}
@@ -1,41 +1,64 @@
1
1
  "use client";
2
2
  import { jsxs, Fragment, jsx } from "react/jsx-runtime";
3
+ import { classnames } from "@blockle/blocks-core";
3
4
  import { useState, useRef, useCallback, useMemo } from "react";
4
5
  import { flushSync } from "react-dom";
5
- import { Box } from "../../layout/Box/Box.js";
6
- import { Stack } from "../../layout/Stack/Stack.js";
6
+ import { useComponentStyles } from "../../../hooks/useComponentStyles/useComponentStyles.js";
7
7
  import { Alert } from "../Alert/Alert.js";
8
- import { container } from "./Toast.css.js";
8
+ import { toast, stack, placement } from "./Toast.css.js";
9
9
  import { ToastContext } from "./ToastContext.js";
10
10
  function makeTransition(transition) {
11
11
  if (document.startViewTransition) {
12
- document.startViewTransition(() => {
12
+ const viewTransition = document.startViewTransition(() => {
13
13
  flushSync(() => {
14
14
  transition();
15
15
  });
16
16
  });
17
+ viewTransition.ready.catch(() => {
18
+ });
17
19
  } else {
18
20
  transition();
19
21
  }
20
22
  }
21
- const ToastProvider = ({ children }) => {
23
+ const placementByPosition = {
24
+ "bottom-left": "bottom-start",
25
+ "bottom-center": "bottom",
26
+ "bottom-right": "bottom-end",
27
+ "top-left": "top-start",
28
+ "top-center": "top",
29
+ "top-right": "top-end"
30
+ };
31
+ const ToastProvider = ({
32
+ children,
33
+ placement: placementProp,
34
+ position
35
+ }) => {
36
+ const placement$1 = placementProp ?? (position ? placementByPosition[position] : "bottom");
37
+ const rootClassName = useComponentStyles("toast", {
38
+ root: true,
39
+ variants: { placement: placement$1 }
40
+ });
41
+ const toastClassName = useComponentStyles("toast", {
42
+ toast: true,
43
+ variants: { placement: placement$1 }
44
+ });
22
45
  const [toasts, setToasts] = useState([]);
23
46
  const toastsRef = useRef(toasts);
24
47
  toastsRef.current = toasts;
25
48
  const removeToast = useCallback((id) => {
26
49
  makeTransition(() => {
27
- setToasts((prev) => prev.filter((toast) => toast.id !== id));
50
+ setToasts((prev) => prev.filter((toast2) => toast2.id !== id));
28
51
  });
29
52
  }, []);
30
- const addToast = useCallback((toast) => {
53
+ const addToast = useCallback((toast2) => {
31
54
  const toasts2 = toastsRef.current;
32
- const match = toasts2.findIndex(({ id }) => id === toast.id) !== -1;
55
+ const match = toasts2.findIndex(({ id }) => id === toast2.id) !== -1;
33
56
  function updateState(prev) {
34
- const match2 = prev.findIndex(({ id }) => id === toast.id) !== -1;
57
+ const match2 = prev.findIndex(({ id }) => id === toast2.id) !== -1;
35
58
  if (match2) {
36
- return prev.map((item) => item.id === toast.id ? toast : item);
59
+ return prev.map((item) => item.id === toast2.id ? toast2 : item);
37
60
  }
38
- return [...prev, toast];
61
+ return [...prev, toast2];
39
62
  }
40
63
  if (match) {
41
64
  setToasts(updateState);
@@ -44,13 +67,13 @@ const ToastProvider = ({ children }) => {
44
67
  makeTransition(() => {
45
68
  setToasts(updateState);
46
69
  });
47
- if (toast.duration) {
48
- const toastId = toast.id;
70
+ if (toast2.duration) {
71
+ const toastId = toast2.id;
49
72
  setTimeout(() => {
50
73
  makeTransition(() => {
51
- setToasts((prev) => prev.filter((toast2) => toast2.id !== toastId));
74
+ setToasts((prev) => prev.filter((toast22) => toast22.id !== toastId));
52
75
  });
53
- }, toast.duration);
76
+ }, toast2.duration);
54
77
  }
55
78
  }, []);
56
79
  const contextValue = useMemo(
@@ -63,18 +86,18 @@ const ToastProvider = ({ children }) => {
63
86
  return /* @__PURE__ */ jsxs(Fragment, { children: [
64
87
  /* @__PURE__ */ jsx(ToastContext.Provider, { value: contextValue, children }),
65
88
  /* @__PURE__ */ jsx(
66
- Box,
89
+ "div",
67
90
  {
68
- asChild: true,
69
- position: "fixed",
70
- insetBlockEnd: 0,
71
- insetInline: 0,
72
- padding: 2,
73
- children: /* @__PURE__ */ jsx(Stack, { gap: 2, children: toasts.map(({ id, children: children2, intent }) => /* @__PURE__ */ jsx(
91
+ className: classnames(
92
+ stack,
93
+ placement[placement$1],
94
+ rootClassName
95
+ ),
96
+ children: toasts.map(({ id, children: children2, intent }) => /* @__PURE__ */ jsx(
74
97
  Alert,
75
98
  {
76
99
  intent,
77
- className: container,
100
+ className: classnames(toast, toastClassName),
78
101
  style: {
79
102
  viewTransitionName: `toast-${id}`
80
103
  },
@@ -84,7 +107,7 @@ const ToastProvider = ({ children }) => {
84
107
  children: children2
85
108
  },
86
109
  id
87
- )) })
110
+ ))
88
111
  }
89
112
  )
90
113
  ] });
@@ -2,7 +2,6 @@ import { HTMLElementProps } from '@blockle/blocks-core';
2
2
  import { default as React } from 'react';
3
3
  export type CheckboxProps = {
4
4
  children?: React.ReactNode;
5
- name: string;
6
5
  required?: boolean;
7
6
  } & HTMLElementProps<HTMLInputElement>;
8
7
  export declare const Checkbox: React.FC<CheckboxProps>;
@@ -1 +1 @@
1
- {"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Checkbox/Checkbox.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;AAEvC,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAiD5C,CAAC"}
1
+ {"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Checkbox/Checkbox.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;AAEvC,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CA+C5C,CAAC"}
@@ -9,7 +9,6 @@ const Checkbox = ({
9
9
  children,
10
10
  className,
11
11
  id,
12
- name,
13
12
  required,
14
13
  ...restProps
15
14
  }) => {
@@ -31,7 +30,6 @@ const Checkbox = ({
31
30
  "input",
32
31
  {
33
32
  type: "checkbox",
34
- name,
35
33
  id: inputId,
36
34
  className: input,
37
35
  ...restProps
@@ -1 +1 @@
1
- {"version":3,"file":"checkbox.css.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Checkbox/checkbox.css.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,QAQpB,CAAC;AAEH,eAAO,MAAM,KAAK,QAShB,CAAC;AAEH,eAAO,MAAM,IAAI,QAEf,CAAC"}
1
+ {"version":3,"file":"checkbox.css.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Checkbox/checkbox.css.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,QASpB,CAAC;AAEH,eAAO,MAAM,KAAK,QAShB,CAAC;AAEH,eAAO,MAAM,IAAI,QAEf,CAAC"}
@@ -5,6 +5,7 @@ setFileScope("src/components/form/Checkbox/checkbox.css.ts", "@blockle/blocks-re
5
5
  const container = style({
6
6
  "@layer": {
7
7
  [layers.molecule]: {
8
+ flexShrink: 0,
8
9
  position: "relative",
9
10
  cursor: "pointer",
10
11
  overflow: "hidden"
@@ -1,7 +1,6 @@
1
1
  import { HTMLElementProps } from '@blockle/blocks-core';
2
2
  export type RadioProps = {
3
3
  children?: React.ReactNode;
4
- name: string;
5
4
  value: string;
6
5
  } & HTMLElementProps<HTMLInputElement>;
7
6
  export declare const Radio: React.FC<RadioProps>;
@@ -1 +1 @@
1
- {"version":3,"file":"Radio.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Radio/Radio.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAOzE,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;AAEvC,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,UAAU,CAqCtC,CAAC"}
1
+ {"version":3,"file":"Radio.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Radio/Radio.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAOzE,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;AAEvC,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,UAAU,CA8BtC,CAAC"}
@@ -6,7 +6,6 @@ import { useComponentStyles } from "../../../hooks/useComponentStyles/useCompone
6
6
  import { Label } from "../Label/Label.js";
7
7
  import { input, icon, container } from "./radio.css.js";
8
8
  const Radio = ({
9
- name,
10
9
  children,
11
10
  className,
12
11
  ...restProps
@@ -21,16 +20,7 @@ const Radio = ({
21
20
  {
22
21
  className: classnames(container, containerClassName, className),
23
22
  children: [
24
- /* @__PURE__ */ jsx(
25
- "input",
26
- {
27
- type: "radio",
28
- name,
29
- id,
30
- className: input,
31
- ...restProps
32
- }
33
- ),
23
+ /* @__PURE__ */ jsx("input", { type: "radio", id, className: input, ...restProps }),
34
24
  /* @__PURE__ */ jsx("div", { className: classnames(icon, iconClassName) })
35
25
  ]
36
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"radio.css.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Radio/radio.css.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,QAQpB,CAAC;AAEH,eAAO,MAAM,KAAK,QAShB,CAAC;AAEH,eAAO,MAAM,IAAI,QAEf,CAAC"}
1
+ {"version":3,"file":"radio.css.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Radio/radio.css.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,QASpB,CAAC;AAEH,eAAO,MAAM,KAAK,QAShB,CAAC;AAEH,eAAO,MAAM,IAAI,QAEf,CAAC"}
@@ -5,6 +5,7 @@ setFileScope("src/components/form/Radio/radio.css.ts", "@blockle/blocks-react");
5
5
  const container = style({
6
6
  "@layer": {
7
7
  [layers.molecule]: {
8
+ flexShrink: 0,
8
9
  position: "relative",
9
10
  cursor: "pointer",
10
11
  overflow: "hidden"
@@ -4,8 +4,9 @@ type SelectTheme = ComponentThemes['select'];
4
4
  export type SelectProps = {
5
5
  children: React.ReactNode;
6
6
  placeholder?: string;
7
+ size?: SelectTheme['variants']['size'];
7
8
  variant?: SelectTheme['variants']['variant'];
8
- } & HTMLElementProps<HTMLSelectElement>;
9
+ } & Omit<HTMLElementProps<HTMLSelectElement>, 'size'>;
9
10
  export declare const Select: React.FC<SelectProps>;
10
11
  export {};
11
12
  //# sourceMappingURL=Select.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Select/Select.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,KAAK,WAAW,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;AAE7C,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC;CAC9C,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;AAExC,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAgCxC,CAAC"}
1
+ {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Select/Select.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,KAAK,WAAW,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;AAE7C,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC;CAC9C,GAAG,IAAI,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAC;AAEtD,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAiCxC,CAAC"}
@@ -8,13 +8,14 @@ const Select = ({
8
8
  children,
9
9
  placeholder,
10
10
  className,
11
+ size,
11
12
  variant,
12
13
  ...restProps
13
14
  }) => {
14
15
  const wrapperClassName = useComponentStyles("select", { root: true }, false);
15
16
  const selectClassName = useComponentStyles("select", {
16
17
  select: true,
17
- variants: { variant }
18
+ variants: { variant, size }
18
19
  });
19
20
  const iconClassName = useComponentStyles("select", { icon: true }, false);
20
21
  return /* @__PURE__ */ jsxs(Box, { className: classnames(wrapper, wrapperClassName), children: [
@@ -1,6 +1,10 @@
1
1
  import { HTMLElementProps } from '@blockle/blocks-core';
2
2
  export type SwitchProps = {
3
- onChange?: (value: boolean) => void;
3
+ /**
4
+ * The native change handler of the underlying input.
5
+ * Read the new value from `event.target.checked`.
6
+ */
7
+ onChange?: React.ChangeEventHandler<HTMLInputElement>;
4
8
  } & Omit<HTMLElementProps<HTMLInputElement>, 'onChange'>;
5
9
  export declare const Switch: React.FC<SwitchProps>;
6
10
  //# sourceMappingURL=Switch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Switch/Switch.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAMzE,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC,GAAG,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,EAAE,UAAU,CAAC,CAAC;AAEzD,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAsExC,CAAC"}
1
+ {"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Switch/Switch.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAM9B,MAAM,MAAM,WAAW,GAAG;IACxB;;;OAGG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;CACvD,GAAG,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,EAAE,UAAU,CAAC,CAAC;AAEzD,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAyDxC,CAAC"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
- import { classnames } from "@blockle/blocks-core";
4
- import { useState, useEffect, useCallback } from "react";
3
+ import { composeRefs, classnames } from "@blockle/blocks-core";
4
+ import { useRef, useState } from "react";
5
5
  import { useComponentStyles } from "../../../hooks/useComponentStyles/useComponentStyles.js";
6
6
  import { input, container } from "./switch.css.js";
7
7
  const Switch = ({
@@ -9,55 +9,45 @@ const Switch = ({
9
9
  className,
10
10
  defaultChecked,
11
11
  onChange,
12
+ ref,
12
13
  ...restProps
13
14
  }) => {
14
- const [isChecked, setIsChecked] = useState(
15
- defaultChecked || checked || false
15
+ const inputRef = useRef(null);
16
+ const [uncontrolledChecked, setUncontrolledChecked] = useState(
17
+ defaultChecked ?? false
16
18
  );
19
+ const isControlled = checked !== void 0;
20
+ const isChecked = isControlled ? checked : uncontrolledChecked;
17
21
  const baseClassName = useComponentStyles("switch", { root: true });
18
22
  const thumbClassName = useComponentStyles("switch", { thumb: true }, false);
19
- useEffect(() => {
20
- if (checked !== void 0) {
21
- setIsChecked(checked);
23
+ const onChangeHandler = (event) => {
24
+ if (!isControlled) {
25
+ setUncontrolledChecked(event.target.checked);
22
26
  }
23
- }, [checked]);
24
- const toggle = useCallback(() => {
25
- setIsChecked((checked2) => {
26
- const newValue = !checked2;
27
- if (onChange) {
28
- onChange(newValue);
29
- }
30
- return newValue;
31
- });
32
- }, [onChange]);
33
- const onChangeHandler = useCallback(
34
- (event) => {
35
- setIsChecked(event.target.checked);
36
- if (onChange) {
37
- onChange(event.target.checked);
38
- }
39
- },
40
- [onChange]
41
- );
27
+ onChange?.(event);
28
+ };
42
29
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
43
30
  "div",
44
31
  {
45
32
  className: classnames(className, container, baseClassName),
46
33
  "data-checked": isChecked,
47
34
  onClick: (event) => {
48
- if (event.target.tagName === "INPUT") {
35
+ if (event.target === inputRef.current) {
49
36
  return;
50
37
  }
51
- toggle();
38
+ inputRef.current?.click();
52
39
  },
53
40
  children: [
54
41
  /* @__PURE__ */ jsx(
55
42
  "input",
56
43
  {
57
44
  type: "checkbox",
45
+ role: "switch",
58
46
  className: input,
59
- checked: isChecked,
47
+ checked,
48
+ defaultChecked,
60
49
  onChange: onChangeHandler,
50
+ ref: composeRefs(ref, inputRef),
61
51
  ...restProps
62
52
  }
63
53
  ),
@@ -1 +1 @@
1
- {"version":3,"file":"switch.css.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Switch/switch.css.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,QAQpB,CAAC;AAEH,eAAO,MAAM,KAAK,QAQhB,CAAC"}
1
+ {"version":3,"file":"switch.css.d.ts","sourceRoot":"","sources":["../../../../src/components/form/Switch/switch.css.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,QASpB,CAAC;AAEH,eAAO,MAAM,KAAK,QAQhB,CAAC"}