@tailorkit/react 0.1.0-beta.40 → 0.1.0-beta.42

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/README.md CHANGED
@@ -1,3 +1,35 @@
1
1
  # TailorKit React
2
2
 
3
3
  Use the React components and hooks in `@tailorkit/react` to integrate TailorKit into your SaaS product.
4
+
5
+ ## Pressable
6
+
7
+ Register the standard `primitives` with the host client and `primitives(theme)` with the contract's Zod, Valibot, or ArkType adapter. Generated extension types expose the same Pressable API for every validator.
8
+
9
+ Pressable owns a `<div role="button">` and its layout. `layout` defaults to `"inline"` (`inline-flex`); `"box"`, `"flex"`, and `"grid"` use `block`, `flex`, and `grid`. It accepts existing primitive theme tokens, responsive styles, and layout props, including `align`, `gap`, `padding`, and `columns`.
10
+
11
+ ```tsx
12
+ <Pressable
13
+ layout="flex"
14
+ align="center"
15
+ gap="sm"
16
+ padding="md"
17
+ onPress={openEmail}
18
+ >
19
+ <Box grow="1">
20
+ <Inline>{email.sender}</Inline>
21
+ <Inline>{email.subject}</Inline>
22
+ </Box>
23
+ <Inline>{email.receivedAt}</Inline>
24
+ </Pressable>
25
+ ```
26
+
27
+ Use non-interactive layout children. Nested interactive controls, including buttons, links, inputs, and other Pressables, are unsupported. Pressable does not promote a child or support `asChild`.
28
+
29
+ Enabled controls participate in Tab and Shift+Tab navigation and show a focus outline. Pointer clicks activate once. Enter activates on keydown; Space activates on keyup and prevents scrolling. Holding a key does not repeat activation. Blur or disabling cancels pending Space activation. Focus stays on the control unless the action intentionally moves it. This follows the [ARIA button keyboard pattern](https://www.w3.org/WAI/ARIA/apg/patterns/button/).
30
+
31
+ Children provide the accessible name. Set `ariaLabel` for an explicit name, including icon-only actions. `disabled` exposes `aria-disabled="true"`, sets `tabIndex={-1}`, and blocks pointer, keyboard, and programmatic click activation. The API also permits omitting `onPress`.
32
+
33
+ Style interactions with `:hover`, `:active`, `:focus-visible`, and `:disabled` conditional token values. `:active` includes held keyboard activation; `:disabled` targets the ARIA disabled state and suppresses other interaction styles. Each style accepts responsive breakpoint values. The focus indicator uses `outline: auto`, letting the browser choose its appearance. Pressable does not specify an outline width, color, or offset, and does not require a host CSS variable or focus-color token.
34
+
35
+ DOM events, keyboard state, focus, and styles stay in the React host. Only the zero-argument `onPress?: () => void` callback is dispatched into the sandbox. The host-provided Button and other primitives retain their existing behavior.
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // src/components/render-slot.tsx
2
2
  import { useStore as useStore2 } from "@nanostores/react";
3
3
  import { createSlotRuntime, buildThemeCss } from "@tailorkit/client-core";
4
- import { useEffect as useEffect2, useId as useId2, useMemo as useMemo3, useState as useState2 } from "react";
4
+ import { useEffect as useEffect2, useId as useId2, useMemo as useMemo3, useState as useState3 } from "react";
5
5
 
6
6
  // src/primitives.tsx
7
7
  import { buildPrimitiveCss } from "@tailorkit/client-core";
8
- import { createContext, useContext, useId, useMemo } from "react";
8
+ import { createContext, useContext, useId, useMemo, useState } from "react";
9
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
10
  var PrimitiveThemeContext = createContext(null);
11
11
  var usePrimitiveNodeId = () => {
@@ -50,6 +50,10 @@ var Pressable = ({
50
50
  const { ariaLabel, layout, onPress } = props;
51
51
  const display = layout === "box" || layout === "flex" || layout === "grid" ? displays[layout] : displays.inline;
52
52
  const disabled = props.disabled === true;
53
+ const [pressedKey, setPressedKey] = useState(null);
54
+ if (disabled && pressedKey !== null) {
55
+ setPressedKey(null);
56
+ }
53
57
  const css = context ? buildPrimitiveCss({
54
58
  display,
55
59
  nodeId,
@@ -61,17 +65,62 @@ var Pressable = ({
61
65
  return /* @__PURE__ */ jsxs(Fragment, { children: [
62
66
  css === "" ? null : /* @__PURE__ */ jsx("style", { "data-tailorkit-node-style": nodeId, children: css }),
63
67
  /* @__PURE__ */ jsx(
64
- "button",
68
+ "div",
65
69
  {
70
+ "aria-disabled": disabled || void 0,
66
71
  "aria-label": isStringValue(ariaLabel) ? ariaLabel : void 0,
72
+ "data-pressed": !disabled && pressedKey !== null ? "" : void 0,
67
73
  "data-tailorkit-node": nodeId,
68
- disabled,
69
- onClick: () => {
70
- if (!disabled && isPressCallback(onPress)) {
74
+ onBlur: () => {
75
+ setPressedKey(null);
76
+ },
77
+ onClick: (event) => {
78
+ if (disabled) {
79
+ event.preventDefault();
80
+ event.stopPropagation();
81
+ return;
82
+ }
83
+ if (event.button === 0 && isPressCallback(onPress)) {
84
+ onPress();
85
+ }
86
+ },
87
+ onKeyDown: (event) => {
88
+ const { key } = event;
89
+ if (event.target !== event.currentTarget || key !== "Enter" && key !== " ") {
90
+ return;
91
+ }
92
+ event.preventDefault();
93
+ if (disabled || event.repeat || pressedKey !== null) {
94
+ return;
95
+ }
96
+ setPressedKey(key);
97
+ if (key === "Enter" && isPressCallback(onPress)) {
71
98
  onPress();
72
99
  }
73
100
  },
74
- type: "button",
101
+ onKeyUp: (event) => {
102
+ const { key } = event;
103
+ if (event.target !== event.currentTarget || key !== "Enter" && key !== " ") {
104
+ return;
105
+ }
106
+ event.preventDefault();
107
+ const activate = !disabled && key === " " && pressedKey === " ";
108
+ setPressedKey(null);
109
+ if (activate && isPressCallback(onPress)) {
110
+ onPress();
111
+ }
112
+ },
113
+ onPointerDown: (event) => {
114
+ if (disabled) {
115
+ event.preventDefault();
116
+ return;
117
+ }
118
+ if (event.button === 0) {
119
+ event.currentTarget.focus();
120
+ }
121
+ },
122
+ role: "button",
123
+ tabIndex: disabled ? -1 : 0,
75
124
  children
76
125
  }
77
126
  )
@@ -115,7 +164,7 @@ import {
115
164
  useContext as useContext2,
116
165
  useEffect,
117
166
  useMemo as useMemo2,
118
- useState
167
+ useState as useState2
119
168
  } from "react";
120
169
  import { jsx as jsx2 } from "react/jsx-runtime";
121
170
  var RemoteUIContext = createContext2(null);
@@ -144,7 +193,6 @@ var RemoteRoot = ({ store }) => {
144
193
  };
145
194
  var RemoteViewHost = ({
146
195
  appUrl,
147
- sourceText,
148
196
  toolBridge,
149
197
  getBackendSession,
150
198
  components: components2,
@@ -152,7 +200,7 @@ var RemoteViewHost = ({
152
200
  props
153
201
  }) => {
154
202
  const appKey = appUrl.toString();
155
- const [state, setState] = useState(() => ({
203
+ const [state, setState] = useState2(() => ({
156
204
  appKey,
157
205
  store: createRemoteViewStore({ components: components2, props })
158
206
  }));
@@ -172,10 +220,9 @@ var RemoteViewHost = ({
172
220
  appUrl,
173
221
  createIframe,
174
222
  getBackendSession,
175
- sourceText,
176
223
  toolBridge
177
224
  }),
178
- [appUrl, sourceText, toolBridge, getBackendSession, createIframe, store]
225
+ [appUrl, toolBridge, getBackendSession, createIframe, store]
179
226
  );
180
227
  if (snapshot.status === "error" && snapshot.error) {
181
228
  return createElement("div", null, formatError(snapshot.error));
@@ -262,7 +309,7 @@ var SlotRenderer = (options) => {
262
309
  options.mode === "managed" ? "RenderSlot" : "RenderSlot.Controlled"
263
310
  );
264
311
  const reactId = useId2();
265
- const [state, setState] = useState2(() => ({
312
+ const [state, setState] = useState3(() => ({
266
313
  runtime: createSlotRuntime(store, options),
267
314
  store
268
315
  }));
@@ -294,7 +341,6 @@ var SlotRenderer = (options) => {
294
341
  RemoteViewHost,
295
342
  {
296
343
  appUrl: snapshot.appUrl,
297
- sourceText: snapshot.sourceText,
298
344
  toolBridge: snapshot.toolBridge,
299
345
  getBackendSession: snapshot.getBackendSession,
300
346
  components: client.components,
@@ -319,7 +365,7 @@ import {
319
365
  createComponentRegistry,
320
366
  createTailorKitStore
321
367
  } from "@tailorkit/client-core";
322
- import { createElement as createElement2, useEffect as useEffect4, useMemo as useMemo8, useState as useState3 } from "react";
368
+ import { createElement as createElement2, useEffect as useEffect4, useMemo as useMemo8, useState as useState4 } from "react";
323
369
 
324
370
  // src/hooks/use-apps.ts
325
371
  import { createValueMemo, createAppsQuery } from "@tailorkit/client-core";
@@ -427,7 +473,7 @@ var createReactTailorKitClient = (options) => {
427
473
  const client = {
428
474
  ...clientConfig,
429
475
  Provider: function Provider({ children, apps }) {
430
- const [store] = useState3(
476
+ const [store] = useState4(
431
477
  () => createTailorKitStore({
432
478
  apps,
433
479
  assetsBaseUrl: client.assetsBaseUrl,
@@ -440,12 +486,6 @@ var createReactTailorKitClient = (options) => {
440
486
  useEffect4(() => {
441
487
  store.setProvidedApps(apps);
442
488
  }, [store, apps]);
443
- useEffect4(
444
- () => () => {
445
- store.previews.dispose();
446
- },
447
- [store]
448
- );
449
489
  const context = useMemo8(() => ({ client, store }), [store]);
450
490
  return createElement2(
451
491
  TailorkitContext.Provider,
@@ -3,7 +3,6 @@ import type { RemoteProps } from "@tailorkit/sandbox/protocol";
3
3
  import type { ReactNode } from "react";
4
4
  interface RemoteViewHostProps {
5
5
  appUrl: string | URL;
6
- sourceText?: string;
7
6
  toolBridge?: ToolBridge;
8
7
  getBackendSession?: (options: {
9
8
  refresh: boolean;
@@ -12,7 +11,7 @@ interface RemoteViewHostProps {
12
11
  createIframe?: () => HTMLIFrameElement;
13
12
  props?: RemoteProps;
14
13
  }
15
- export declare const RemoteViewHost: ({ appUrl, sourceText, toolBridge, getBackendSession, components, createIframe, props, }: RemoteViewHostProps) => ReactNode;
14
+ export declare const RemoteViewHost: ({ appUrl, toolBridge, getBackendSession, components, createIframe, props, }: RemoteViewHostProps) => ReactNode;
16
15
  interface RemoteViewProps {
17
16
  nodeId: string;
18
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailorkit/react",
3
- "version": "0.1.0-beta.40",
3
+ "version": "0.1.0-beta.42",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -47,11 +47,11 @@
47
47
  "@floating-ui/react-dom": "^2.1.9",
48
48
  "@nanostores/react": "2.0.1",
49
49
  "@standard-schema/spec": "^1.1.0",
50
- "@tailorkit/app": "0.1.0-beta.40",
51
- "@tailorkit/client-core": "0.1.0-beta.40",
52
- "@tailorkit/client-platform": "0.1.0-beta.40",
53
- "@tailorkit/core": "0.1.0-beta.40",
54
- "@tailorkit/sandbox": "0.1.0-beta.40",
50
+ "@tailorkit/app": "0.1.0-beta.42",
51
+ "@tailorkit/client-core": "0.1.0-beta.42",
52
+ "@tailorkit/client-platform": "0.1.0-beta.42",
53
+ "@tailorkit/core": "0.1.0-beta.42",
54
+ "@tailorkit/sandbox": "0.1.0-beta.42",
55
55
  "nanostores": "1.5.5"
56
56
  },
57
57
  "devDependencies": {