@colixsystems/widget-sdk 0.170.0 → 0.173.1

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/dist/list-view.js CHANGED
@@ -1,5 +1,5 @@
1
- // The ONE implementation behind the `<List>`, `<ListRow>` and `<EmptyState>`
2
- // SDK primitives. The platform bindings (`list.js` / `list.native.js`) differ
1
+ // The ONE implementation behind the `<List>`, `<ListRow>`, `<ListHeader>` and
2
+ // `<EmptyState>` SDK primitives. The platform bindings (`list.js` / `list.native.js`) differ
3
3
  // only in where the primitives come from, so the hosts cannot drift.
4
4
  //
5
5
  // Every widget drawing a list of records used to hand-roll its rows, so two
@@ -10,7 +10,13 @@ import React from "react";
10
10
  import { useHostTheme } from "./hooks.js";
11
11
  import { DEFAULT_THEME_TOKENS } from "./_theme-tokens.js";
12
12
  import { Icon } from "./icon.js";
13
- import { resolveListTokens, LIST_TOKEN_NAMES } from "./list-tokens.js";
13
+ import {
14
+ resolveListTokens,
15
+ listRowFrame,
16
+ listSideEdgeWidth,
17
+ LIST_TOKEN_NAMES,
18
+ LIST_SELECTED_BAR_WIDTH,
19
+ } from "./list-tokens.js";
14
20
  import { textRoleStyle } from "./text-roles.js";
15
21
 
16
22
  // Outside a widget host (a test, a preview) the defaults still paint a row.
@@ -26,15 +32,49 @@ function listOverrides(props) {
26
32
  return out;
27
33
  }
28
34
 
35
+ function soleElement(children) {
36
+ const parts = React.Children.toArray(children);
37
+ return parts.length === 1 && React.isValidElement(parts[0]) ? parts[0] : null;
38
+ }
39
+
40
+ const HORIZONTAL_PADDING = [
41
+ "padding",
42
+ "paddingHorizontal",
43
+ "paddingLeft",
44
+ "paddingStart",
45
+ "paddingInline",
46
+ "paddingInlineStart",
47
+ ];
48
+
29
49
  /**
30
50
  * Bind the shared list components to one platform's primitives.
31
51
  *
32
- * @param {{View: any, Text: any, Pressable: any}} rn
52
+ * @param {{View: any, Text: any, Pressable: any, StyleSheet: any}} rn
33
53
  */
