@expressive/ui 0.0.0-stage → 0.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.
- package/LICENSE +21 -0
- package/README.md +48 -2
- package/dist/button/base.d.ts +63 -0
- package/dist/button/base.js +90 -0
- package/dist/button/base.js.map +1 -0
- package/dist/button/button.d.ts +56 -0
- package/dist/button/button.js +138 -0
- package/dist/button/button.js.map +1 -0
- package/dist/calendar/base.d.ts +112 -0
- package/dist/calendar/base.js +235 -0
- package/dist/calendar/base.js.map +1 -0
- package/dist/calendar/calendar.d.ts +78 -0
- package/dist/calendar/calendar.js +145 -0
- package/dist/calendar/calendar.js.map +1 -0
- package/dist/calendar/date.js +56 -0
- package/dist/calendar/date.js.map +1 -0
- package/dist/dialog/base.d.ts +74 -0
- package/dist/dialog/base.js +143 -0
- package/dist/dialog/base.js.map +1 -0
- package/dist/dialog/dialog.d.ts +69 -0
- package/dist/dialog/dialog.js +108 -0
- package/dist/dialog/dialog.js.map +1 -0
- package/dist/dom.d.ts +16 -0
- package/dist/dom.js +17 -0
- package/dist/dropzone/base.d.ts +77 -0
- package/dist/dropzone/base.js +159 -0
- package/dist/dropzone/base.js.map +1 -0
- package/dist/dropzone/dropzone.d.ts +38 -0
- package/dist/dropzone/dropzone.js +92 -0
- package/dist/dropzone/dropzone.js.map +1 -0
- package/dist/form/base.d.ts +157 -0
- package/dist/form/base.js +243 -0
- package/dist/form/base.js.map +1 -0
- package/dist/form/form.d.ts +123 -0
- package/dist/form/form.js +179 -0
- package/dist/form/form.js.map +1 -0
- package/dist/icons/caret.d.ts +8 -0
- package/dist/icons/caret.js +16 -0
- package/dist/icons/caret.js.map +1 -0
- package/dist/icons/check.d.ts +8 -0
- package/dist/icons/check.js +16 -0
- package/dist/icons/check.js.map +1 -0
- package/dist/icons/cross.d.ts +8 -0
- package/dist/icons/cross.js +16 -0
- package/dist/icons/cross.js.map +1 -0
- package/dist/icons/icon.js +16 -0
- package/dist/icons/icon.js.map +1 -0
- package/dist/icons/spinner.d.ts +8 -0
- package/dist/icons/spinner.js +27 -0
- package/dist/icons/spinner.js.map +1 -0
- package/dist/icons/upload.d.ts +8 -0
- package/dist/icons/upload.js +16 -0
- package/dist/icons/upload.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +12 -0
- package/dist/layout/base.d.ts +88 -0
- package/dist/layout/base.js +158 -0
- package/dist/layout/base.js.map +1 -0
- package/dist/layout/layout.d.ts +53 -0
- package/dist/layout/layout.js +80 -0
- package/dist/layout/layout.js.map +1 -0
- package/dist/piece.js +13 -0
- package/dist/piece.js.map +1 -0
- package/dist/popover/base.d.ts +94 -0
- package/dist/popover/base.js +176 -0
- package/dist/popover/base.js.map +1 -0
- package/dist/popover/place.d.ts +12 -0
- package/dist/popover/place.js +32 -0
- package/dist/popover/place.js.map +1 -0
- package/dist/popover/popover.d.ts +52 -0
- package/dist/popover/popover.js +104 -0
- package/dist/popover/popover.js.map +1 -0
- package/dist/select/base.d.ts +196 -0
- package/dist/select/base.js +409 -0
- package/dist/select/base.js.map +1 -0
- package/dist/select/select.d.ts +119 -0
- package/dist/select/select.js +219 -0
- package/dist/select/select.js.map +1 -0
- package/dist/table/base.d.ts +96 -0
- package/dist/table/base.js +154 -0
- package/dist/table/base.js.map +1 -0
- package/dist/table/column.d.ts +53 -0
- package/dist/table/column.js +29 -0
- package/dist/table/column.js.map +1 -0
- package/dist/table/table.d.ts +41 -0
- package/dist/table/table.js +101 -0
- package/dist/table/table.js.map +1 -0
- package/package.json +76 -4
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/popover/place.ts
|
|
2
|
+
/**
|
|
3
|
+
* Center a box on the anchor's preferred side, flipping when only the other side fits, and keep it inside the
|
|
4
|
+
* view. The arrow tracks the anchor's center while staying on the box.
|
|
5
|
+
*/
|
|
6
|
+
function place(options) {
|
|
7
|
+
const { anchor, width, height, view, side: prefer = "below", gap = 8, pad = 8, inset = 12 } = options;
|
|
8
|
+
const minLeft = view.left + pad;
|
|
9
|
+
const maxLeft = view.left + view.width - pad - width;
|
|
10
|
+
const center = anchor.left + anchor.width / 2;
|
|
11
|
+
const left = maxLeft < minLeft ? minLeft : Math.min(Math.max(center - width / 2, minLeft), maxLeft);
|
|
12
|
+
const below = anchor.top + anchor.height + gap;
|
|
13
|
+
const above = anchor.top - gap - height;
|
|
14
|
+
const fits = {
|
|
15
|
+
below: below + height <= view.top + view.height - pad,
|
|
16
|
+
above: above >= view.top + pad
|
|
17
|
+
};
|
|
18
|
+
const other = prefer == "below" ? "above" : "below";
|
|
19
|
+
const side = fits[prefer] || !fits[other] ? prefer : other;
|
|
20
|
+
const top = side == "below" ? below : Math.max(above, view.top + pad);
|
|
21
|
+
const edge = Math.min(inset, width / 2);
|
|
22
|
+
return {
|
|
23
|
+
left,
|
|
24
|
+
top,
|
|
25
|
+
side,
|
|
26
|
+
arrow: Math.min(Math.max(center - left, edge), width - edge)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
export { place };
|
|
32
|
+
//# sourceMappingURL=place.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"place.js","names":[],"sources":["../../src/popover/place.ts"],"sourcesContent":["export interface Rect {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\nexport type Side = 'below' | 'above';\n\nexport interface Place {\n left: number;\n top: number;\n side: Side;\n /** Arrow center from the box's left edge. */\n arrow: number;\n}\n\n/**\n * Center a box on the anchor's preferred side, flipping when only the other side fits, and keep it inside the\n * view. The arrow tracks the anchor's center while staying on the box.\n */\nexport function place(options: {\n anchor: Rect;\n width: number;\n height: number;\n view: Rect;\n side?: Side;\n gap?: number;\n pad?: number;\n inset?: number;\n}): Place {\n const { anchor, width, height, view, side: prefer = 'below', gap = 8, pad = 8, inset = 12 } = options;\n\n const minLeft = view.left + pad;\n const maxLeft = view.left + view.width - pad - width;\n const center = anchor.left + anchor.width / 2;\n const left = maxLeft < minLeft ? minLeft : Math.min(Math.max(center - width / 2, minLeft), maxLeft);\n\n const below = anchor.top + anchor.height + gap;\n const above = anchor.top - gap - height;\n const fits = {\n below: below + height <= view.top + view.height - pad,\n above: above >= view.top + pad\n };\n const other: Side = prefer == 'below' ? 'above' : 'below';\n const side = fits[prefer] || !fits[other] ? prefer : other;\n const top = side == 'below' ? below : Math.max(above, view.top + pad);\n\n const edge = Math.min(inset, width / 2);\n const arrow = Math.min(Math.max(center - left, edge), width - edge);\n\n return { left, top, side, arrow };\n}\n"],"mappings":";;;;;AAqBA,SAAgB,MAAM,SASZ;CACR,MAAM,EAAE,QAAQ,OAAO,QAAQ,MAAM,MAAM,SAAS,SAAS,MAAM,GAAG,MAAM,GAAG,QAAQ,OAAO;CAE9F,MAAM,UAAU,KAAK,OAAO;CAC5B,MAAM,UAAU,KAAK,OAAO,KAAK,QAAQ,MAAM;CAC/C,MAAM,SAAS,OAAO,OAAO,OAAO,QAAQ;CAC5C,MAAM,OAAO,UAAU,UAAU,UAAU,KAAK,IAAI,KAAK,IAAI,SAAS,QAAQ,GAAG,QAAQ,EAAE,QAAQ;CAEnG,MAAM,QAAQ,OAAO,MAAM,OAAO,SAAS;CAC3C,MAAM,QAAQ,OAAO,MAAM,MAAM;CACjC,MAAM,OAAO;EACX,OAAO,QAAQ,UAAU,KAAK,MAAM,KAAK,SAAS;EAClD,OAAO,SAAS,KAAK,MAAM;EAC5B;CACD,MAAM,QAAc,UAAU,UAAU,UAAU;CAClD,MAAM,OAAO,KAAK,WAAW,CAAC,KAAK,SAAS,SAAS;CACrD,MAAM,MAAM,QAAQ,UAAU,QAAQ,KAAK,IAAI,OAAO,KAAK,MAAM,IAAI;CAErE,MAAM,OAAO,KAAK,IAAI,OAAO,QAAQ,EAAE;AAGvC,QAAO;EAAE;EAAM;EAAK;EAAM,OAFZ,KAAK,IAAI,KAAK,IAAI,SAAS,MAAM,KAAK,EAAE,QAAQ,KAAK;EAElC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Popover as Popover$1 } from "./base.js";
|
|
2
|
+
import { Component } from "@expressive/mvc";
|
|
3
|
+
|
|
4
|
+
//#region src/popover/popover.d.ts
|
|
5
|
+
declare namespace Popover {
|
|
6
|
+
type Place = Popover$1.Place;
|
|
7
|
+
type Side = Popover$1.Side;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Button that opens a panel anchored to it.
|
|
11
|
+
*
|
|
12
|
+
* The panel portals to the body, sits on the preferred `side` and flips when only the other fits, staying inside
|
|
13
|
+
* the viewport as it scrolls and resizes; its arrow tracks the button. Escape closes it and returns focus to the
|
|
14
|
+
* button; a press outside or focus leaving both closes it. Not modal - the page stays usable. Pieces - `Trigger`,
|
|
15
|
+
* `Layer`, `Panel`, `Arrow` - are function components reading `Popover.get()`, each styled on its own; pass one as a
|
|
16
|
+
* prop (`<Popover Arrow={…} />`) or assign it in a subclass to replace it. Stock pieces are statics. A replacement
|
|
17
|
+
* `Trigger` spreads `triggerProps`, a replacement `Panel` `panelProps`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* <Popover label="Filters">
|
|
21
|
+
* <Checkbox … />
|
|
22
|
+
* </Popover>
|
|
23
|
+
*/
|
|
24
|
+
declare class Popover extends Popover$1 {
|
|
25
|
+
static Trigger: () => Component.Node;
|
|
26
|
+
static Layer: ({
|
|
27
|
+
children
|
|
28
|
+
}: {
|
|
29
|
+
children: Component.Node;
|
|
30
|
+
}) => Component.Node;
|
|
31
|
+
static Panel: ({
|
|
32
|
+
children
|
|
33
|
+
}: {
|
|
34
|
+
children: Component.Node;
|
|
35
|
+
}) => Component.Node;
|
|
36
|
+
static Arrow: () => Component.Node;
|
|
37
|
+
Trigger: () => Component.Node;
|
|
38
|
+
Layer: ({
|
|
39
|
+
children
|
|
40
|
+
}: {
|
|
41
|
+
children: Component.Node;
|
|
42
|
+
}) => Component.Node;
|
|
43
|
+
Panel: ({
|
|
44
|
+
children
|
|
45
|
+
}: {
|
|
46
|
+
children: Component.Node;
|
|
47
|
+
}) => Component.Node;
|
|
48
|
+
Arrow: () => Component.Node;
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { Popover };
|
|
52
|
+
//# sourceMappingURL=popover.d.ts.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Popover as Popover$1 } from "./base.js";
|
|
2
|
+
import { Caret } from "../icons/caret.js";
|
|
3
|
+
import { Portal, style } from "@expressive/dom";
|
|
4
|
+
import { jsx, jsxs } from "@expressive/dom/jsx-runtime";
|
|
5
|
+
|
|
6
|
+
//#region src/popover/popover.tsx
|
|
7
|
+
const Trigger = () => {
|
|
8
|
+
const { label, open, triggerProps } = Popover$1.get();
|
|
9
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
10
|
+
...triggerProps,
|
|
11
|
+
children: [label, /* @__PURE__ */ jsx(Caret, { _open: open })]
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
style(Trigger, {
|
|
15
|
+
display: "inline-flex",
|
|
16
|
+
alignItems: "center",
|
|
17
|
+
gap: "6px",
|
|
18
|
+
height: "36px",
|
|
19
|
+
padding: "0 10px 0 14px",
|
|
20
|
+
border: "1px solid var(--ui-line, #ccc)",
|
|
21
|
+
borderRadius: "6px",
|
|
22
|
+
background: "var(--ui-surface, #fff)",
|
|
23
|
+
color: "inherit",
|
|
24
|
+
font: "inherit",
|
|
25
|
+
cursor: "pointer",
|
|
26
|
+
":hover": { background: "var(--ui-highlight, #deebff)" },
|
|
27
|
+
":focus-visible": {
|
|
28
|
+
outline: "2px solid var(--ui-accent, #2684ff)",
|
|
29
|
+
outlineOffset: "2px"
|
|
30
|
+
},
|
|
31
|
+
_open: { transform: "rotate(180deg)" }
|
|
32
|
+
});
|
|
33
|
+
const Layer = ({ children }) => /* @__PURE__ */ jsx(Portal, {
|
|
34
|
+
into: "body",
|
|
35
|
+
children
|
|
36
|
+
});
|
|
37
|
+
const Panel = ({ children }) => /* @__PURE__ */ jsx("div", {
|
|
38
|
+
...Popover$1.get().panelProps,
|
|
39
|
+
children
|
|
40
|
+
});
|
|
41
|
+
style(Panel, {
|
|
42
|
+
position: "fixed",
|
|
43
|
+
zIndex: "1000",
|
|
44
|
+
boxSizing: "border-box",
|
|
45
|
+
maxWidth: "calc(100vw - 16px)",
|
|
46
|
+
padding: "12px 14px",
|
|
47
|
+
background: "var(--ui-surface, #fff)",
|
|
48
|
+
color: "inherit",
|
|
49
|
+
border: "1px solid var(--ui-line, #ccc)",
|
|
50
|
+
borderRadius: "8px",
|
|
51
|
+
boxShadow: "0 6px 24px rgb(0 0 0 / 0.12)",
|
|
52
|
+
outline: "0"
|
|
53
|
+
});
|
|
54
|
+
const Arrow = () => {
|
|
55
|
+
const { above, props } = Popover$1.get().arrowProps;
|
|
56
|
+
return /* @__PURE__ */ jsx("span", {
|
|
57
|
+
...props,
|
|
58
|
+
_above: above
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
style(Arrow, {
|
|
62
|
+
position: "absolute",
|
|
63
|
+
top: "-6px",
|
|
64
|
+
width: "10px",
|
|
65
|
+
height: "10px",
|
|
66
|
+
background: "inherit",
|
|
67
|
+
borderTop: "1px solid var(--ui-line, #ccc)",
|
|
68
|
+
borderLeft: "1px solid var(--ui-line, #ccc)",
|
|
69
|
+
transform: "rotate(45deg)",
|
|
70
|
+
_above: {
|
|
71
|
+
top: "auto",
|
|
72
|
+
bottom: "-6px",
|
|
73
|
+
transform: "rotate(225deg)"
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
/**
|
|
77
|
+
* Button that opens a panel anchored to it.
|
|
78
|
+
*
|
|
79
|
+
* The panel portals to the body, sits on the preferred `side` and flips when only the other fits, staying inside
|
|
80
|
+
* the viewport as it scrolls and resizes; its arrow tracks the button. Escape closes it and returns focus to the
|
|
81
|
+
* button; a press outside or focus leaving both closes it. Not modal - the page stays usable. Pieces - `Trigger`,
|
|
82
|
+
* `Layer`, `Panel`, `Arrow` - are function components reading `Popover.get()`, each styled on its own; pass one as a
|
|
83
|
+
* prop (`<Popover Arrow={…} />`) or assign it in a subclass to replace it. Stock pieces are statics. A replacement
|
|
84
|
+
* `Trigger` spreads `triggerProps`, a replacement `Panel` `panelProps`.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* <Popover label="Filters">
|
|
88
|
+
* <Checkbox … />
|
|
89
|
+
* </Popover>
|
|
90
|
+
*/
|
|
91
|
+
var Popover = class extends Popover$1 {
|
|
92
|
+
static Trigger = Trigger;
|
|
93
|
+
static Layer = Layer;
|
|
94
|
+
static Panel = Panel;
|
|
95
|
+
static Arrow = Arrow;
|
|
96
|
+
Trigger = Trigger;
|
|
97
|
+
Layer = Layer;
|
|
98
|
+
Panel = Panel;
|
|
99
|
+
Arrow = Arrow;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
//#endregion
|
|
103
|
+
export { Popover };
|
|
104
|
+
//# sourceMappingURL=popover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover.js","names":["Base.Popover"],"sources":["../../src/popover/popover.tsx"],"sourcesContent":["/** @jsxImportSource @expressive/dom */\nimport { Component } from '@expressive/mvc';\nimport { Portal, style } from '@expressive/dom';\n\nimport { Caret } from '../icons';\nimport * as Base from './base';\n\ndeclare namespace Popover {\n type Place = Base.Popover.Place;\n type Side = Base.Popover.Side;\n}\n\nconst Trigger = (): Component.Node => {\n const { label, open, triggerProps } = Base.Popover.get();\n\n return (\n <button {...triggerProps}>\n {label}\n <Caret _open={open} />\n </button>\n );\n};\n\nstyle(Trigger, {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '6px',\n height: '36px',\n padding: '0 10px 0 14px',\n border: '1px solid var(--ui-line, #ccc)',\n borderRadius: '6px',\n background: 'var(--ui-surface, #fff)',\n color: 'inherit',\n font: 'inherit',\n cursor: 'pointer',\n ':hover': {\n background: 'var(--ui-highlight, #deebff)'\n },\n ':focus-visible': {\n outline: '2px solid var(--ui-accent, #2684ff)',\n outlineOffset: '2px'\n },\n _open: {\n transform: 'rotate(180deg)'\n }\n});\n\nconst Layer = ({ children }: { children: Component.Node }): Component.Node => <Portal into=\"body\">{children}</Portal>;\n\nconst Panel = ({ children }: { children: Component.Node }): Component.Node => (\n <div {...Base.Popover.get().panelProps}>{children}</div>\n);\n\nstyle(Panel, {\n position: 'fixed',\n zIndex: '1000',\n boxSizing: 'border-box',\n maxWidth: 'calc(100vw - 16px)',\n padding: '12px 14px',\n background: 'var(--ui-surface, #fff)',\n color: 'inherit',\n border: '1px solid var(--ui-line, #ccc)',\n borderRadius: '8px',\n boxShadow: '0 6px 24px rgb(0 0 0 / 0.12)',\n outline: '0'\n});\n\nconst Arrow = (): Component.Node => {\n const { above, props } = Base.Popover.get().arrowProps;\n\n return <span {...props} _above={above} />;\n};\n\nstyle(Arrow, {\n position: 'absolute',\n top: '-6px',\n width: '10px',\n height: '10px',\n background: 'inherit',\n borderTop: '1px solid var(--ui-line, #ccc)',\n borderLeft: '1px solid var(--ui-line, #ccc)',\n transform: 'rotate(45deg)',\n _above: {\n top: 'auto',\n bottom: '-6px',\n transform: 'rotate(225deg)'\n }\n});\n\n/**\n * Button that opens a panel anchored to it.\n *\n * The panel portals to the body, sits on the preferred `side` and flips when only the other fits, staying inside\n * the viewport as it scrolls and resizes; its arrow tracks the button. Escape closes it and returns focus to the\n * button; a press outside or focus leaving both closes it. Not modal - the page stays usable. Pieces - `Trigger`,\n * `Layer`, `Panel`, `Arrow` - are function components reading `Popover.get()`, each styled on its own; pass one as a\n * prop (`<Popover Arrow={…} />`) or assign it in a subclass to replace it. Stock pieces are statics. A replacement\n * `Trigger` spreads `triggerProps`, a replacement `Panel` `panelProps`.\n *\n * @example\n * <Popover label=\"Filters\">\n * <Checkbox … />\n * </Popover>\n */\nclass Popover extends Base.Popover {\n static Trigger = Trigger;\n static Layer = Layer;\n static Panel = Panel;\n static Arrow = Arrow;\n\n Trigger = Trigger;\n Layer = Layer;\n Panel = Panel;\n Arrow = Arrow;\n}\n\nexport { Arrow, Layer, Panel, Popover, Trigger };\n"],"mappings":";;;;;;AAYA,MAAM,gBAAgC;CACpC,MAAM,EAAE,OAAO,MAAM,2BAA8B,KAAK;AAExD,QACE,qBAAC;EAAO,GAAI;aACT,OACD,oBAAC,SAAM,OAAO,OAAQ;GACf;;AAIb,MAAM,SAAS;CACb,SAAS;CACT,YAAY;CACZ,KAAK;CACL,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,cAAc;CACd,YAAY;CACZ,OAAO;CACP,MAAM;CACN,QAAQ;CACR,UAAU,EACR,YAAY,gCACb;CACD,kBAAkB;EAChB,SAAS;EACT,eAAe;EAChB;CACD,OAAO,EACL,WAAW,kBACZ;CACF,CAAC;AAEF,MAAM,SAAS,EAAE,eAA6D,oBAAC;CAAO,MAAK;CAAQ;EAAkB;AAErH,MAAM,SAAS,EAAE,eACf,oBAAC;CAAI,aAAiB,KAAK,CAAC;CAAa;EAAe;AAG1D,MAAM,OAAO;CACX,UAAU;CACV,QAAQ;CACR,WAAW;CACX,UAAU;CACV,SAAS;CACT,YAAY;CACZ,OAAO;CACP,QAAQ;CACR,cAAc;CACd,WAAW;CACX,SAAS;CACV,CAAC;AAEF,MAAM,cAA8B;CAClC,MAAM,EAAE,OAAO,oBAAuB,KAAK,CAAC;AAE5C,QAAO,oBAAC;EAAK,GAAI;EAAO,QAAQ;GAAS;;AAG3C,MAAM,OAAO;CACX,UAAU;CACV,KAAK;CACL,OAAO;CACP,QAAQ;CACR,YAAY;CACZ,WAAW;CACX,YAAY;CACZ,WAAW;CACX,QAAQ;EACN,KAAK;EACL,QAAQ;EACR,WAAW;EACZ;CACF,CAAC;;;;;;;;;;;;;;;;AAiBF,IAAM,UAAN,cAAsBA,UAAa;CACjC,OAAO,UAAU;CACjB,OAAO,QAAQ;CACf,OAAO,QAAQ;CACf,OAAO,QAAQ;CAEf,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,QAAQ"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { Component, Model, ref } from "@expressive/mvc";
|
|
2
|
+
|
|
3
|
+
//#region src/select/base.d.ts
|
|
4
|
+
declare namespace Select {
|
|
5
|
+
interface Option<T = unknown> {
|
|
6
|
+
value: T;
|
|
7
|
+
label: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** One option, or an array when `multi`. */
|
|
11
|
+
type Value = Option | Option[] | null;
|
|
12
|
+
type Key = Pick<KeyboardEvent, 'key' | 'shiftKey' | 'isComposing' | 'preventDefault' | 'stopPropagation'>;
|
|
13
|
+
/** Where the menu sits, in viewport pixels; `above` when it flipped over the control. */
|
|
14
|
+
interface Place {
|
|
15
|
+
left: number;
|
|
16
|
+
top: number;
|
|
17
|
+
width: number;
|
|
18
|
+
above: boolean;
|
|
19
|
+
}
|
|
20
|
+
type Wrap = (props: {
|
|
21
|
+
children: Component.Node;
|
|
22
|
+
}) => Component.Node;
|
|
23
|
+
type Each = (props: {
|
|
24
|
+
option: Option;
|
|
25
|
+
}) => Component.Node;
|
|
26
|
+
type Item = (props: {
|
|
27
|
+
option: Option;
|
|
28
|
+
index: number;
|
|
29
|
+
}) => Component.Node;
|
|
30
|
+
type Part = () => Component.Node;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Combobox behavior without a look: state, keyboard, announcements and ARIA, assembled from pieces.
|
|
34
|
+
*
|
|
35
|
+
* Every visible piece is a placeholder that throws when rendered - a skin assigns each one it renders.
|
|
36
|
+
* Pieces spread the matching `*Props` getter onto their element to stay accessible. `Layer` wraps the menu and renders
|
|
37
|
+
* in place by default; a skin replaces it to move the menu out of clipping containers, placed at `place`.
|
|
38
|
+
*/
|
|
39
|
+
declare class Select extends Component {
|
|
40
|
+
options: readonly Select.Option[];
|
|
41
|
+
value: Select.Value;
|
|
42
|
+
onChange?: (value: Select.Value) => void;
|
|
43
|
+
multi: boolean;
|
|
44
|
+
/** Whether Backspace and `clear` remove values. Defaults to `multi`. */
|
|
45
|
+
clearable?: boolean;
|
|
46
|
+
searchable: boolean;
|
|
47
|
+
disabled: boolean;
|
|
48
|
+
placeholder: string;
|
|
49
|
+
/** The input's id, for a `<label htmlFor>`; prefixes the listbox and option ids. */
|
|
50
|
+
id: string;
|
|
51
|
+
query: string;
|
|
52
|
+
open: boolean;
|
|
53
|
+
focused: boolean;
|
|
54
|
+
highlighted: number;
|
|
55
|
+
message: string;
|
|
56
|
+
input: ref.Object<HTMLInputElement>;
|
|
57
|
+
/** Where the menu sits, once measured. */
|
|
58
|
+
place?: Select.Place;
|
|
59
|
+
Root: Select.Wrap;
|
|
60
|
+
Control: Select.Wrap;
|
|
61
|
+
Values: Select.Wrap;
|
|
62
|
+
Chip: Select.Each;
|
|
63
|
+
Value: Select.Each;
|
|
64
|
+
Input: Select.Part;
|
|
65
|
+
Clear: Select.Part;
|
|
66
|
+
Toggle: Select.Part;
|
|
67
|
+
Layer: Select.Wrap;
|
|
68
|
+
Menu: Select.Wrap;
|
|
69
|
+
Option: Select.Item;
|
|
70
|
+
Empty: Select.Part;
|
|
71
|
+
Live: Select.Part;
|
|
72
|
+
constructor(...args: Model.Args);
|
|
73
|
+
get selection(): Select.Option[];
|
|
74
|
+
/** Options matching `query`, accent- and case-insensitive; skips disabled and, when `multi`, chosen ones. */
|
|
75
|
+
get filtered(): Select.Option[];
|
|
76
|
+
/** The highlighted option while open. */
|
|
77
|
+
get active(): Select.Option | undefined;
|
|
78
|
+
get canClear(): boolean;
|
|
79
|
+
/** Text for the `aria-live` region; empty while unfocused. */
|
|
80
|
+
get announcement(): string;
|
|
81
|
+
/** For the element wrapping values and buttons: a press anywhere focuses the input and opens. */
|
|
82
|
+
get controlProps(): {
|
|
83
|
+
ref: (element: HTMLElement | null) => void;
|
|
84
|
+
onMouseDown: (event: MouseEvent) => void;
|
|
85
|
+
};
|
|
86
|
+
get inputProps(): {
|
|
87
|
+
readonly ref: ref.Object<HTMLInputElement>;
|
|
88
|
+
readonly id: string;
|
|
89
|
+
readonly role: "combobox";
|
|
90
|
+
readonly 'aria-autocomplete': "list";
|
|
91
|
+
readonly 'aria-expanded': boolean;
|
|
92
|
+
readonly 'aria-controls': `${string}-listbox`;
|
|
93
|
+
readonly 'aria-activedescendant': string | undefined;
|
|
94
|
+
readonly autoComplete: "off";
|
|
95
|
+
readonly disabled: boolean;
|
|
96
|
+
readonly readOnly: boolean;
|
|
97
|
+
readonly placeholder: string;
|
|
98
|
+
readonly value: string;
|
|
99
|
+
readonly onInput: (event: Event) => void;
|
|
100
|
+
readonly onFocus: () => void;
|
|
101
|
+
readonly onBlur: () => void;
|
|
102
|
+
readonly onKeyDown: (event: Select.Key) => void;
|
|
103
|
+
};
|
|
104
|
+
get clearProps(): {
|
|
105
|
+
readonly type: "button";
|
|
106
|
+
readonly tabIndex: -1;
|
|
107
|
+
readonly 'aria-label': "Clear selection";
|
|
108
|
+
readonly onMouseDown: typeof hold;
|
|
109
|
+
readonly onClick: () => void;
|
|
110
|
+
};
|
|
111
|
+
get toggleProps(): {
|
|
112
|
+
readonly type: "button";
|
|
113
|
+
readonly tabIndex: -1;
|
|
114
|
+
readonly 'aria-label': "Hide options" | "Show options";
|
|
115
|
+
readonly onMouseDown: typeof hold;
|
|
116
|
+
readonly onClick: () => void;
|
|
117
|
+
};
|
|
118
|
+
/** For a chip's remove button. */
|
|
119
|
+
get removeProps(): (option: Select.Option) => {
|
|
120
|
+
readonly type: "button";
|
|
121
|
+
readonly tabIndex: -1;
|
|
122
|
+
readonly 'aria-label': `Remove ${string}`;
|
|
123
|
+
readonly onMouseDown: typeof hold;
|
|
124
|
+
readonly onClick: () => void;
|
|
125
|
+
};
|
|
126
|
+
/** For the menu: hidden at the origin until measured, then at `place`, as wide as the control. */
|
|
127
|
+
get menuProps(): {
|
|
128
|
+
readonly ref: (element: HTMLElement | null) => void;
|
|
129
|
+
readonly id: `${string}-listbox`;
|
|
130
|
+
readonly role: "listbox";
|
|
131
|
+
readonly 'aria-multiselectable': boolean;
|
|
132
|
+
readonly onMouseDown: typeof hold;
|
|
133
|
+
readonly style: {
|
|
134
|
+
left: string;
|
|
135
|
+
top: string;
|
|
136
|
+
width: string;
|
|
137
|
+
opacity?: undefined;
|
|
138
|
+
} | {
|
|
139
|
+
left: string;
|
|
140
|
+
top: string;
|
|
141
|
+
opacity: string;
|
|
142
|
+
width?: undefined;
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
/** For an option's element, with `active` and `selected` for styling. */
|
|
146
|
+
get optionProps(): (option: Select.Option, index: number) => {
|
|
147
|
+
active: boolean;
|
|
148
|
+
selected: boolean;
|
|
149
|
+
props: {
|
|
150
|
+
readonly ref: ((element: HTMLElement | null) => void) | undefined;
|
|
151
|
+
readonly id: `${string}-option-${number}`;
|
|
152
|
+
readonly role: "option";
|
|
153
|
+
readonly 'aria-selected': boolean;
|
|
154
|
+
readonly onMouseMove: () => number;
|
|
155
|
+
readonly onClick: () => void;
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
isSelected(option: Select.Option): boolean;
|
|
159
|
+
choose(option: Select.Option): void;
|
|
160
|
+
remove(option: Select.Option): void;
|
|
161
|
+
clear(): void;
|
|
162
|
+
search(query: string): void;
|
|
163
|
+
/** Open, highlighting the current choice - else the first option, or the last when `last`. */
|
|
164
|
+
show(last?: boolean): void;
|
|
165
|
+
hide(): void;
|
|
166
|
+
toggle(): void;
|
|
167
|
+
focus(): void;
|
|
168
|
+
blur(): void;
|
|
169
|
+
/**
|
|
170
|
+
* Arrows, Home/End, PageUp/PageDown move the highlight; Enter, Space and Tab choose;
|
|
171
|
+
* Escape closes; Backspace removes the last value.
|
|
172
|
+
*
|
|
173
|
+
* Calls `preventDefault` and `stopPropagation` on keys it consumes. Tab and Backspace pass through.
|
|
174
|
+
*/
|
|
175
|
+
keyDown(event: Select.Key): void;
|
|
176
|
+
/** Focus the input and open, from a press anywhere on the control. */
|
|
177
|
+
press(event: MouseEvent): void;
|
|
178
|
+
/** Follow scroll and resize while open, keeping the menu at the control; returns the release. Defaults to `window`. */
|
|
179
|
+
protected listen(): () => void;
|
|
180
|
+
/** Where the menu fits by the control, or `undefined` before both mount. Defaults to the DOM's layout. */
|
|
181
|
+
protected measure(): Select.Place | undefined;
|
|
182
|
+
/** Re-measure and move the menu when its place changed; for a `listen` that follows layout. */
|
|
183
|
+
protected reposition(): void;
|
|
184
|
+
/** Ref for the control, in `controlProps`; the menu anchors to it. */
|
|
185
|
+
protected anchor(element: HTMLElement | null): void;
|
|
186
|
+
/** Ref for the menu, in `menuProps`; places it once mounted. */
|
|
187
|
+
protected mount(element: HTMLElement | null): void;
|
|
188
|
+
/** Ref for the highlighted option, in `optionProps`; keeps it in view. Defaults to `scrollIntoView`. */
|
|
189
|
+
protected reveal(element: HTMLElement | null): void;
|
|
190
|
+
private update;
|
|
191
|
+
render(): Component.Node;
|
|
192
|
+
}
|
|
193
|
+
declare function hold(event: MouseEvent): void;
|
|
194
|
+
//#endregion
|
|
195
|
+
export { Select };
|
|
196
|
+
//# sourceMappingURL=base.d.ts.map
|