@checkcourt/sdk 0.9.0 → 0.10.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/README.md CHANGED
@@ -193,6 +193,36 @@ ui.form({
193
193
  });
194
194
  ```
195
195
 
196
+ Blocks can carry an `icon` from `UI_ICONS` (text, heading, stat, badge, list items), a `stat`
197
+ can be `size: "lg"`, badges have a soft amber `"warning"` variant, `ui.columns` lays out 2
198
+ to 6 children in equal widths, and `ui.row(children, { justify: "between" })` spreads a row
199
+ across the full width (first child left, last child right). A weather widget:
200
+
201
+ ```ts
202
+ const days = [
203
+ ["Heute", "sun", "24°"],
204
+ ["Do", "cloud-sun", "21°"],
205
+ ["Fr", "cloud-rain", "17°"],
206
+ ["Sa", "cloud-drizzle", "18°"],
207
+ ["So", "sun", "23°"],
208
+ ] as const;
209
+
210
+ ui.doc([
211
+ ui.row(
212
+ [
213
+ ui.stat("Jetzt", "24°", { icon: "sun", size: "lg", hint: "Sonnig" }),
214
+ ui.badge("Regen möglich", { variant: "warning", icon: "droplets" }),
215
+ ],
216
+ { justify: "between" },
217
+ ),
218
+ ui.divider(),
219
+ ui.columns(
220
+ days.map(([day, icon, temp]) => ui.stat(day, temp, { icon })),
221
+ { dividers: true, align: "center" },
222
+ ),
223
+ ]);
224
+ ```
225
+
196
226
  Compare `context.installation_id` and `context.tenant_id` with what you stored from
197
227
  `app.installed` before you act on a request. For the context token alone (for example in
198
228
  the backend of an iframe extension), use `verifyExtensionContext(token, secret)`.
package/dist/iframe.d.ts CHANGED
@@ -6,7 +6,7 @@ export declare const MAX_TOAST_LENGTH = 200;
6
6
  /** Top-level areas `navigate()` may target; anything else is dropped by CheckCourt. */
7
7
  export declare const NAVIGABLE_SECTIONS: readonly ["dashboard", "booking", "my-bookings", "club", "team", "trainer", "fees", "arbeitsstunden", "settings", "admin"];
8
8
  /** CSS variables CheckCourt sends with every theme message. */
9
- export declare const THEME_TOKEN_NAMES: readonly ["--background", "--foreground", "--card", "--card-foreground", "--muted", "--muted-foreground", "--primary", "--primary-foreground", "--secondary", "--secondary-foreground", "--accent", "--accent-foreground", "--destructive", "--border", "--input", "--ring", "--radius"];
9
+ export declare const THEME_TOKEN_NAMES: readonly ["--background", "--foreground", "--card", "--card-foreground", "--muted", "--muted-foreground", "--primary", "--primary-foreground", "--secondary", "--secondary-foreground", "--accent", "--accent-foreground", "--destructive", "--warning", "--warning-foreground", "--warning-muted", "--border", "--input", "--ring", "--radius"];
10
10
  export type ThemeTokenName = (typeof THEME_TOKEN_NAMES)[number];
11
11
  export type FrameMessage = {
12
12
  type: "checkcourt:resize";
package/dist/iframe.js CHANGED
@@ -31,6 +31,9 @@ export const THEME_TOKEN_NAMES = [
31
31
  "--accent",
32
32
  "--accent-foreground",
33
33
  "--destructive",
34
+ "--warning",
35
+ "--warning-foreground",
36
+ "--warning-muted",
34
37
  "--border",
35
38
  "--input",
36
39
  "--ring",
package/dist/ui.d.ts CHANGED
@@ -2,34 +2,55 @@ export declare const UI_VERSION = "v1";
2
2
  export declare const MAX_UI_BLOCKS = 50;
3
3
  /** Top-level blocks sit at depth 1; a container's children one deeper. */
4
4
  export declare const MAX_UI_DEPTH = 3;
5
- export declare const BADGE_VARIANTS: readonly ["default", "secondary", "outline", "destructive"];
5
+ export declare const BADGE_VARIANTS: readonly ["default", "secondary", "outline", "destructive", "warning"];
6
6
  export declare const BUTTON_VARIANTS: readonly ["default", "secondary", "outline", "destructive"];
7
7
  export type BadgeVariant = (typeof BADGE_VARIANTS)[number];
8
8
  export type ButtonVariant = (typeof BUTTON_VARIANTS)[number];
9
+ /** Icons a block may show, by lucide name; CheckCourt draws them in the text color at text size. */
10
+ export declare const UI_ICONS: readonly ["sun", "moon", "cloud", "cloud-sun", "cloud-moon", "cloud-sun-rain", "cloud-rain", "cloud-drizzle", "cloud-lightning", "cloud-snow", "cloud-fog", "snowflake", "wind", "droplet", "droplets", "umbrella", "thermometer", "sunrise", "sunset", "check", "x", "info", "alert-triangle", "triangle-alert", "alert-circle", "circle-alert", "clock", "calendar", "map-pin", "trophy", "users", "user", "lock", "unlock", "lock-open", "lightbulb", "zap", "euro", "star", "heart", "bell", "flag", "activity", "timer", "ticket", "door-open"];
11
+ export type UiIcon = (typeof UI_ICONS)[number];
12
+ /** `lg` shows the value as a large display figure, `md` (default) as a regular stat. */
13
+ export declare const STAT_SIZES: readonly ["md", "lg"];
14
+ export type StatSize = (typeof STAT_SIZES)[number];
15
+ export declare const COLUMNS_ALIGNMENTS: readonly ["start", "center"];
16
+ export type ColumnsAlignment = (typeof COLUMNS_ALIGNMENTS)[number];
17
+ /** `between` spreads a row's children across the full width, first at the left edge, last at the right. */
18
+ export declare const ROW_JUSTIFICATIONS: readonly ["start", "between"];
19
+ export type RowJustify = (typeof ROW_JUSTIFICATIONS)[number];
20
+ /** A `columns` block holds 2 to 6 children. */
21
+ export declare const MIN_COLUMNS = 2;
22
+ export declare const MAX_COLUMNS = 6;
9
23
  export type UiTextBlock = {
10
24
  type: "text";
11
25
  text: string;
12
26
  tone?: "muted";
27
+ icon?: UiIcon;
13
28
  };
14
29
  export type UiHeadingBlock = {
15
30
  type: "heading";
16
31
  text: string;
17
32
  level: 2 | 3;
33
+ icon?: UiIcon;
18
34
  };
35
+ /** With `icon`, the icon sits before the value. */
19
36
  export type UiStatBlock = {
20
37
  type: "stat";
21
38
  label: string;
22
39
  value: string;
23
40
  hint?: string;
41
+ icon?: UiIcon;
42
+ size?: StatSize;
24
43
  };
25
44
  export type UiBadgeBlock = {
26
45
  type: "badge";
27
46
  label: string;
28
47
  variant?: BadgeVariant;
48
+ icon?: UiIcon;
29
49
  };
30
50
  export type UiListItem = {
31
51
  title: string;
32
52
  description?: string;
53
+ icon?: UiIcon;
33
54
  };
34
55
  export type UiListBlock = {
35
56
  type: "list";
@@ -136,8 +157,16 @@ export type UiStackBlock = {
136
157
  export type UiRowBlock = {
137
158
  type: "row";
138
159
  children: UiBlock[];
160
+ justify?: RowJustify;
139
161
  };
140
- export type UiBlock = UiTextBlock | UiHeadingBlock | UiStatBlock | UiBadgeBlock | UiListBlock | UiKeyValueBlock | UiLinkBlock | UiButtonBlock | UiFormBlock | UiDividerBlock | UiStackBlock | UiRowBlock;
162
+ /** 2 to 6 children side by side in equal widths; on narrow cards 4 wrap to 2 per row, 5 and 6 to 3. */
163
+ export type UiColumnsBlock = {
164
+ type: "columns";
165
+ children: UiBlock[];
166
+ dividers?: boolean;
167
+ align?: ColumnsAlignment;
168
+ };
169
+ export type UiBlock = UiTextBlock | UiHeadingBlock | UiStatBlock | UiBadgeBlock | UiListBlock | UiKeyValueBlock | UiLinkBlock | UiButtonBlock | UiFormBlock | UiDividerBlock | UiStackBlock | UiRowBlock | UiColumnsBlock;
141
170
  export type UiToast = {
142
171
  kind: "success" | "error";
143
172
  message: string;
@@ -265,12 +294,23 @@ export declare const ui: {
265
294
  readonly hidden: (options?: UiDocumentOptions) => UiHiddenDocument;
266
295
  readonly text: (text: string, options?: {
267
296
  tone?: "muted";
297
+ icon?: UiIcon;
268
298
  }) => UiTextBlock;
269
- readonly heading: (text: string, level?: 2 | 3) => UiHeadingBlock;
299
+ /** `ui.heading(text, 3)` or `ui.heading(text, { level: 3, icon: "trophy" })`; level defaults to 2. */
300
+ readonly heading: (text: string, levelOrOptions?: 2 | 3 | {
301
+ level?: 2 | 3;
302
+ icon?: UiIcon;
303
+ }) => UiHeadingBlock;
270
304
  readonly stat: (label: string, value: string, options?: {
271
305
  hint?: string;
306
+ icon?: UiIcon;
307
+ size?: StatSize;
272
308
  }) => UiStatBlock;
273
- readonly badge: (label: string, variant?: BadgeVariant) => UiBadgeBlock;
309
+ /** `ui.badge(label, "warning")` or `ui.badge(label, { variant: "warning", icon: "droplets" })`. */
310
+ readonly badge: (label: string, variantOrOptions?: BadgeVariant | {
311
+ variant?: BadgeVariant;
312
+ icon?: UiIcon;
313
+ }) => UiBadgeBlock;
274
314
  readonly list: (items: UiListItem[]) => UiListBlock;
275
315
  /** Pairs keep their order; a plain object is turned into pairs in key order. */
276
316
  readonly keyValue: (pairs: UiKeyValuePair[] | Record<string, string>) => UiKeyValueBlock;
@@ -294,8 +334,15 @@ export declare const ui: {
294
334
  };
295
335
  readonly divider: () => UiDividerBlock;
296
336
  readonly stack: (children: UiBlock[]) => UiStackBlock;
297
- /** Horizontal, wraps on narrow screens. */
298
- readonly row: (children: UiBlock[]) => UiRowBlock;
337
+ /** Horizontal, wraps on narrow screens; `justify: "between"` pushes the last child to the right edge. */
338
+ readonly row: (children: UiBlock[], options?: {
339
+ justify?: RowJustify;
340
+ }) => UiRowBlock;
341
+ /** 2 to 6 equal-width columns, optionally with vertical dividers; throws on any other count. */
342
+ readonly columns: (children: UiBlock[], options?: {
343
+ dividers?: boolean;
344
+ align?: ColumnsAlignment;
345
+ }) => UiColumnsBlock;
299
346
  };
300
347
  /** Toasts for `ui.doc(blocks, { toast })`, at most 200 characters. */
301
348
  export declare const toast: {
package/dist/ui.js CHANGED
@@ -2,8 +2,65 @@ export const UI_VERSION = "v1";
2
2
  export const MAX_UI_BLOCKS = 50;
3
3
  /** Top-level blocks sit at depth 1; a container's children one deeper. */
4
4
  export const MAX_UI_DEPTH = 3;
5
- export const BADGE_VARIANTS = ["default", "secondary", "outline", "destructive"];
5
+ export const BADGE_VARIANTS = ["default", "secondary", "outline", "destructive", "warning"];
6
6
  export const BUTTON_VARIANTS = ["default", "secondary", "outline", "destructive"];
7
+ /** Icons a block may show, by lucide name; CheckCourt draws them in the text color at text size. */
8
+ export const UI_ICONS = [
9
+ "sun",
10
+ "moon",
11
+ "cloud",
12
+ "cloud-sun",
13
+ "cloud-moon",
14
+ "cloud-sun-rain",
15
+ "cloud-rain",
16
+ "cloud-drizzle",
17
+ "cloud-lightning",
18
+ "cloud-snow",
19
+ "cloud-fog",
20
+ "snowflake",
21
+ "wind",
22
+ "droplet",
23
+ "droplets",
24
+ "umbrella",
25
+ "thermometer",
26
+ "sunrise",
27
+ "sunset",
28
+ "check",
29
+ "x",
30
+ "info",
31
+ "alert-triangle",
32
+ "triangle-alert",
33
+ "alert-circle",
34
+ "circle-alert",
35
+ "clock",
36
+ "calendar",
37
+ "map-pin",
38
+ "trophy",
39
+ "users",
40
+ "user",
41
+ "lock",
42
+ "unlock",
43
+ "lock-open",
44
+ "lightbulb",
45
+ "zap",
46
+ "euro",
47
+ "star",
48
+ "heart",
49
+ "bell",
50
+ "flag",
51
+ "activity",
52
+ "timer",
53
+ "ticket",
54
+ "door-open",
55
+ ];
56
+ /** `lg` shows the value as a large display figure, `md` (default) as a regular stat. */
57
+ export const STAT_SIZES = ["md", "lg"];
58
+ export const COLUMNS_ALIGNMENTS = ["start", "center"];
59
+ /** `between` spreads a row's children across the full width, first at the left edge, last at the right. */
60
+ export const ROW_JUSTIFICATIONS = ["start", "between"];
61
+ /** A `columns` block holds 2 to 6 children. */
62
+ export const MIN_COLUMNS = 2;
63
+ export const MAX_COLUMNS = 6;
7
64
  /** CheckCourt caps every render lifetime at this many seconds. */
8
65
  export const UI_CACHE_MAX_AGE_LIMIT = 300;
9
66
  /** Longest badge label of a `court.annotation`. */
@@ -75,6 +132,14 @@ function variantOf(variant, what) {
75
132
  }
76
133
  return variant;
77
134
  }
135
+ function iconOf(icon) {
136
+ if (icon === undefined)
137
+ return undefined;
138
+ if (!UI_ICONS.includes(icon)) {
139
+ throw new TypeError(`Unknown icon ${String(icon)}; use one of UI_ICONS`);
140
+ }
141
+ return icon;
142
+ }
78
143
  function entriesOf(list, what) {
79
144
  if (!Array.isArray(list))
80
145
  throw new TypeError(`${what} must be an array`);
@@ -157,19 +222,31 @@ export const ui = {
157
222
  return compact({ ui: UI_VERSION, hidden: true, toast: options.toast, cache: cacheOf(options.maxAge) });
158
223
  },
159
224
  text(text, options = {}) {
160
- return compact({ type: "text", text, tone: options.tone });
225
+ return compact({ type: "text", text, tone: options.tone, icon: iconOf(options.icon) });
161
226
  },
162
- heading(text, level = 2) {
163
- return { type: "heading", text, level };
227
+ /** `ui.heading(text, 3)` or `ui.heading(text, { level: 3, icon: "trophy" })`; level defaults to 2. */
228
+ heading(text, levelOrOptions = 2) {
229
+ const options = typeof levelOrOptions === "object" ? levelOrOptions : { level: levelOrOptions };
230
+ return compact({ type: "heading", text, level: options.level ?? 2, icon: iconOf(options.icon) });
164
231
  },
165
232
  stat(label, value, options = {}) {
166
- return compact({ type: "stat", label, value, hint: options.hint });
233
+ if (options.size !== undefined && !STAT_SIZES.includes(options.size)) {
234
+ throw new TypeError(`stat size must be one of ${STAT_SIZES.join(", ")}`);
235
+ }
236
+ return compact({ type: "stat", label, value, hint: options.hint, icon: iconOf(options.icon), size: options.size });
167
237
  },
168
- badge(label, variant) {
169
- return compact({ type: "badge", label, variant });
238
+ /** `ui.badge(label, "warning")` or `ui.badge(label, { variant: "warning", icon: "droplets" })`. */
239
+ badge(label, variantOrOptions) {
240
+ const options = typeof variantOrOptions === "object" ? variantOrOptions : { variant: variantOrOptions };
241
+ return compact({
242
+ type: "badge",
243
+ label,
244
+ variant: variantOf(options.variant, "badge variant"),
245
+ icon: iconOf(options.icon),
246
+ });
170
247
  },
171
248
  list(items) {
172
- return { type: "list", items: items.map((item) => compact({ ...item })) };
249
+ return { type: "list", items: items.map((item) => compact({ ...item, icon: iconOf(item.icon) })) };
173
250
  },
174
251
  /** Pairs keep their order; a plain object is turned into pairs in key order. */
175
252
  keyValue(pairs) {
@@ -198,9 +275,22 @@ export const ui = {
198
275
  stack(children) {
199
276
  return { type: "stack", children };
200
277
  },
201
- /** Horizontal, wraps on narrow screens. */
202
- row(children) {
203
- return { type: "row", children };
278
+ /** Horizontal, wraps on narrow screens; `justify: "between"` pushes the last child to the right edge. */
279
+ row(children, options = {}) {
280
+ if (options.justify !== undefined && !ROW_JUSTIFICATIONS.includes(options.justify)) {
281
+ throw new TypeError(`row justify must be one of ${ROW_JUSTIFICATIONS.join(", ")}`);
282
+ }
283
+ return compact({ type: "row", children, justify: options.justify });
284
+ },
285
+ /** 2 to 6 equal-width columns, optionally with vertical dividers; throws on any other count. */
286
+ columns(children, options = {}) {
287
+ if (!Array.isArray(children) || children.length < MIN_COLUMNS || children.length > MAX_COLUMNS) {
288
+ throw new RangeError(`columns takes ${MIN_COLUMNS} to ${MAX_COLUMNS} children`);
289
+ }
290
+ if (options.align !== undefined && !COLUMNS_ALIGNMENTS.includes(options.align)) {
291
+ throw new TypeError(`columns align must be one of ${COLUMNS_ALIGNMENTS.join(", ")}`);
292
+ }
293
+ return compact({ type: "columns", children, dividers: options.dividers, align: options.align });
204
294
  },
205
295
  };
206
296
  /** Toasts for `ui.doc(blocks, { toast })`, at most 200 characters. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkcourt/sdk",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Official TypeScript SDK for the CheckCourt app platform",
5
5
  "private": false,
6
6
  "license": "MIT",