@unofficialbox/box-open-elements-preact 0.0.0-stage → 0.42.2
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 +21 -0
- package/README.md +64 -2
- package/dist/button.d.ts +23 -0
- package/dist/button.js +22 -0
- package/dist/combobox.d.ts +46 -0
- package/dist/combobox.js +35 -0
- package/dist/create-web-component.d.ts +19 -0
- package/dist/create-web-component.js +48 -0
- package/dist/dialog.d.ts +46 -0
- package/dist/dialog.js +37 -0
- package/dist/drawer.d.ts +43 -0
- package/dist/drawer.js +23 -0
- package/dist/events.d.ts +9 -0
- package/dist/events.js +1 -0
- package/dist/explorer-selection.d.ts +4 -0
- package/dist/explorer-selection.js +13 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +8 -0
- package/dist/select.d.ts +39 -0
- package/dist/select.js +42 -0
- package/dist/text-field.d.ts +45 -0
- package/dist/text-field.js +54 -0
- package/package.json +51 -4
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,65 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Preact adapter
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@unofficialbox/box-open-elements-preact` is implemented, awaiting independent review,
|
|
4
|
+
and **not published**. Use the workspace or a locally packed tarball for now.
|
|
5
|
+
The package version follows core lockstep; it does not indicate npm availability.
|
|
6
|
+
|
|
7
|
+
Exports: `Button`, `TextField`, `Select`, `Dialog`, `Combobox`, `Drawer`, `createWebComponent`,
|
|
8
|
+
`ExplorerSelectionController`, and `useExplorerSelectionController`, with public
|
|
9
|
+
prop/event/ref declarations and per-component subpath exports.
|
|
10
|
+
|
|
11
|
+
Peers: core `^0.42.2` and preact `^11.0.1`.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { TextField, Select, Dialog } from "@unofficialbox/box-open-elements-preact";
|
|
15
|
+
|
|
16
|
+
// Render inside your framework component. Pass normal JSX props, including
|
|
17
|
+
// native Preact state values. Structured values go through BOE setters.
|
|
18
|
+
<TextField name="title" label="Title" value="Example"
|
|
19
|
+
ref={element => element?.focus()}
|
|
20
|
+
onValueChanged={event => console.log(event.detail.value)} />;
|
|
21
|
+
<Select options={[{ label: "One", value: "1" }]} value="1" />;
|
|
22
|
+
<Dialog open={false} heading="Details"><span slot="footer">Footer</span></Dialog>;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This package uses Preact 11's native ref-as-prop contract and native hooks, with
|
|
26
|
+
no `preact/compat` dependency. Object refs and callback refs are forwarded to the
|
|
27
|
+
host; Preact callback-ref cleanup functions are preserved. The controller hook
|
|
28
|
+
subscribes after commit and releases on controller change/unmount. Preact 10 is
|
|
29
|
+
not in the supported peer range.
|
|
30
|
+
|
|
31
|
+
Mapped BOE event callbacks receive the actual DOM event; `preventDefault` and
|
|
32
|
+
`stopPropagation` act on it. Cancellation works when the underlying event is
|
|
33
|
+
cancelable; BOE Dialog's `cancel` notification itself is not cancelable. Factory
|
|
34
|
+
`events` maps preserve exact spelling/case, and listeners are removed when the
|
|
35
|
+
framework owner unmounts. Temporary DOM disconnect/reconnect retains the owner.
|
|
36
|
+
The wrappers retain BOE's optional-property semantics: omitted scalar props leave
|
|
37
|
+
the element value alone; disabled/invalid default to false. Use explicit false,
|
|
38
|
+
empty string, or empty array to clear controlled values. BOE Select intentionally
|
|
39
|
+
reflects its assigned options array as JSON. Host attributes, children, and slots
|
|
40
|
+
remain framework-native. Use exact camelCase property names for custom-element
|
|
41
|
+
setters; other host props use Preact's normal attribute handling. Boolean wrapper
|
|
42
|
+
props take true/false, while `aria-*` and `data-*` false values remain string
|
|
43
|
+
attributes. If you need an attribute distinct from a custom-element setter, use
|
|
44
|
+
the forwarded ref's `setAttribute`/`removeAttribute` in a lifecycle hook; Preact
|
|
45
|
+
has no Solid-style attribute-prefix API.
|
|
46
|
+
|
|
47
|
+
SSR imports require no DOM globals. Server rendering emits inert custom-element
|
|
48
|
+
hosts and light-DOM children; controlled properties and arrays are assigned on
|
|
49
|
+
client hydration, so do not expect populated shadow DOM before JavaScript runs.
|
|
50
|
+
Mapped listeners attach before initial property sync, including synchronous
|
|
51
|
+
`open-changed` notifications from initially-open Dialog and Drawer. Browser
|
|
52
|
+
fixtures cover closed-first and initially-open client/hydrated rendering.
|
|
53
|
+
Hydrate with native `preact.hydrate` and use `preact-render-to-string` for server
|
|
54
|
+
rendering. Deduplicate Preact in bundlers to keep renderer/hooks on one runtime.
|
|
55
|
+
|
|
56
|
+
Validation: `bun run frameworks:preact`, `bun run frameworks:preact:ssr`,
|
|
57
|
+
`bun run test -- packages/preact`, `bun run frameworks:jsx:browser`, and
|
|
58
|
+
`bun run adapters:pack-check`. See [support status](../../docs/integration/framework-adapters.md)
|
|
59
|
+
and [release requirements](../../RELEASING.md).
|
|
60
|
+
|
|
61
|
+
Design references: [Preact 11 custom elements](https://preactjs.com/guide/v11/web-components/) and
|
|
62
|
+
[native SSR](https://preactjs.com/guide/v11/server-side-rendering/).
|
|
63
|
+
The installed 11.0.1 `create-element` source preserves refs in function-component
|
|
64
|
+
props, and native callback refs support cleanup functions. A dedicated native
|
|
65
|
+
adapter avoids React peer/type/event contracts and does not depend on compat.
|
package/dist/button.d.ts
ADDED
|
@@ -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
|
+
}>;
|
package/dist/combobox.js
ADDED
|
@@ -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
|
+
}
|
package/dist/dialog.d.ts
ADDED
|
@@ -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
|
+
});
|
package/dist/drawer.d.ts
ADDED
|
@@ -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
|
+
});
|
package/dist/events.d.ts
ADDED
|
@@ -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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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";
|
package/dist/select.d.ts
ADDED
|
@@ -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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.42.2",
|
|
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.2",
|
|
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
|
+
}
|