@unofficialbox/box-open-elements-preact 0.0.0-stage → 0.42.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kyle @ Unofficial Box
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,72 @@
1
- # Temporary Holding Version
1
+ # Preact adapter
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `@unofficialbox/box-open-elements-preact` is published on npm and independently
4
+ reviewed. Its `0.42.2` human bootstrap was verified by integrity/content comparison
5
+ against merged commit `03fa63b33105dbcef9a6c1e6eed770db2e554cb4`, a normal isolated
6
+ registry install, public JSX types, SSR/hydration, and real browser acceptance.
7
+ That bootstrap has no OIDC provenance; publisher setup and first OIDC validation
8
+ are distinct release steps. See [RELEASING.md](../../RELEASING.md).
9
+
10
+ ```sh
11
+ npm install @unofficialbox/box-open-elements-preact preact
12
+ ```
13
+
14
+ Exports: `Button`, `TextField`, `Select`, `Dialog`, `Combobox`, `Drawer`, `createWebComponent`,
15
+ `ExplorerSelectionController`, and `useExplorerSelectionController`, with public
16
+ prop/event/ref declarations and per-component subpath exports.
17
+
18
+ Peers for this package version: core `^0.42.3` and preact `^11.0.1`.
19
+
20
+ ```tsx
21
+ import { TextField, Select, Dialog } from "@unofficialbox/box-open-elements-preact";
22
+
23
+ // Render inside your framework component. Pass normal JSX props, including
24
+ // native Preact state values. Structured values go through BOE setters.
25
+ <TextField name="title" label="Title" value="Example"
26
+ ref={element => element?.focus()}
27
+ onValueChanged={event => console.log(event.detail.value)} />;
28
+ <Select options={[{ label: "One", value: "1" }]} value="1" />;
29
+ <Dialog open={false} heading="Details"><span slot="footer">Footer</span></Dialog>;
30
+ ```
31
+
32
+ This package uses Preact 11's native ref-as-prop contract and native hooks, with
33
+ no `preact/compat` dependency. Object refs and callback refs are forwarded to the
34
+ host; Preact callback-ref cleanup functions are preserved. The controller hook
35
+ subscribes after commit and releases on controller change/unmount. Preact 10 is
36
+ not in the supported peer range.
37
+
38
+ Mapped BOE event callbacks receive the actual DOM event; `preventDefault` and
39
+ `stopPropagation` act on it. Cancellation works when the underlying event is
40
+ cancelable; BOE Dialog's `cancel` notification itself is not cancelable. Factory
41
+ `events` maps preserve exact spelling/case, and listeners are removed when the
42
+ framework owner unmounts. Temporary DOM disconnect/reconnect retains the owner.
43
+ The wrappers retain BOE's optional-property semantics: omitted scalar props leave
44
+ the element value alone; disabled/invalid default to false. Use explicit false,
45
+ empty string, or empty array to clear controlled values. BOE Select intentionally
46
+ reflects its assigned options array as JSON. Host attributes, children, and slots
47
+ remain framework-native. Use exact camelCase property names for custom-element
48
+ setters; other host props use Preact's normal attribute handling. Boolean wrapper
49
+ props take true/false, while `aria-*` and `data-*` false values remain string
50
+ attributes. If you need an attribute distinct from a custom-element setter, use
51
+ the forwarded ref's `setAttribute`/`removeAttribute` in a lifecycle hook; Preact
52
+ has no Solid-style attribute-prefix API.
53
+
54
+ SSR imports require no DOM globals. Server rendering emits inert custom-element
55
+ hosts and light-DOM children; controlled properties and arrays are assigned on
56
+ client hydration, so do not expect populated shadow DOM before JavaScript runs.
57
+ Mapped listeners attach before initial property sync, including synchronous
58
+ `open-changed` notifications from initially-open Dialog and Drawer. Browser
59
+ fixtures cover closed-first and initially-open client/hydrated rendering.
60
+ Hydrate with native `preact.hydrate` and use `preact-render-to-string` for server
61
+ rendering. Deduplicate Preact in bundlers to keep renderer/hooks on one runtime.
62
+
63
+ Validation: `bun run frameworks:preact`, `bun run frameworks:preact:ssr`,
64
+ `bun run test -- packages/preact`, `bun run frameworks:jsx:browser`, and
65
+ `bun run adapters:pack-check`. See [support status](../../docs/integration/framework-adapters.md)
66
+ and [release requirements](../../RELEASING.md).
67
+
68
+ Design references: [Preact 11 custom elements](https://preactjs.com/guide/v11/web-components/) and
69
+ [native SSR](https://preactjs.com/guide/v11/server-side-rendering/).
70
+ The installed 11.0.1 `create-element` source preserves refs in function-component
71
+ props, and native callback refs support cleanup functions. A dedicated native
72
+ adapter avoids React peer/type/event contracts and does not depend on compat.
@@ -0,0 +1,23 @@
1
+ import { Button as ButtonElement } from "@unofficialbox/box-open-elements/button";
2
+ import { type WebComponentProps } from "./create-web-component.js";
3
+ import type { NativeEventHandler } from "./events.js";
4
+ export type ButtonProps = Omit<WebComponentProps, "onClick"> & {
5
+ label?: string;
6
+ accessibleLabel?: string;
7
+ tone?: string;
8
+ size?: string;
9
+ disabled?: boolean;
10
+ onClick?: NativeEventHandler<ButtonElement, MouseEvent>;
11
+ };
12
+ export declare const Button: (props: Omit<WebComponentProps, "onClick"> & {
13
+ label?: string;
14
+ accessibleLabel?: string;
15
+ tone?: string;
16
+ size?: string;
17
+ disabled?: boolean;
18
+ onClick?: NativeEventHandler<ButtonElement, MouseEvent>;
19
+ } & {
20
+ ref?: import("preact").Ref<ButtonElement> | undefined;
21
+ }) => import("preact").VNode<import("preact").Attributes & {
22
+ ref: (node: ButtonElement | null) => void | (() => void);
23
+ }>;
package/dist/button.js ADDED
@@ -0,0 +1,22 @@
1
+ import { Button as ButtonElement } from "@unofficialbox/box-open-elements/button";
2
+ import { createWebComponent } from "./create-web-component.js";
3
+ ButtonElement.register();
4
+ export const Button = createWebComponent({
5
+ tagName: "box-button",
6
+ propertyNames: ["label", "accessibleLabel", "tone", "size", "disabled"],
7
+ events: [{ propName: "onClick", eventName: "click" }],
8
+ sync: (element, props) => {
9
+ if (props.label !== undefined) {
10
+ element.label = props.label;
11
+ }
12
+ if (props.accessibleLabel !== undefined)
13
+ element.accessibleLabel = props.accessibleLabel;
14
+ if (props.tone !== undefined) {
15
+ element.tone = props.tone;
16
+ }
17
+ if (props.size !== undefined) {
18
+ element.size = props.size;
19
+ }
20
+ element.disabled = Boolean(props.disabled);
21
+ },
22
+ });
@@ -0,0 +1,46 @@
1
+ import { Combobox as ComboboxElement } from "@unofficialbox/box-open-elements/combobox";
2
+ import { type WebComponentProps } from "./create-web-component.js";
3
+ import type { CustomEventHandler, ValueChangedDetail } from "./events.js";
4
+ export type ComboboxOption = {
5
+ label: string;
6
+ value: string;
7
+ description?: string;
8
+ group?: string;
9
+ disabled?: boolean;
10
+ };
11
+ export type ComboboxProps = WebComponentProps & {
12
+ label?: string;
13
+ value?: string;
14
+ options?: ComboboxOption[];
15
+ placeholder?: string;
16
+ placement?: string;
17
+ disabled?: boolean;
18
+ name?: string;
19
+ invalid?: boolean;
20
+ errorMessage?: string;
21
+ description?: string;
22
+ required?: boolean;
23
+ hideLabel?: boolean;
24
+ onValueChanged?: CustomEventHandler<ComboboxElement, ValueChangedDetail>;
25
+ };
26
+ export declare const Combobox: (props: Omit<import("preact").HTMLAttributes<HTMLElement>, "ref" | "onCancel"> & {
27
+ children?: import("preact").ComponentChildren;
28
+ } & {
29
+ label?: string;
30
+ value?: string;
31
+ options?: ComboboxOption[];
32
+ placeholder?: string;
33
+ placement?: string;
34
+ disabled?: boolean;
35
+ name?: string;
36
+ invalid?: boolean;
37
+ errorMessage?: string;
38
+ description?: string;
39
+ required?: boolean;
40
+ hideLabel?: boolean;
41
+ onValueChanged?: CustomEventHandler<ComboboxElement, ValueChangedDetail>;
42
+ } & {
43
+ ref?: import("preact").Ref<ComboboxElement> | undefined;
44
+ }) => import("preact").VNode<import("preact").Attributes & {
45
+ ref: (node: ComboboxElement | null) => void | (() => void);
46
+ }>;
@@ -0,0 +1,35 @@
1
+ import { Combobox as ComboboxElement } from "@unofficialbox/box-open-elements/combobox";
2
+ import { createWebComponent } from "./create-web-component.js";
3
+ ComboboxElement.register();
4
+ export const Combobox = createWebComponent({
5
+ tagName: "box-combobox",
6
+ propertyNames: [
7
+ "label", "value", "options", "placeholder", "placement", "disabled",
8
+ "name", "invalid", "errorMessage", "description", "required", "hideLabel",
9
+ ],
10
+ events: [{ propName: "onValueChanged", eventName: "value-changed" }],
11
+ sync: (element, props) => {
12
+ if (props.label !== undefined)
13
+ element.label = props.label;
14
+ if (props.value !== undefined)
15
+ element.value = props.value;
16
+ if (props.options !== undefined)
17
+ element.options = props.options;
18
+ if (props.placeholder !== undefined)
19
+ element.placeholder = props.placeholder;
20
+ if (props.placement !== undefined)
21
+ element.placement = props.placement;
22
+ if (props.name !== undefined)
23
+ element.name = props.name;
24
+ if (props.errorMessage !== undefined)
25
+ element.errorMessage = props.errorMessage;
26
+ if (props.description !== undefined)
27
+ element.description = props.description;
28
+ if (props.required !== undefined)
29
+ element.required = props.required;
30
+ if (props.hideLabel !== undefined)
31
+ element.hideLabel = props.hideLabel;
32
+ element.disabled = Boolean(props.disabled);
33
+ element.invalid = Boolean(props.invalid);
34
+ },
35
+ });
@@ -0,0 +1,19 @@
1
+ import { type ComponentChildren, type HTMLAttributes, type Ref } from "preact";
2
+ export type WebComponentProps = Omit<HTMLAttributes<HTMLElement>, "ref" | "onCancel"> & {
3
+ children?: ComponentChildren;
4
+ };
5
+ export type CreateWebComponentOptions<E extends HTMLElement, P> = {
6
+ tagName: string;
7
+ sync: (element: E, props: P) => void;
8
+ propertyNames?: ReadonlyArray<keyof P & string>;
9
+ events?: ReadonlyArray<{
10
+ propName: keyof P & string;
11
+ eventName: string;
12
+ }>;
13
+ };
14
+ /** Native Preact 11 component: ref is a prop, without preact/compat or React. */
15
+ export declare function createWebComponent<E extends HTMLElement, P extends object>(options: CreateWebComponentOptions<E, P>): (props: P & {
16
+ ref?: Ref<E>;
17
+ }) => import("preact").VNode<import("preact").Attributes & {
18
+ ref: (node: E | null) => void | (() => void);
19
+ }>;
@@ -0,0 +1,48 @@
1
+ import { h } from "preact";
2
+ import { useCallback, useLayoutEffect, useRef } from "preact/hooks";
3
+ /** Native Preact 11 component: ref is a prop, without preact/compat or React. */
4
+ export function createWebComponent(options) {
5
+ return function WebComponent(props) {
6
+ const element = useRef(null);
7
+ const committed = useRef(props);
8
+ const setRef = useCallback((node) => {
9
+ element.current = node;
10
+ if (typeof props.ref === "function")
11
+ return props.ref(node);
12
+ if (props.ref)
13
+ props.ref.current = node;
14
+ }, [props.ref]);
15
+ // Bind before initial sync: opening overlays emits synchronously.
16
+ useLayoutEffect(() => {
17
+ const node = element.current;
18
+ if (!node)
19
+ return;
20
+ const subscriptions = (options.events ?? []).map(binding => {
21
+ const listener = (event) => {
22
+ const handler = committed.current[binding.propName];
23
+ if (typeof handler === "function")
24
+ handler(event);
25
+ };
26
+ node.addEventListener(binding.eventName, listener);
27
+ return { ...binding, listener };
28
+ });
29
+ return () => {
30
+ for (const binding of subscriptions)
31
+ node.removeEventListener(binding.eventName, binding.listener);
32
+ };
33
+ }, []);
34
+ // Only committed renders affect listeners, including interrupted renders.
35
+ useLayoutEffect(() => {
36
+ committed.current = props;
37
+ if (element.current)
38
+ options.sync(element.current, props);
39
+ });
40
+ const host = { ...props };
41
+ delete host.ref;
42
+ for (const name of options.propertyNames ?? [])
43
+ delete host[name];
44
+ for (const binding of options.events ?? [])
45
+ delete host[binding.propName];
46
+ return h(options.tagName, { ...host, ref: setRef });
47
+ };
48
+ }
@@ -0,0 +1,46 @@
1
+ import type { ComponentChildren } from "preact";
2
+ import { Dialog as DialogElement } from "@unofficialbox/box-open-elements/dialog";
3
+ import { type WebComponentProps } from "./create-web-component.js";
4
+ import type { CustomEventHandler } from "./events.js";
5
+ export type DialogConfirmTone = "primary" | "danger";
6
+ export type DialogSize = "small" | "medium" | "large" | "fullscreen";
7
+ export type DialogProps = WebComponentProps & {
8
+ children?: ComponentChildren;
9
+ open?: boolean;
10
+ heading?: string;
11
+ description?: string;
12
+ confirmLabel?: string;
13
+ confirmTone?: DialogConfirmTone;
14
+ confirmDisabled?: boolean;
15
+ confirmBusy?: boolean;
16
+ confirmBusyLabel?: string;
17
+ size?: DialogSize;
18
+ onOpenChanged?: CustomEventHandler<DialogElement, {
19
+ open: boolean;
20
+ }>;
21
+ onConfirm?: CustomEventHandler<DialogElement, null>;
22
+ onCancel?: CustomEventHandler<DialogElement, null>;
23
+ };
24
+ export declare const Dialog: (props: Omit<import("preact").HTMLAttributes<HTMLElement>, "ref" | "onCancel"> & {
25
+ children?: ComponentChildren;
26
+ } & {
27
+ children?: ComponentChildren;
28
+ open?: boolean;
29
+ heading?: string;
30
+ description?: string;
31
+ confirmLabel?: string;
32
+ confirmTone?: DialogConfirmTone;
33
+ confirmDisabled?: boolean;
34
+ confirmBusy?: boolean;
35
+ confirmBusyLabel?: string;
36
+ size?: DialogSize;
37
+ onOpenChanged?: CustomEventHandler<DialogElement, {
38
+ open: boolean;
39
+ }>;
40
+ onConfirm?: CustomEventHandler<DialogElement, null>;
41
+ onCancel?: CustomEventHandler<DialogElement, null>;
42
+ } & {
43
+ ref?: import("preact").Ref<DialogElement> | undefined;
44
+ }) => import("preact").VNode<import("preact").Attributes & {
45
+ ref: (node: DialogElement | null) => void | (() => void);
46
+ }>;
package/dist/dialog.js ADDED
@@ -0,0 +1,37 @@
1
+ import { Dialog as DialogElement } from "@unofficialbox/box-open-elements/dialog";
2
+ import { createWebComponent } from "./create-web-component.js";
3
+ DialogElement.register();
4
+ export const Dialog = createWebComponent({
5
+ tagName: "box-dialog",
6
+ propertyNames: ["open", "heading", "description", "confirmLabel", "confirmTone", "confirmDisabled", "confirmBusy", "confirmBusyLabel", "size"],
7
+ events: [
8
+ { propName: "onOpenChanged", eventName: "open-changed" },
9
+ { propName: "onConfirm", eventName: "confirm" },
10
+ { propName: "onCancel", eventName: "cancel" },
11
+ ],
12
+ sync: (element, props) => {
13
+ if (props.heading !== undefined) {
14
+ element.heading = props.heading;
15
+ }
16
+ if (props.description !== undefined) {
17
+ element.description = props.description;
18
+ }
19
+ if (props.confirmLabel !== undefined) {
20
+ element.confirmLabel = props.confirmLabel;
21
+ }
22
+ if (props.confirmTone !== undefined)
23
+ element.confirmTone = props.confirmTone;
24
+ if (props.confirmDisabled !== undefined)
25
+ element.confirmDisabled = props.confirmDisabled;
26
+ if (props.confirmBusy !== undefined)
27
+ element.confirmBusy = props.confirmBusy;
28
+ if (props.confirmBusyLabel !== undefined)
29
+ element.confirmBusyLabel = props.confirmBusyLabel;
30
+ if (props.size !== undefined) {
31
+ element.size = props.size;
32
+ }
33
+ if (props.open !== undefined) {
34
+ element.open = props.open;
35
+ }
36
+ },
37
+ });
@@ -0,0 +1,43 @@
1
+ import type { ComponentChildren } from "preact";
2
+ import { Drawer as DrawerRef } from "@unofficialbox/box-open-elements/drawer";
3
+ import { type WebComponentProps } from "./create-web-component.js";
4
+ import type { CustomEventHandler } from "./events.js";
5
+ export type { DrawerRef };
6
+ export type DrawerProps = WebComponentProps & {
7
+ children?: ComponentChildren;
8
+ heading?: string;
9
+ description?: string;
10
+ open?: boolean;
11
+ position?: string;
12
+ size?: string;
13
+ busy?: boolean;
14
+ hideCloseButton?: boolean;
15
+ onOpenChanged?: CustomEventHandler<DrawerRef, {
16
+ open: boolean;
17
+ }>;
18
+ onDismiss?: CustomEventHandler<DrawerRef, {
19
+ source: "close-button" | "backdrop" | "escape";
20
+ }>;
21
+ };
22
+ export declare const Drawer: (props: Omit<import("preact").HTMLAttributes<HTMLElement>, "ref" | "onCancel"> & {
23
+ children?: ComponentChildren;
24
+ } & {
25
+ children?: ComponentChildren;
26
+ heading?: string;
27
+ description?: string;
28
+ open?: boolean;
29
+ position?: string;
30
+ size?: string;
31
+ busy?: boolean;
32
+ hideCloseButton?: boolean;
33
+ onOpenChanged?: CustomEventHandler<DrawerRef, {
34
+ open: boolean;
35
+ }>;
36
+ onDismiss?: CustomEventHandler<DrawerRef, {
37
+ source: "close-button" | "backdrop" | "escape";
38
+ }>;
39
+ } & {
40
+ ref?: import("preact").Ref<DrawerRef> | undefined;
41
+ }) => import("preact").VNode<import("preact").Attributes & {
42
+ ref: (node: DrawerRef | null) => void | (() => void);
43
+ }>;
package/dist/drawer.js ADDED
@@ -0,0 +1,23 @@
1
+ import { Drawer as DrawerRef } from "@unofficialbox/box-open-elements/drawer";
2
+ import { createWebComponent } from "./create-web-component.js";
3
+ export const Drawer = createWebComponent({
4
+ tagName: DrawerRef.tagName,
5
+ propertyNames: ["heading", "description", "open", "position", "size", "busy", "hideCloseButton"],
6
+ events: [{ propName: "onOpenChanged", eventName: "open-changed" }, { propName: "onDismiss", eventName: "dismiss" }],
7
+ sync: (element, props) => {
8
+ if (props.heading !== undefined)
9
+ element.heading = props.heading;
10
+ if (props.description !== undefined)
11
+ element.description = props.description;
12
+ if (props.open !== undefined)
13
+ element.open = props.open;
14
+ if (props.position !== undefined)
15
+ element.position = props.position;
16
+ if (props.size !== undefined)
17
+ element.size = props.size;
18
+ if (props.busy !== undefined)
19
+ element.busy = props.busy;
20
+ if (props.hideCloseButton !== undefined)
21
+ element.hideCloseButton = props.hideCloseButton;
22
+ },
23
+ });
@@ -0,0 +1,9 @@
1
+ export type CustomEventHandler<E extends HTMLElement, Detail> = (event: CustomEvent<Detail> & {
2
+ currentTarget: E;
3
+ }) => void;
4
+ export type NativeEventHandler<E extends HTMLElement, Ev extends Event = Event> = (event: Ev & {
5
+ currentTarget: E;
6
+ }) => void;
7
+ export type ValueChangedDetail = {
8
+ value: string;
9
+ };
package/dist/events.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import { ExplorerSelectionController, type ExplorerSelectionState } from "@unofficialbox/box-open-elements/patterns/content-explorer/selection";
2
+ export { ExplorerSelectionController };
3
+ export type { ExplorerSelectionState };
4
+ export declare function useExplorerSelectionController(controller: ExplorerSelectionController): Readonly<ExplorerSelectionState>;
@@ -0,0 +1,13 @@
1
+ import { useLayoutEffect, useState } from "preact/hooks";
2
+ import { ExplorerSelectionController } from "@unofficialbox/box-open-elements/patterns/content-explorer/selection";
3
+ export { ExplorerSelectionController };
4
+ export function useExplorerSelectionController(controller) {
5
+ const [snapshot, setSnapshot] = useState(() => ({ controller, state: controller.getState() }));
6
+ useLayoutEffect(() => {
7
+ const update = () => setSnapshot({ controller, state: controller.getState() });
8
+ const unsubscribe = controller.subscribe("selectionChanged", update);
9
+ update();
10
+ return unsubscribe;
11
+ }, [controller]);
12
+ return snapshot.controller === controller ? snapshot.state : controller.getState();
13
+ }
@@ -0,0 +1,9 @@
1
+ export { createWebComponent, type WebComponentProps, type CreateWebComponentOptions } from "./create-web-component.js";
2
+ export type { CustomEventHandler, NativeEventHandler, ValueChangedDetail } from "./events.js";
3
+ export { Button, type ButtonProps } from "./button.js";
4
+ export { TextField, type TextFieldProps } from "./text-field.js";
5
+ export { Select, type SelectProps, type SelectOption } from "./select.js";
6
+ export { Dialog, type DialogProps, type DialogConfirmTone, type DialogSize } from "./dialog.js";
7
+ export { ExplorerSelectionController, useExplorerSelectionController, type ExplorerSelectionState } from "./explorer-selection.js";
8
+ export { Combobox, type ComboboxProps, type ComboboxOption } from "./combobox.js";
9
+ export { Drawer, type DrawerProps, type DrawerRef } from "./drawer.js";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { createWebComponent } from "./create-web-component.js";
2
+ export { Button } from "./button.js";
3
+ export { TextField } from "./text-field.js";
4
+ export { Select } from "./select.js";
5
+ export { Dialog } from "./dialog.js";
6
+ export { ExplorerSelectionController, useExplorerSelectionController } from "./explorer-selection.js";
7
+ export { Combobox } from "./combobox.js";
8
+ export { Drawer } from "./drawer.js";
@@ -0,0 +1,39 @@
1
+ import { Select as SelectElement } from "@unofficialbox/box-open-elements/select";
2
+ import { type WebComponentProps } from "./create-web-component.js";
3
+ import type { CustomEventHandler, ValueChangedDetail } from "./events.js";
4
+ export type SelectOption = {
5
+ label: string;
6
+ value: string;
7
+ };
8
+ export type SelectProps = WebComponentProps & {
9
+ label?: string;
10
+ value?: string;
11
+ options?: SelectOption[];
12
+ disabled?: boolean;
13
+ name?: string;
14
+ invalid?: boolean;
15
+ errorMessage?: string;
16
+ description?: string;
17
+ required?: boolean;
18
+ hideLabel?: boolean;
19
+ onValueChanged?: CustomEventHandler<SelectElement, ValueChangedDetail>;
20
+ };
21
+ export declare const Select: (props: Omit<import("preact").HTMLAttributes<HTMLElement>, "ref" | "onCancel"> & {
22
+ children?: import("preact").ComponentChildren;
23
+ } & {
24
+ label?: string;
25
+ value?: string;
26
+ options?: SelectOption[];
27
+ disabled?: boolean;
28
+ name?: string;
29
+ invalid?: boolean;
30
+ errorMessage?: string;
31
+ description?: string;
32
+ required?: boolean;
33
+ hideLabel?: boolean;
34
+ onValueChanged?: CustomEventHandler<SelectElement, ValueChangedDetail>;
35
+ } & {
36
+ ref?: import("preact").Ref<SelectElement> | undefined;
37
+ }) => import("preact").VNode<import("preact").Attributes & {
38
+ ref: (node: SelectElement | null) => void | (() => void);
39
+ }>;
package/dist/select.js ADDED
@@ -0,0 +1,42 @@
1
+ import { Select as SelectElement } from "@unofficialbox/box-open-elements/select";
2
+ import { createWebComponent } from "./create-web-component.js";
3
+ SelectElement.register();
4
+ export const Select = createWebComponent({
5
+ tagName: "box-select",
6
+ propertyNames: [
7
+ "label",
8
+ "value",
9
+ "options",
10
+ "disabled",
11
+ "name",
12
+ "invalid",
13
+ "errorMessage",
14
+ "description", "required", "hideLabel",
15
+ ],
16
+ events: [{ propName: "onValueChanged", eventName: "value-changed" }],
17
+ sync: (element, props) => {
18
+ if (props.description !== undefined)
19
+ element.description = props.description;
20
+ if (props.required !== undefined)
21
+ element.required = props.required;
22
+ if (props.hideLabel !== undefined)
23
+ element.hideLabel = props.hideLabel;
24
+ if (props.label !== undefined) {
25
+ element.label = props.label;
26
+ }
27
+ if (props.value !== undefined) {
28
+ element.value = props.value;
29
+ }
30
+ if (props.options !== undefined) {
31
+ element.options = props.options;
32
+ }
33
+ if (props.name !== undefined) {
34
+ element.name = props.name;
35
+ }
36
+ if (props.errorMessage !== undefined) {
37
+ element.errorMessage = props.errorMessage;
38
+ }
39
+ element.disabled = Boolean(props.disabled);
40
+ element.invalid = Boolean(props.invalid);
41
+ },
42
+ });
@@ -0,0 +1,45 @@
1
+ import { TextField as TextFieldElement } from "@unofficialbox/box-open-elements/text-field";
2
+ import { type WebComponentProps } from "./create-web-component.js";
3
+ import type { CustomEventHandler, ValueChangedDetail } from "./events.js";
4
+ export type TextFieldProps = WebComponentProps & {
5
+ label?: string;
6
+ value?: string;
7
+ placeholder?: string;
8
+ type?: "text" | "password" | "email" | "tel" | "url" | "search" | "number";
9
+ reveal?: boolean;
10
+ revealLabel?: string;
11
+ autocomplete?: string;
12
+ disabled?: boolean;
13
+ readonly?: boolean;
14
+ name?: string;
15
+ invalid?: boolean;
16
+ errorMessage?: string;
17
+ description?: string;
18
+ required?: boolean;
19
+ hideLabel?: boolean;
20
+ onValueChanged?: CustomEventHandler<TextFieldElement, ValueChangedDetail>;
21
+ };
22
+ export declare const TextField: (props: Omit<import("preact").HTMLAttributes<HTMLElement>, "ref" | "onCancel"> & {
23
+ children?: import("preact").ComponentChildren;
24
+ } & {
25
+ label?: string;
26
+ value?: string;
27
+ placeholder?: string;
28
+ type?: "text" | "password" | "email" | "tel" | "url" | "search" | "number";
29
+ reveal?: boolean;
30
+ revealLabel?: string;
31
+ autocomplete?: string;
32
+ disabled?: boolean;
33
+ readonly?: boolean;
34
+ name?: string;
35
+ invalid?: boolean;
36
+ errorMessage?: string;
37
+ description?: string;
38
+ required?: boolean;
39
+ hideLabel?: boolean;
40
+ onValueChanged?: CustomEventHandler<TextFieldElement, ValueChangedDetail>;
41
+ } & {
42
+ ref?: import("preact").Ref<TextFieldElement> | undefined;
43
+ }) => import("preact").VNode<import("preact").Attributes & {
44
+ ref: (node: TextFieldElement | null) => void | (() => void);
45
+ }>;
@@ -0,0 +1,54 @@
1
+ import { TextField as TextFieldElement } from "@unofficialbox/box-open-elements/text-field";
2
+ import { createWebComponent } from "./create-web-component.js";
3
+ TextFieldElement.register();
4
+ export const TextField = createWebComponent({
5
+ tagName: "box-text-field",
6
+ propertyNames: [
7
+ "label",
8
+ "value",
9
+ "placeholder",
10
+ "type", "reveal", "revealLabel", "autocomplete",
11
+ "disabled",
12
+ "readonly",
13
+ "name",
14
+ "invalid",
15
+ "errorMessage",
16
+ "description", "required", "hideLabel",
17
+ ],
18
+ events: [{ propName: "onValueChanged", eventName: "value-changed" }],
19
+ sync: (element, props) => {
20
+ if (props.readonly !== undefined)
21
+ element.readonly = props.readonly;
22
+ if (props.description !== undefined)
23
+ element.description = props.description;
24
+ if (props.required !== undefined)
25
+ element.required = props.required;
26
+ if (props.hideLabel !== undefined)
27
+ element.hideLabel = props.hideLabel;
28
+ if (props.label !== undefined) {
29
+ element.label = props.label;
30
+ }
31
+ if (props.value !== undefined) {
32
+ element.value = props.value;
33
+ }
34
+ if (props.placeholder !== undefined) {
35
+ element.placeholder = props.placeholder;
36
+ }
37
+ if (props.type !== undefined)
38
+ element.type = props.type;
39
+ if (props.reveal !== undefined)
40
+ element.reveal = props.reveal;
41
+ if (props.revealLabel !== undefined)
42
+ element.revealLabel = props.revealLabel;
43
+ if (props.autocomplete !== undefined)
44
+ element.autocomplete = props.autocomplete;
45
+ if (props.name !== undefined) {
46
+ element.name = props.name;
47
+ }
48
+ if (props.errorMessage !== undefined) {
49
+ element.errorMessage = props.errorMessage;
50
+ }
51
+ element.disabled = Boolean(props.disabled);
52
+ element.invalid = Boolean(props.invalid);
53
+ },
54
+ });
package/package.json CHANGED
@@ -1,6 +1,53 @@
1
1
  {
2
2
  "name": "@unofficialbox/box-open-elements-preact",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.42.3",
4
+ "description": "Optional native preact adapters for box-open-elements Web Components.",
5
+ "license": "MIT",
6
+ "homepage": "https://unofficialbox.github.io/box-open-elements",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/unofficialbox/box-open-elements.git",
10
+ "directory": "packages/preact"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/unofficialbox/box-open-elements/issues"
14
+ },
15
+ "keywords": [
16
+ "preact",
17
+ "web-components",
18
+ "design-system",
19
+ "box"
20
+ ],
21
+ "type": "module",
22
+ "sideEffects": false,
23
+ "main": "dist/index.js",
24
+ "types": "dist/index.d.ts",
25
+ "exports": {
26
+ "./*": {
27
+ "types": "./dist/*.d.ts",
28
+ "import": "./dist/*.js"
29
+ },
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js"
33
+ }
34
+ },
35
+ "files": [
36
+ "dist"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "engines": {
42
+ "node": ">=20.9.0"
43
+ },
44
+ "peerDependencies": {
45
+ "@unofficialbox/box-open-elements": "^0.42.3",
46
+ "preact": "^11.0.1"
47
+ },
48
+ "scripts": {
49
+ "build": "tsc -p tsconfig.build.json",
50
+ "prepublishOnly": "bun run build",
51
+ "typecheck": "tsc -p tsconfig.json --noEmit"
52
+ }
53
+ }