@plitzi/sdk-elements 0.38.13 → 0.38.14

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/CHANGELOG.md CHANGED
@@ -1,5 +1,158 @@
1
1
  # @plitzi/sdk-elements
2
2
 
3
+ ## 0.38.14
4
+
5
+ ### Patch Changes
6
+
7
+ - 493955a: ## SDK
8
+
9
+ - **An embedded space leaves the page's theme alone** (`@plitzi/plitzi-sdk`, `@plitzi/plitzi-builder`): plitzi-ui's
10
+ `Provider` stamps `dark` on `<html>` by default — and, being `light`, takes it off — so every space mounted with
11
+ `themeScope: 'container'` turned the page around it light, and nothing put it back. The SDK and the builder now leave
12
+ the class to their own theme provider (`applyColorModeClass={false}`).
13
+ - **A space inside a space** (`@plitzi/plitzi-sdk`, `@plitzi/sdk-shared`): one drawn inside another — the `plitziSdk`
14
+ element, a plugin rendering one, the builder's canvas — knows it (`render.enclosed`). It is `container` unless it
15
+ says otherwise, wears its theme on its own root and starts from the page's when its own is `system`, keeps its
16
+ stylesheet to its own root (`@scope` — its `:root` palette and `.dark` rules repainted the outer page, its
17
+ `.plitzi-sdk` rule restyled the outer root), writes no `<title>` (`ownsHead`) and no `theme` cookie.
18
+ - **The `plitziSdk` element draws** (`@plitzi/plitzi-sdk`): it rendered in `widget` mode with no page to show, so it
19
+ drew nothing. A widget with no `currentPageId` now shows the page its home address would. And it takes a space's
20
+ documents as well as a key: `offlineData`, bound whole (an `apiContainer`'s answer, a runtime's route, what
21
+ `plitzi_render` returns) or as `offlineData.schema` and `offlineData.style` from two elements — waiting, without an
22
+ error, for the half that has not answered; one that cannot be drawn is said why, in the console and on the canvas.
23
+ - **`<PlitziSdk>` inside a plugin draws** (`@plitzi/plitzi-sdk`): it mounted a router of its own inside the page's, and
24
+ React Router refused it — `routing: 'memory'` or not. A space inside another is a widget: `raw` becomes `widget`
25
+ there, one page and no router (a frame of its own, `iframe` or `shadow`, is left as asked).
26
+ - **A space inside another is as tall as what it holds** (`@plitzi/plitzi-sdk`): its root was `min-h-screen`, as a
27
+ space that IS the page is, so a card of three lines drawn in a panel took the window's height.
28
+ - **`<PlitziSdk.Plugin component>` takes a component with its own props** (`@plitzi/plitzi-sdk`), as `render()`'s
29
+ plugins do: typed `ComponentPluginFC`, it refused any component that read its attributes.
30
+ - **An edited page keeps its address in step** (`@plitzi/plitzi-sdk`): the routes are built from the pages' own
31
+ elements, and those were read again only when the list of pages changed — an edit to one page (its slug, its flag)
32
+ leaves that list the same, and the routes kept its old address until something else rebuilt them.
33
+ - **Packages declare only what they use** (`@plitzi/sdk-dev-tools`, `@plitzi/sdk-shared`): `@plitzi/sdk-plugins`, which
34
+ the dev tools no longer import, and `terser`, which no build here runs, are no longer dependencies.
35
+ - **An edited page keeps its route up to date** (`@plitzi/plitzi-sdk`): the routes are built from the pages' own
36
+ elements, which were read again only when the list of pages changed — an edit to one page (its slug, its flag) left
37
+ the list the same and the route at the old address. They follow every edit to a page now, and stay the same object
38
+ while no page changes.
39
+ - **`@plitzi/sdk-dev-tools` no longer depends on `@plitzi/sdk-plugins`**, which nothing in it imports.
40
+ - **A dropdown's menu opens inside a card that lifts or clips** (`@plitzi/sdk-elements`, `@plitzi/sdk-shared`): drawn
41
+ inside the dropdown, an ancestor with a `transform` — a card lifting on hover — made itself the containing block of
42
+ the menu's `position: fixed`, and one with `overflow: hidden` cut it off: the menu opened and nobody saw it. The open
43
+ menu and its backdrop are drawn at the space's root (`spaceRootOf`, `@plitzi/sdk-shared/helpers/spaceRoot`); a
44
+ style written for the popup by an ancestor's selector no longer reaches it — its own classes do.
45
+ - **A copy button says when it could not copy** (`@plitzi/sdk-elements`, `@plitzi/sdk-shared`,
46
+ `@plitzi/sdk-interactions`, `@plitzi/sdk-variables`, `@plitzi/sdk-dev-tools`): a page not served over https has no
47
+ clipboard — a phone opening a development server by its address — and a code block's "Copy" threw there. It reads
48
+ "Not copied", and says why. `clipboardWriter` and `copyText` (`@plitzi/sdk-shared/helpers/clipboard`) are the one
49
+ check, the `copyToClipboard` step's too.
50
+
51
+ ## Authoring
52
+ - **`plitziSdk` is authored like any element** (`@plitzi/sdk-authoring`, `@plitzi/sdk-elements`): its declaration moved
53
+ to `@plitzi/sdk-elements` beside the others, so it has a factory — `plitziSdk({ id, bind: { offlineData: 'view.data'
54
+ } })` —, its attributes typed, and `plitzi explain` knows it.
55
+ - **A binding may write part of an attribute** (`@plitzi/sdk-authoring`): `offlineData.schema` writes into
56
+ `offlineData`, as the runtime always did; authoring refused it as an attribute nothing reads.
57
+ - **`choosesChildren`** (`@plitzi/sdk-shared`, `@plitzi/sdk-authoring`): a plugin that shows only some of the elements
58
+ put inside it — a dashboard's panels, a wizard's step — says so in its declaration, and the page checks owe none of
59
+ them on screen; what it draws is still read by the checks of the whole page (images, sideways scroll, legibility).
60
+ `verify` was red on every such page.
61
+
62
+ - **A page says how the document scrolls** (`@plitzi/sdk-elements`, `@plitzi/sdk-authoring`, `@plitzi/sdk-mcp`):
63
+ `documentScroll: 'locked'` holds the document still while the page is open — a dashboard or a board used with a
64
+ finger, where the whole page was dragged and bounced back though nothing in it scrolls — and `'noBounce'` keeps the
65
+ scroll without the rubber band or pull-to-refresh. The page draws the rule itself, so it is in the server's HTML and
66
+ leaves with the page; never inside a widget, a space drawn in another, or the builder's canvas. A Settings select,
67
+ `PageSpec.documentScroll`, `upsertPage`'s `documentScroll` (`null` undoes it) and the page read; an unknown value is
68
+ refused (`attribute-value`). `upsertPage` is now held to the lint of the page it writes, as element writes are.
69
+
70
+ ## MCP
71
+ - **`@plitzi/sdk-mcp/render`** (`@plitzi/sdk-mcp`): `plitzi_render`'s widget render on its own — no MCP server, no SSR,
72
+ no OAuth — for a project whose own agent draws in its own pages: `render`, `renderWidgetShape` to declare it as a
73
+ tool, `renderGuideText` for the agent. `base`, the style of the project's space, brings its tokens, classes and fonts
74
+ to the widget.
75
+
76
+ ## Server
77
+ - **`publicUrl` says where people reach the server** (`@plitzi/sdk-server`): it was `http://127.0.0.1:<port>` whatever
78
+ the server was. It is `https` once `serverOptions.tls` gives it a certificate, and this machine's network address
79
+ with `HOST` open to the network (what a tablet opens, what an OAuth provider sends it back to); `PUBLIC_URL` still
80
+ wins. The network addresses it prints say `https` too.
81
+ - **A server serving TLS reaches itself** (`@plitzi/sdk-server`): with a local certificate (mkcert) Node trusts nothing
82
+ the browsers were told to, so a runtime calling its own MCP failed. Its requests to its own address now connect to its
83
+ listener and are taken when it presents exactly its certificate (`reachOwnServer`, exported from `/runtime`).
84
+ - **A runtime reads compressed answers again** (`@plitzi/sdk-server`): the dispatcher `reachOwnServer` (and the
85
+ platform's `reachSpaceInside`) installs reached every other host with an undici connector that offers HTTP/2, and an
86
+ answer that came back over h2 reached Node's `fetch` without its `content-encoding` — every API that compresses read
87
+ as Brotli bytes. Other hosts are reached over HTTP/1.1 now, as Node's own `fetch` reaches them.
88
+ - **A project's server serves HTTPS from `.env`** (`@plitzi/sdk-server`, `@plitzi/sdk-shared`): `TLS_CERT` and
89
+ `TLS_KEY`, the certificate's and the key's paths — what `plitzi cert` writes — with no code of the project's;
90
+ `serverOptions.tls` still wins. A network address the certificate does not name is said at boot, the machine's
91
+ `.local` name is printed among the addresses when it is named, and a server on loopback alone says how to open it to
92
+ the network.
93
+ - **A session survives a phone on the Wi-Fi** (`@plitzi/sdk-server`): a private network address (`192.168.…`, `10.…`,
94
+ Tailscale's `100.64/10`, IPv6 unique- and link-local) and a machine's mDNS name (`studio.local`) get the local
95
+ cookie profile — `SameSite=Lax`, no `Secure`, no `Domain`. They got `SameSite=None; Secure`, which a browser drops
96
+ over http: nobody could sign in from a tablet. A deployment's own `.local` sub-domains (`app.example.local`) keep the
97
+ strict profile.
98
+ - **Hiding the dev tools on a server over HTTPS hydrates** (`@plitzi/sdk-server`): a server with a certificate speaks
99
+ HTTP/2, which sends no `Host` — only `:authority` — and the cookies named for the origin's port (the dev tools'
100
+ `plitzi_debug_<port>`, forced flags, painted state) were looked for without it. Shift+F12 hid the dev tools in the
101
+ browser, the server rendered them back, and the page failed to hydrate. Every one is read off `requestAuthority`.
102
+ - **A rebuild of plugins says it finished** (`@plitzi/sdk-server`): bundles removed under a running server (`tmp/`
103
+ deleted) were each a warning, "rebuilding…", and their "ready" at a level a project's terminal does not show. It is
104
+ one episode now: said when noticed, and again when the last is built — how many, how long, which failed.
105
+ - **`tmp/dev-server.json` records the scheme** (`@plitzi/sdk-server`): `url` is `https` with a certificate, and the
106
+ file adds the network addresses and the `publicUrl`.
107
+ - **`public/` is asked about every time** (`@plitzi/sdk-server`): it was cached for an hour — a `.js` or `.css` in it
108
+ for a year, `immutable` — so a file a project rewrites while it runs arrived stale. It is `no-cache` now, answered
109
+ `304` by its `ETag` while unchanged; `immutable` stays for what is versioned (the SDK, the plugins).
110
+
111
+ ## CLI
112
+ - **`check`, `shot`, `verify` and `visual` find a server on HTTPS** (`@plitzi/cli`): they rebuilt
113
+ `http://127.0.0.1:<port>`, so a project serving a certificate had no page checked. They read the URL `npm start`
114
+ recorded, and take a local certificate on this machine only. A project's `playwright.config.ts` comes from
115
+ `plitzi upgrade`.
116
+ - **`verify` runs the project's tests** (`@plitzi/cli`): a step `test`, after the lint, when `package.json` has a
117
+ `test` script.
118
+ - **`tsconfig.json` is the project's** (`@plitzi/cli`): the compiler options moved to `plitzi/tsconfig.base.json`, which
119
+ the CLI keeps up and `tsconfig.json` extends — so a folder of the project's own (`scripts/`, `tools/`) goes in its
120
+ `include` without changing a file of the CLI's. `plitzi doctor --fix` makes an older `tsconfig.json` extend it,
121
+ keeping only the options the project changed.
122
+ - **`doctor --fix` and `upgrade` write as the project's Prettier does** (`@plitzi/cli`): a repaired `tsconfig.json` or
123
+ `package.json` left `verify`'s format step red.
124
+ - **`upgrade` renames `usePlitziServiceContext`** (`@plitzi/cli`): with `PlitziServiceProvider` and
125
+ `PlitziServiceContextValue`, renamed in 0.38.12 with no alias — import, use and the module path that named it.
126
+ - **`skills update` compares every file** (`@plitzi/cli`): only `SKILL.md` was, so a change in a reference alone was
127
+ "up to date".
128
+ - **`NOTES.md`, the project's notes for agents** (`@plitzi/cli`): `AGENTS.md` and `CLAUDE.md` are the CLI's and
129
+ `upgrade` replaces them, so what a project's own sessions had to know had nowhere to live. `CLAUDE.md` imports
130
+ `NOTES.md`, `AGENTS.md` sends every agent to it, and nothing the CLI does touches it; `doctor --fix` writes one for
131
+ a project that has none.
132
+
133
+ - **`start:dev-inspect`** (`@plitzi/cli`): a server project's `start:dev` with Node's inspector open on
134
+ 127.0.0.1:9229, so a debugger (VS Code, `chrome://inspect`) attaches to the server, and again after each restart.
135
+ `plitzi upgrade` adds it; `plitzi runtime add` has it restart on the runtime too.
136
+ - **`plitzi cert`** (`@plitzi/cli`): HTTPS while developing, for a phone or a tablet on the Wi-Fi — over http its
137
+ browser gives a page no microphone, camera or clipboard. A certificate from mkcert's local authority for this machine,
138
+ its network addresses and its `.local` name, in `tmp/tls/`, named in `.env`; what to install on the phone, said.
139
+ - **A browser project opens to the network** (`@plitzi/cli`): its `vite.config.ts` reads `HOST`, `TLS_CERT` and
140
+ `TLS_KEY` from `.env`, as a server project does, and writes `tmp/dev-server.json` — the port Vite took and the scheme
141
+ — so `page check`, `page shot` and `visual` find it on another port or over HTTPS. `plitzi upgrade` brings it.
142
+
143
+ ## Docs
144
+ - **Two plugins share through `state`** (`@plitzi/sdk-authoring` skill): each plugin is a bundle of its own, so a module
145
+ both import is two copies, and what one keeps there the other never sees.
146
+ - **`verify --help`** names the project's tests among its steps.
147
+
148
+ - Updated dependencies [493955a]
149
+ - @plitzi/sdk-auth@0.38.14
150
+ - @plitzi/sdk-event-bridge@0.38.14
151
+ - @plitzi/sdk-interactions@0.38.14
152
+ - @plitzi/sdk-navigation@0.38.14
153
+ - @plitzi/sdk-schema@0.38.14
154
+ - @plitzi/sdk-shared@0.38.14
155
+
3
156
  ## 0.38.13
4
157
 
5
158
  ### Patch Changes
@@ -1,34 +1,40 @@
1
1
  import { useCallback as e, useEffect as t, useState as n } from "react";
2
2
  import { jsx as r, jsxs as i } from "react/jsx-runtime";
3
3
  import a from "clsx";
4
+ import { NOT_COPIED_REASON as o, copyText as s } from "@plitzi/sdk-shared/helpers/clipboard";
4
5
  //#region src/components/MarkdownDocument/components/CodeBlock/CodeBlock.tsx
5
- var o = 2e3, s = ({ language: s, code: c, classNames: l, children: u }) => {
6
- let [d, f] = n(!1);
6
+ var c = 2e3, l = {
7
+ idle: "Copy",
8
+ copied: "Copied",
9
+ refused: "Not copied"
10
+ }, u = ({ language: u, code: d, classNames: f, children: p }) => {
11
+ let [m, h] = n("idle");
7
12
  t(() => {
8
- if (!d) return;
9
- let e = setTimeout(() => f(!1), o);
13
+ if (m === "idle") return;
14
+ let e = setTimeout(() => h("idle"), c);
10
15
  return () => clearTimeout(e);
16
+ }, [m]);
17
+ let g = e(() => {
18
+ s(d).then((e) => h(e ? "copied" : "refused"));
11
19
  }, [d]);
12
- let p = e(() => {
13
- navigator.clipboard.writeText(c).then(() => f(!0));
14
- }, [c]);
15
20
  return /* @__PURE__ */ i("div", {
16
- className: a("markdown-code", l.codeBlockFrame),
17
- "data-language": s,
21
+ className: a("markdown-code", f.codeBlockFrame),
22
+ "data-language": u,
18
23
  children: [/* @__PURE__ */ i("div", {
19
- className: a("markdown-code-header", l.codeBlockHeader),
24
+ className: a("markdown-code-header", f.codeBlockHeader),
20
25
  children: [/* @__PURE__ */ r("span", {
21
- className: a("markdown-code-language", l.codeBlockLanguage),
22
- children: s ?? "text"
26
+ className: a("markdown-code-language", f.codeBlockLanguage),
27
+ children: u ?? "text"
23
28
  }), /* @__PURE__ */ r("button", {
24
29
  type: "button",
25
- className: a("markdown-code-copy", l.codeBlockCopy),
26
- onClick: p,
30
+ className: a("markdown-code-copy", f.codeBlockCopy),
31
+ onClick: g,
27
32
  "aria-live": "polite",
28
- children: d ? "Copied" : "Copy"
33
+ title: m === "refused" ? o : void 0,
34
+ children: l[m]
29
35
  })]
30
- }), u]
36
+ }), p]
31
37
  });