34
54
  export function makeList(rn) {
35
- const { View, Text, Pressable } = rn;
55
+ const { View, Text, Pressable, StyleSheet } = rn;
36
56
  const ListContext = React.createContext(null);
37
57
 
58
+ function useRowTokens(props) {
59
+ const theme = useListTheme();
60
+ const inherited = React.useContext(ListContext);
61
+ return [theme, resolveListTokens(theme, { ...inherited, ...listOverrides(props) })];
62
+ }
63
+
64
+ // A lone row View spans the row, as the title block does. Anything
65
+ // else (cells, a component rendering cells) stays in the row as before.
66
+ function customBody(children) {
67
+ const only = soleElement(children);
68
+ return only && only.type === View
69
+ ? React.createElement(View, { style: { flex: 1, minWidth: 0 } }, only)
70
+ : children;
71
+ }
72
+
73
+ function setsOwnInset(style) {
74
+ const flat = StyleSheet.flatten(style) || {};
75
+ return HORIZONTAL_PADDING.some((key) => flat[key] !== undefined);
76
+ }
77
+
38
78
  function List(props) {
39
79
  const { children, style, testID } = props;
40
80
  const theme = useListTheme();
@@ -72,12 +112,7 @@ export function makeList(rn) {
72
112
  testID,
73
113
  children,
74
114
  } = props;
75
- const theme = useListTheme();
76
- const inherited = React.useContext(ListContext);
77
- const tokens = resolveListTokens(theme, {
78
- ...inherited,
79
- ...listOverrides(props),
80
- });
115
+ const [theme, tokens] = useRowTokens(props);
81
116
  const { colors, spacing, typography } = theme;
82
117
 
83
118
  const pressable = typeof onPress === "function";
@@ -90,10 +125,8 @@ export function makeList(rn) {
90
125
  ? { borderBottomWidth: tokens.dividerWidth, borderColor: tokens.dividerColor }
91
126
  : null;
92
127
  const rowStyle = {
93
- flexDirection: "row",
94
- alignItems: "center",
95
- gap: spacing.md,
96
- paddingHorizontal: tokens.rowPadding,
128
+ // An inset the widget sets itself is left exactly as it asked.
129
+ ...listRowFrame(tokens, spacing, selected && !setsOwnInset(style)),
97
130
  paddingVertical: tokens.rowPadding,
98
131
  // A dense look may shrink a static row, never a tap target.
99
132
  minHeight: pressable ? Math.max(tokens.rowMinHeight, 44) : tokens.rowMinHeight,
@@ -104,7 +137,7 @@ export function makeList(rn) {
104
137
  ...(selected
105
138
  ? {
106
139
  backgroundColor: tokens.selectedBackground,
107
- borderLeftWidth: 3,
140
+ borderLeftWidth: LIST_SELECTED_BAR_WIDTH,
108
141
  borderLeftColor: tokens.selectedAccent,
109
142
  }
110
143
  : null),
@@ -112,7 +145,7 @@ export function makeList(rn) {
112
145
 
113
146
  const body =
114
147
  children !== undefined
115
- ? children
148
+ ? customBody(children)
116
149
  : React.createElement(
117
150
  View,
118
151
  { style: { flex: 1, gap: 2 } },
@@ -174,6 +207,39 @@ export function makeList(rn) {
174
207
  }
175
208
  ListRow.displayName = "ListRow";
176
209
 
210
+ // A table's column labels: a ListRow's frame and body slot with none of its
211
+ // row chrome, so each label sits over its column.
212
+ function ListHeader(props) {
213
+ const { leading, trailing, divider = true, accessibilityLabel, style, testID, children } = props;
214
+ const [theme, tokens] = useRowTokens(props);
215
+ const { spacing } = theme;
216
+ // Transparent side edges stand in for a tile's outline.
217
+ const side = listSideEdgeWidth(tokens);
218
+ const headerStyle = {
219
+ ...listRowFrame(tokens, spacing),
220
+ paddingVertical: Math.min(tokens.rowPadding, spacing.sm),
221
+ ...(side > 0
222
+ ? { borderLeftWidth: side, borderRightWidth: side, borderColor: "transparent" }
223
+ : null),
224
+ ...(divider && tokens.dividerWidth > 0
225
+ ? { borderBottomWidth: tokens.dividerWidth, borderBottomColor: tokens.dividerColor }
226
+ : null),
227
+ };
228
+ return React.createElement(
229
+ View,
230
+ {
231
+ style: [headerStyle, style],
232
+ testID,
233
+ accessibilityLabel,
234
+ accessible: accessibilityLabel ? true : undefined,
235
+ },
236
+ leading || null,
237
+ customBody(children),
238
+ trailing || null,
239
+ );
240
+ }
241
+ ListHeader.displayName = "ListHeader";
242
+
177
243
  function EmptyState({ icon = "inbox", title, message, children, style, testID }) {
178
244
  const { colors, spacing, radii, typography } = useListTheme();
179
245
  return React.createElement(
@@ -211,5 +277,5 @@ export function makeList(rn) {
211
277
  }
212
278
  EmptyState.displayName = "EmptyState";
213
279
 
214
- return { List, ListRow, EmptyState };
280
+ return { List, ListRow, ListHeader, EmptyState };
215
281
  }
package/dist/list.js CHANGED
@@ -1,7 +1,7 @@
1
- // `<List>`, `<ListRow>`, `<EmptyState>` (web). Binds ./list-view.js to
2
- // react-native-web; see ./primitives.js for why not the bare specifier.
1
+ // `<List>`, `<ListRow>`, `<ListHeader>`, `<EmptyState>` (web). Binds
2
+ // ./list-view.js to react-native-web; see ./primitives.js for why not the bare specifier.
3
3
 
4
4
  import * as ReactNative from "react-native-web";
5
5
  import { makeList } from "./list-view.js";
6
6
 
7
- export const { List, ListRow, EmptyState } = makeList(ReactNative);
7
+ export const { List, ListRow, ListHeader, EmptyState } = makeList(ReactNative);
@@ -1,7 +1,7 @@
1
- // `<List>`, `<ListRow>`, `<EmptyState>` (native). Binds ./list-view.js to
2
- // React Native's own primitives; only the import differs from ./list.js.
1
+ // `<List>`, `<ListRow>`, `<ListHeader>`, `<EmptyState>` (native). Binds
2
+ // ./list-view.js to React Native's own primitives; only the import differs from ./list.js.
3
3
 
4
- import { View, Text, Pressable } from "react-native";
4
+ import { View, Text, Pressable, StyleSheet } from "react-native";
5
5
  import { makeList } from "./list-view.js";
6
6
 
7
- export const { List, ListRow, EmptyState } = makeList({ View, Text, Pressable });
7
+ export const { List, ListRow, ListHeader, EmptyState } = makeList({ View, Text, Pressable, StyleSheet });
package/dist/webbundle.js CHANGED
@@ -90,12 +90,15 @@ function cssInjectPlugin() {
90
90
  * packer passes the frontend's `node_modules` (where the vetted web packages
91
91
  * are installed per the adding-vetted-packages skill) so the bundle resolves
92
92
  * them no matter the invoking CWD.
93
+ * @param {boolean} [opts.minify] minify the output for production serving.
94
+ * Off by default so the dev server's bundle stays readable.
93
95
  * @returns {Promise<string>} the bundled ESM source.
94
96
  */
95
97
  export async function bundleWebEntry({
96
98
  entryPath,
97
99
  external = hostExternalSpecifiers(),
98
100
  nodePaths = [],
101
+ minify = false,
99
102
  }) {
100
103
  const esbuild = await loadEsbuild();
101
104
  const result = await esbuild.build({
@@ -138,6 +141,7 @@ export async function bundleWebEntry({
138
141
  // adds extra roots so a packer invoked from the repo root still resolves the
139
142
  // vetted web packages installed under frontend/node_modules.
140
143
  nodePaths,
144
+ minify,
141
145
  logLevel: "silent",
142
146
  });
143
147
  const out = result.outputFiles?.[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colixsystems/widget-sdk",
3
- "version": "0.170.0",
3
+ "version": "0.173.1",
4
4
  "description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
5
5
  "homepage": "https://github.com/Colix-AB/AppStudio",
6
6
  "type": "module",
package/src/webbundle.js CHANGED
@@ -90,12 +90,15 @@ function cssInjectPlugin() {
90
90
  * packer passes the frontend's `node_modules` (where the vetted web packages
91
91
  * are installed per the adding-vetted-packages skill) so the bundle resolves
92
92
  * them no matter the invoking CWD.
93
+ * @param {boolean} [opts.minify] minify the output for production serving.
94
+ * Off by default so the dev server's bundle stays readable.
93
95
  * @returns {Promise<string>} the bundled ESM source.
94
96
  */
95
97
  export async function bundleWebEntry({
96
98
  entryPath,
97
99
  external = hostExternalSpecifiers(),
98
100
  nodePaths = [],
101
+ minify = false,
99
102
  }) {
100
103
  const esbuild = await loadEsbuild();
101
104
  const result = await esbuild.build({
@@ -138,6 +141,7 @@ export async function bundleWebEntry({
138
141
  // adds extra roots so a packer invoked from the repo root still resolves the
139
142
  // vetted web packages installed under frontend/node_modules.
140
143
  nodePaths,
144
+ minify,
141
145
  logLevel: "silent",
142
146
  });
143
147
  const out = result.outputFiles?.[0];