32
38
  };
33
39
  //#endregion
34
- export { s as default };
40
+ export { u as default };
@@ -7,33 +7,48 @@ var a = ({ spaceKey: a = "", environment: o = "main", onUpdate: s }) => {
7
7
  let c = e(s);
8
8
  return /* @__PURE__ */ n("div", {
9
9
  className: "flex flex-col gap-4 py-2",
10
- children: [/* @__PURE__ */ t(r, {
11
- value: a,
12
- label: "Space Key",
13
- onChange: c.text("spaceKey")
14
- }), /* @__PURE__ */ n(i, {
15
- value: o,
16
- label: "Environment",
17
- onChange: c.text("environment"),
18
- children: [
19
- /* @__PURE__ */ t("option", {
20
- value: "main",
21
- children: "Main"
22
- }),
23
- /* @__PURE__ */ t("option", {
24
- value: "development",
25
- children: "Development"
26
- }),
27
- /* @__PURE__ */ t("option", {
28
- value: "staging",
29
- children: "Staging"
30
- }),
31
- /* @__PURE__ */ t("option", {
32
- value: "production",
33
- children: "Production"
34
- })
35
- ]
36
- })]
10
+ children: [
11
+ /* @__PURE__ */ t(r, {
12
+ value: a,
13
+ label: "Space Key",
14
+ onChange: c.text("spaceKey")
15
+ }),
16
+ /* @__PURE__ */ n(i, {
17
+ value: o,
18
+ label: "Environment",
19
+ onChange: c.text("environment"),
20
+ children: [
21
+ /* @__PURE__ */ t("option", {
22
+ value: "main",
23
+ children: "Main"
24
+ }),
25
+ /* @__PURE__ */ t("option", {
26
+ value: "development",
27
+ children: "Development"
28
+ }),
29
+ /* @__PURE__ */ t("option", {
30
+ value: "staging",
31
+ children: "Staging"
32
+ }),
33
+ /* @__PURE__ */ t("option", {
34
+ value: "production",
35
+ children: "Production"
36
+ })
37
+ ]
38
+ }),
39
+ /* @__PURE__ */ n("p", {
40
+ className: "text-xs",
41
+ children: [
42
+ "Or bind ",
43
+ /* @__PURE__ */ t("code", { children: "offlineData" }),
44
+ " to a space's documents — an API's answer, a runtime's route — or",
45
+ /* @__PURE__ */ t("code", { children: "offlineData.schema" }),
46
+ " and ",
47
+ /* @__PURE__ */ t("code", { children: "offlineData.style" }),
48
+ " from two elements, to draw it with no backend; the key is then not read."
49
+ ]
50
+ })
51
+ ]
37
52
  });
38
53
  };
39
54
  //#endregion
@@ -0,0 +1,39 @@
1
+ import { Environment, OfflineDataRaw } from '@plitzi/sdk-shared';
2
+ /** What this element can be authored with. */
3
+ export type PlitziSdkAttributes = {
4
+ /** The space drawn, fetched by its key. */
5
+ spaceKey?: string;
6
+ environment?: Environment;
7
+ /**
8
+ * The space drawn from its documents instead — `schema` and `style` with its compiled `cache`, what `plitzi_render`
9
+ * answers — with no backend behind it: UI generated on the fly, a preview. Usually bound: whole, to an
10
+ * `apiContainer`'s answer or a runtime's route, or `offlineData.schema` and `offlineData.style` from two elements.
11
+ * With it, `spaceKey` is not read.
12
+ */
13
+ offlineData?: OfflineDataRaw;
14
+ };
15
+ declare const declaration: import('@plitzi/sdk-shared/authoring/declare').DeclaredElement<{
16
+ readonly type: "plitziSdk";
17
+ readonly content: {
18
+ readonly attributes: {
19
+ readonly spaceKey: "";
20
+ readonly environment: "main";
21
+ };
22
+ readonly definition: {
23
+ readonly label: "Plitzi Sdk";
24
+ readonly description: "Another space drawn inside this one, as a widget — its home page, no router of its own: by its key, or from its documents (`offlineData`), bound to UI generated on the fly.";
25
+ };
26
+ readonly market: {
27
+ readonly category: "advanced";
28
+ readonly icon: "https://cdn.plitzi.com/resources/img/favicon.svg";
29
+ };
30
+ readonly defaultStyle: {
31
+ readonly style: {
32
+ readonly base: {
33
+ readonly default: {};
34
+ };
35
+ };
36
+ };
37
+ };
38
+ }, PlitziSdkAttributes>;
39
+ export default declaration;
@@ -0,0 +1,22 @@
1
+ import { elementDeclaration as e } from "@plitzi/sdk-shared/authoring/declare";
2
+ //#region src/elements/advanced/PlitziSdk/declaration.ts
3
+ var t = e()({
4
+ type: "plitziSdk",
5
+ content: {
6
+ attributes: {
7
+ spaceKey: "",
8
+ environment: "main"
9
+ },
10
+ definition: {
11
+ label: "Plitzi Sdk",
12
+ description: "Another space drawn inside this one, as a widget — its home page, no router of its own: by its key, or from its documents (`offlineData`), bound to UI generated on the fly."
13
+ },
14
+ market: {
15
+ category: "advanced",
16
+ icon: "https://cdn.plitzi.com/resources/img/favicon.svg"
17
+ },
18
+ defaultStyle: { style: { base: { default: {} } } }
19
+ }
20
+ });
21
+ //#endregion
22
+ export { t as default };
@@ -12,7 +12,7 @@ export type DropdownProps = {
12
12
  containerLeftOffset?: number;
13
13
  disabled?: boolean;
14
14
  };
15
- declare const Dropdown: ({ ref, children, className, popupPlacement, openPopup, backgroundDisabled, closeOnClickBackground, closeOnClickPopup, containerTopOffset, containerLeftOffset, disabled }: DropdownProps) => import('react').JSX.Element;
15
+ declare const Dropdown: ({ ref: refProp, children, className, popupPlacement, openPopup, backgroundDisabled, closeOnClickBackground, closeOnClickPopup, containerTopOffset, containerLeftOffset, disabled }: DropdownProps) => import('react').JSX.Element;
16
16
  declare const _default: {
17
17
  (props: import('../../../Element/hocs/withElement').WithElementProps<DropdownProps>): import('react').JSX.Element;
18
18
  displayName: string;
@@ -4,64 +4,78 @@ import { RootElement as n } from "../../../Element/RootElement.mjs";
4
4
  import r from "./DropdownContext.mjs";
5
5
  import i from "./useDropdown.mjs";
6
6
  import a from "./useDropdownTrigger.mjs";
7
- import { useCallback as o, useMemo as s, useRef as c } from "react";
8
- import l from "@plitzi/sdk-shared/hooks/usePlitzi";
9
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
10
- import f from "clsx";
7
+ import { useCallback as o, useLayoutEffect as s, useMemo as c, useRef as l, useState as u } from "react";
8
+ import d from "@plitzi/sdk-shared/hooks/usePlitzi";
9
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
10
+ import m from "clsx";
11
+ import { createPortal as h } from "react-dom";
12
+ import { spaceRootOf as g } from "@plitzi/sdk-shared/helpers/spaceRoot";
11
13
  //#region src/elements/basic/Dropdown/Dropdown.tsx
12
- var p = ({ ref: e, children: p, className: m = "", popupPlacement: h = "bottom", openPopup: g = !1, backgroundDisabled: _ = !1, closeOnClickBackground: v = !0, closeOnClickPopup: y = !0, containerTopOffset: b = 5, containerLeftOffset: x = 5, disabled: S = !1 }) => {
13
- let { setElementState: C, definition: { styleSelectors: w } } = t(), { settings: { previewMode: T }, utils: { getWindow: E } } = l(), D = c(null), O = c(null), k = s(() => E(), [E]), A = o((e) => {
14
- v && (e.stopPropagation(), e.preventDefault(), C((e) => ({
14
+ var _ = ({ ref: e, children: _, className: v = "", popupPlacement: y = "bottom", openPopup: b = !1, backgroundDisabled: x = !1, closeOnClickBackground: S = !0, closeOnClickPopup: C = !0, containerTopOffset: w = 5, containerLeftOffset: T = 5, disabled: E = !1 }) => {
15
+ let { setElementState: D, definition: { styleSelectors: O } } = t(), { settings: { previewMode: k }, utils: { getWindow: A } } = d(), j = l(null), M = e ?? j, N = l(null), P = l(null), F = c(() => A(), [A]), I = o((e) => {
16
+ S && (e.stopPropagation(), e.preventDefault(), D((e) => ({
15
17
  ...e,
16
18
  openPopup: !1
17
19
  })));
18
- }, [v, C]), j = o((e) => {
19
- C((t) => ({
20
+ }, [S, D]), L = o((e) => {
21
+ D((t) => ({
20
22
  ...t,
21
23
  openPopup: e
22
24
  }));
23
- }, [C]), [, , M, N, , P] = i({
24
- ref: e,
25
- popupRef: D,
26
- open: g,
27
- disabled: !T || S,
28
- closeOnClickPopup: y,
29
- closeOnClickOutside: v && !_,
30
- placement: h,
31
- offsetX: x,
32
- offsetY: b,
33
- myWindow: k,
34
- onChange: j
35
- }), F = a({
36
- popupRef: D,
37
- open: g,
38
- positioned: !!P,
39
- enabled: !!T && !S
40
- }), I = o((e) => {
41
- F(e), M(e);
42
- }, [F, M]), L = s(() => ({
43
- popupRef: D,
44
- openPopup: g,
45
- parameters: P,
46
- onClick: N
47
- }), [
48
- N,
49
- g,
50
- P
25
+ }, [D]), [, , R, z, , B] = i({
26
+ ref: M,
27
+ popupRef: N,
28
+ open: b,
29
+ disabled: !k || E,
30
+ closeOnClickPopup: C,
31
+ closeOnClickOutside: S && !x,
32
+ placement: y,
33
+ offsetX: T,
34
+ offsetY: w,
35
+ myWindow: F,
36
+ onChange: L
37
+ }), [V, H] = u(null);
38
+ s(() => {
39
+ b && k && !V && H(g(M.current));
40
+ }, [
41
+ b,
42
+ k,
43
+ V,
44
+ M
51
45
  ]);
52
- return /* @__PURE__ */ d(n, {
53
- ref: e,
54
- className: f("plitzi-component__dropdown", m, { "container--empty--skip": !T && !p }),
55
- onClick: I,
56
- children: [/* @__PURE__ */ u(r, {
57
- value: L,
58
- children: p
59
- }), g && _ && T && /* @__PURE__ */ u("div", {
60
- ref: O,
61
- className: f("plitzi-component__dropdown__background-container", w.backgroundContainer),
62
- onClick: A
63
- })]
46
+ let U = k ? V : null, W = a({
47
+ rootRef: M,
48
+ popupRef: N,
49
+ open: b,
50
+ positioned: !!B,
51
+ enabled: !!k && !E
52
+ }), G = o((e) => {
53
+ W(e), R(e);
54
+ }, [W, R]), K = c(() => ({
55
+ popupRef: N,
56
+ openPopup: b,
57
+ parameters: B,
58
+ onClick: z,
59
+ layer: U
60
+ }), [
61
+ z,
62
+ b,
63
+ B,
64
+ U
65
+ ]), q = b && x && k && /* @__PURE__ */ f("div", {
66
+ ref: P,
67
+ className: m("plitzi-component__dropdown__background-container", O.backgroundContainer),
68
+ onClick: I
69
+ });
70
+ return /* @__PURE__ */ p(n, {
71
+ ref: M,
72
+ className: m("plitzi-component__dropdown", v, { "container--empty--skip": !k && !_ }),
73
+ onClick: G,
74
+ children: [/* @__PURE__ */ f(r, {
75
+ value: K,
76
+ children: _
77
+ }), q && U ? h(q, U) : q]
64
78
  });
65
- }, m = e(p);
79
+ }, v = e(_);
66
80
  //#endregion
67
- export { p as Dropdown, m as default };
81
+ export { _ as Dropdown, v as default };
@@ -5,6 +5,8 @@ export type DropdownContextValue = {
5
5
  openPopup: boolean;
6
6
  parameters?: FloatingPosition;
7
7
  onClick: (e: MouseEvent) => void;
8
+ /** Where the open popup is drawn instead of in place: the space's root, once the menu has opened (see `Dropdown`). */
9
+ layer: HTMLElement | null;
8
10
  };
9
11
  declare const DropdownContext: import('react').Context<DropdownContextValue>;
10
12
  export default DropdownContext;
@@ -4,16 +4,19 @@ import n from "../DropdownContext.mjs";
4
4
  import { use as r, useImperativeHandle as i } from "react";
5
5
  import { jsx as a } from "react/jsx-runtime";
6
6
  import o from "clsx";
7
+ import { createPortal as s } from "react-dom";
7
8
  //#region src/elements/basic/Dropdown/DropdownPopup/DropdownPopup.tsx
8
- var s = ({ ref: e, className: s = "", children: c }) => {
9
- let { popupRef: l, openPopup: u, parameters: d, onClick: f } = r(n);
10
- return i(e, () => l.current ?? null, [l]), /* @__PURE__ */ a(t, {
11
- ref: l,
12
- className: o("plitzi-component__dropdown-popup", s, { "popup-container--no-visible": !u || !d }),
13
- style: d,
14
- onClick: f,
15
- children: c
9
+ var c = ({ ref: e, className: c = "", children: l }) => {
10
+ let { popupRef: u, openPopup: d, parameters: f, onClick: p, layer: m } = r(n);
11
+ i(e, () => u.current ?? null, [u]);
12
+ let h = /* @__PURE__ */ a(t, {
13
+ ref: u,
14
+ className: o("plitzi-component__dropdown-popup", c, { "popup-container--no-visible": !d || !f }),
15
+ style: f,
16
+ onClick: p,
17
+ children: l
16
18
  });
17
- }, c = e(s);
19
+ return m ? s(h, m) : h;
20
+ }, l = e(c);
18
21
  //#endregion
19
- export { s as DropdownPopup, c as default };
22
+ export { c as DropdownPopup, l as default };
@@ -1,5 +1,7 @@
1
1
  import { MouseEvent, RefObject } from 'react';
2
2
  export type UseDropdownTriggerProps = {
3
+ /** The dropdown's own element, where its trigger is. */
4
+ rootRef?: RefObject<HTMLElement | null>;
3
5
  popupRef: RefObject<HTMLElement | null>;
4
6
  open: boolean;
5
7
  /** Where the popup is drawn; until it is known the popup is hidden, and nothing in it can take the focus. */
@@ -22,5 +24,5 @@ export type UseDropdownTriggerProps = {
22
24
  *
23
25
  * @returns what the dropdown's own click handler calls first, to learn whether a keyboard pressed the trigger.
24
26
  */
25
- declare const useDropdownTrigger: ({ popupRef, open, positioned, enabled }: UseDropdownTriggerProps) => ((event: MouseEvent) => void);
27
+ declare const useDropdownTrigger: ({ rootRef, popupRef, open, positioned, enabled }: UseDropdownTriggerProps) => ((event: MouseEvent) => void);
26
28
  export default useDropdownTrigger;
@@ -1,30 +1,28 @@
1
1
  import { useCallback as e, useEffect as t, useRef as n } from "react";
2
2
  //#region src/elements/basic/Dropdown/useDropdownTrigger.ts
3
- var r = "button, a[href], input, select, textarea, [role=\"button\"]", i = (e) => {
4
- let t = e?.closest(".plitzi-component__dropdown");
5
- return t ? [...t.querySelectorAll(r)].find((t) => !e?.contains(t)) : void 0;
6
- }, a = ({ popupRef: a, open: o, positioned: s, enabled: c }) => {
7
- let l = n(!1), u = n(o), d = e((e) => {
8
- l.current = e.detail === 0;
3
+ var r = "button, a[href], input, select, textarea, [role=\"button\"]", i = (e, t) => e ? [...e.querySelectorAll(r)].find((e) => !t?.contains(e)) : void 0, a = ({ rootRef: a, popupRef: o, open: s, positioned: c, enabled: l }) => {
4
+ let u = n(!1), d = n(s), f = e((e) => {
5
+ u.current = e.detail === 0;
9
6
  }, []);
10
7
  return t(() => {
11
- let e = c ? i(a.current) : void 0;
12
- e && (e.setAttribute("aria-haspopup", "true"), e.setAttribute("aria-expanded", String(o)));
8
+ let e = l ? i(a?.current, o.current) : void 0;
9
+ e && (e.setAttribute("aria-haspopup", "true"), e.setAttribute("aria-expanded", String(s)));
13
10
  }), t(() => {
14
- let e = a.current, t = u.current && !o;
15
- if (u.current = o, c && e) {
11
+ let e = o.current, t = d.current && !s;
12
+ if (d.current = s, l && e) {
16
13
  if (t && e.contains(e.ownerDocument.activeElement)) {
17
- i(e)?.focus({ preventScroll: !0 });
14
+ i(a?.current, e)?.focus({ preventScroll: !0 });
18
15
  return;
19
16
  }
20
- o && s && l.current && !e.contains(e.ownerDocument.activeElement) && (l.current = !1, e.querySelector(r)?.focus({ preventScroll: !0 }));
17
+ s && c && u.current && !e.contains(e.ownerDocument.activeElement) && (u.current = !1, e.querySelector(r)?.focus({ preventScroll: !0 }));
21
18
  }
22
19
  }, [
20
+ l,
21
+ s,
23
22
  c,
24
23
  o,
25
- s,
26
24
  a
27
- ]), d;
25
+ ]), f;
28
26
  };
29
27
  //#endregion
30
28
  export { a as default };