@krapwoo/ds-viewer 0.2.0 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krapwoo/ds-viewer",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "A browsable, always-current catalog for an Expo / React Native design system.",
@@ -29,6 +29,7 @@
29
29
  "starter-kit",
30
30
  "dist/cli",
31
31
  "dist/config",
32
+ "dist/native",
32
33
  "config/index.ts",
33
34
  "README.md",
34
35
  "LICENSE"
@@ -51,6 +52,7 @@
51
52
  },
52
53
  "devDependencies": {
53
54
  "@types/node": "^22.0.0",
55
+ "@types/react": "^19.0.0",
54
56
  "puppeteer": "^25.12.0"
55
57
  },
56
58
  "scripts": {
@@ -59,6 +61,8 @@
59
61
  "typecheck": "tsc -p tsconfig.json --noEmit",
60
62
  "kit:dev": "npm run build && cd kit-host && node node_modules/@krapwoo/ds-viewer/dist/cli/main.js dev",
61
63
  "check:catalog": "npm run build && node scripts/checkCatalogConsole.mjs kit-host",
64
+ "check:doctor": "npm run build && cd kit-host && node node_modules/@krapwoo/ds-viewer/dist/cli/main.js doctor --ci",
65
+ "check:types": "tsc --noEmit -p kit-host",
62
66
  "release": "node scripts/release.mjs"
63
67
  }
64
68
  }
@@ -1,30 +1,12 @@
1
1
  import React from 'react';
2
- import { defineCatalogPage, type ComparisonDef } from '@krapwoo/ds-viewer';
2
+ import { defineCatalogPage, grid } from '@krapwoo/ds-viewer';
3
3
  import { Avatar } from './Avatar';
4
4
  import { DS_SEMANTIC } from '../../tokens';
5
5
 
6
- // Grid comparisons: each cell is a real instance with exactly the props its row and column name.
7
- // Never build these by multiplying `variants` with `states` — those are pre-rendered nodes and
8
- // cannot combine.
9
- function grid(
10
- rowLabel: string,
11
- columnLabel: string,
12
- rows: { key: string; label: string }[],
13
- columns: { key: string; label: string }[],
14
- cell: (row: string, column: string) => React.ReactNode,
15
- size?: ComparisonDef['size'],
16
- ): ComparisonDef {
17
- return {
18
- rowLabel,
19
- columnLabel,
20
- rows,
21
- columns,
22
- cells: rows.flatMap((row) => columns.map((column) => ({ rowKey: row.key, columnKey: column.key, node: cell(row.key, column.key) }))),
23
- size,
24
- };
25
- }
26
-
27
6
  // Avatar: content Kind × Size (Avatar's `size` is continuous; 24 / 40 default / 64 is the sweep).
7
+ // Neither axis names a real prop — "Kind" is which of 3 optional props is set, not one enum, and
8
+ // `size` has no fixed option set — so both stay unbound. An unbound axis is fully valid; `doctor`
9
+ // only ever reports how many examples are unbound as information, never as an error or a warning.
28
10
  const AVATAR_COMPARISON = grid(
29
11
  'Kind',
30
12
  'Size',
@@ -1,29 +1,8 @@
1
1
  import React from 'react';
2
- import { defineCatalogPage, type ComparisonDef } from '@krapwoo/ds-viewer';
2
+ import { defineCatalogPage, grid } from '@krapwoo/ds-viewer';
3
3
  import { Badge } from './Badge';
4
4
  import type { BadgeVariant } from './Badge.types';
5
5
 
6
- // Grid comparisons: each cell is a real instance with exactly the props its row and column name.
7
- // Never build these by multiplying `variants` with `states` — those are pre-rendered nodes and
8
- // cannot combine.
9
- function grid(
10
- rowLabel: string,
11
- columnLabel: string,
12
- rows: { key: string; label: string }[],
13
- columns: { key: string; label: string }[],
14
- cell: (row: string, column: string) => React.ReactNode,
15
- size?: ComparisonDef['size'],
16
- ): ComparisonDef {
17
- return {
18
- rowLabel,
19
- columnLabel,
20
- rows,
21
- columns,
22
- cells: rows.flatMap((row) => columns.map((column) => ({ rowKey: row.key, columnKey: column.key, node: cell(row.key, column.key) }))),
23
- size,
24
- };
25
- }
26
-
27
6
  // Badge: Tone × Icon layout. One icon per tone, matching the Variants examples.
28
7
  const BADGE_TONES: { key: BadgeVariant; label: string; text: string; icon: React.ComponentProps<typeof Badge>['leadingIcon'] }[] = [
29
8
  { key: 'neutral', label: 'Neutral', text: 'Local', icon: 'pin' },
@@ -32,10 +11,23 @@ const BADGE_TONES: { key: BadgeVariant; label: string; text: string; icon: React
32
11
  { key: 'warning', label: 'Warning', text: 'Delayed', icon: 'triangle-alert' },
33
12
  { key: 'negative', label: 'Negative', text: 'Suspended', icon: 'circle-slash' },
34
13
  ];
14
+ // `rows` is bound to Badge's own `variant` prop (design §4) — written as its own literal array
15
+ // (not `BADGE_TONES.map(...)`) so `doctor`'s static reader can check it without evaluating a
16
+ // function call; `BADGE_TONES` above still drives the real rendering in `cell` below.
17
+ const BADGE_ROWS = {
18
+ prop: 'variant',
19
+ items: [
20
+ { key: 'neutral', label: 'Neutral' },
21
+ { key: 'info', label: 'Info' },
22
+ { key: 'positive', label: 'Positive' },
23
+ { key: 'warning', label: 'Warning' },
24
+ { key: 'negative', label: 'Negative' },
25
+ ],
26
+ } as const;
35
27
  const BADGE_COMPARISON = grid(
36
28
  'Tone',
37
29
  'Icon',
38
- BADGE_TONES.map(({ key, label }) => ({ key, label })),
30
+ BADGE_ROWS,
39
31
  [
40
32
  { key: 'label-only', label: 'Label only' },
41
33
  { key: 'leading-icon', label: 'Leading icon' },
@@ -235,7 +235,7 @@ const styles = StyleSheet.create({
235
235
  },
236
236
  // Translucent pressed overlay — sits above the banner bg so the tinted bg shows through.
237
237
  pressOverlay: {
238
- ...StyleSheet.absoluteFillObject,
238
+ ...StyleSheet.absoluteFill,
239
239
  backgroundColor: DS_SEMANTIC.interaction.pressed,
240
240
  borderRadius: DS_RADIUS.medium,
241
241
  },
@@ -126,8 +126,8 @@ export function BottomSheet({ visible, onDismiss, header, children, footer, styl
126
126
  }
127
127
 
128
128
  const styles = StyleSheet.create({
129
- overlay: { ...StyleSheet.absoluteFillObject, justifyContent: 'flex-end' },
130
- backdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: DS_SEMANTIC.element.overlayBackdrop },
129
+ overlay: { ...StyleSheet.absoluteFill, justifyContent: 'flex-end' },
130
+ backdrop: { ...StyleSheet.absoluteFill, backgroundColor: DS_SEMANTIC.element.overlayBackdrop },
131
131
  sheet: {
132
132
  maxHeight: MAX_HEIGHT_PERCENT,
133
133
  backgroundColor: DS_SEMANTIC.surface.white,
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { View, StyleSheet } from 'react-native';
3
- import { defineCatalogPage, type ComparisonDef } from '@krapwoo/ds-viewer';
3
+ import { defineCatalogPage, grid } from '@krapwoo/ds-viewer';
4
4
  import { Button } from './Button';
5
5
  import type { ButtonVariant } from './Button.types';
6
6
  import { DS_SEMANTIC } from '../../tokens';
@@ -16,38 +16,24 @@ const styles = StyleSheet.create({
16
16
  },
17
17
  });
18
18
 
19
- // Grid comparisons: each cell is a real instance with exactly the props its row and column name.
20
- // Never build these by multiplying `variants` with `states` — those are pre-rendered nodes and
21
- // cannot combine.
22
- function grid(
23
- rowLabel: string,
24
- columnLabel: string,
25
- rows: { key: string; label: string }[],
26
- columns: { key: string; label: string }[],
27
- cell: (row: string, column: string) => React.ReactNode,
28
- size?: ComparisonDef['size'],
29
- ): ComparisonDef {
30
- return {
31
- rowLabel,
32
- columnLabel,
33
- rows,
34
- columns,
35
- cells: rows.flatMap((row) => columns.map((column) => ({ rowKey: row.key, columnKey: column.key, node: cell(row.key, column.key) }))),
36
- size,
37
- };
38
- }
39
-
40
19
  // Button: Variant × State. `white` keeps the dark backdrop its variant requires.
20
+ // `rows` is bound to Button's own `variant` prop (design §4): each row key below is an actual
21
+ // `ButtonVariant` option, so `doctor` can tell when one is renamed or removed. `columns` names no
22
+ // single prop (disabled/loading are two separate booleans, not one "State" prop) and stays unbound
23
+ // — unbound axes are just as valid, and `doctor` never flags one.
41
24
  const BUTTON_COMPARISON = grid(
42
25
  'Variant',
43
26
  'State',
44
- [
45
- { key: 'primary', label: 'Primary' },
46
- { key: 'secondary', label: 'Secondary' },
47
- { key: 'tertiary', label: 'Tertiary' },
48
- { key: 'white', label: 'White' },
49
- { key: 'ghost', label: 'Ghost' },
50
- ],
27
+ {
28
+ prop: 'variant',
29
+ items: [
30
+ { key: 'primary', label: 'Primary' },
31
+ { key: 'secondary', label: 'Secondary' },
32
+ { key: 'tertiary', label: 'Tertiary' },
33
+ { key: 'white', label: 'White' },
34
+ { key: 'ghost', label: 'Ghost' },
35
+ ],
36
+ },
51
37
  [
52
38
  { key: 'default', label: 'Default' },
53
39
  { key: 'disabled', label: 'Disabled' },
@@ -65,12 +65,12 @@ export function Dialog({ visible, onDismiss, children, style }: DialogProps) {
65
65
 
66
66
  const styles = StyleSheet.create({
67
67
  overlay: {
68
- ...StyleSheet.absoluteFillObject,
68
+ ...StyleSheet.absoluteFill,
69
69
  alignItems: 'center',
70
70
  justifyContent: 'center',
71
71
  padding: DS_SPACING[800],
72
72
  },
73
- backdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: DS_SEMANTIC.element.overlayBackdrop },
73
+ backdrop: { ...StyleSheet.absoluteFill, backgroundColor: DS_SEMANTIC.element.overlayBackdrop },
74
74
  card: {
75
75
  width: '100%',
76
76
  maxWidth: 400,
@@ -1,36 +1,22 @@
1
1
  import React from 'react';
2
- import { defineCatalogPage, type ComparisonDef } from '@krapwoo/ds-viewer';
2
+ import { defineCatalogPage, grid } from '@krapwoo/ds-viewer';
3
3
  import { Pill } from './Pill';
4
4
 
5
- // Grid comparisons: each cell is a real instance with exactly the props its row and column name.
6
- // Never build these by multiplying `variants` with `states` — those are pre-rendered nodes and
7
- // cannot combine.
8
- function grid(
9
- rowLabel: string,
10
- columnLabel: string,
11
- rows: { key: string; label: string }[],
12
- columns: { key: string; label: string }[],
13
- cell: (row: string, column: string) => React.ReactNode,
14
- size?: ComparisonDef['size'],
15
- ): ComparisonDef {
16
- return {
17
- rowLabel,
18
- columnLabel,
19
- rows,
20
- columns,
21
- cells: rows.flatMap((row) => columns.map((column) => ({ rowKey: row.key, columnKey: column.key, node: cell(row.key, column.key) }))),
22
- size,
23
- };
24
- }
25
-
26
5
  // Pill: Selection × State.
6
+ // `rows` is bound to Pill's own `variant` prop (design §4); the second item's key is
7
+ // `'not_selected'` (underscore), matching `PillVariant` exactly — not `'not-selected'`, which is
8
+ // only this page's own label-ish spelling and would fail `doctor`'s "item key must be a real
9
+ // option" check.
27
10
  const PILL_COMPARISON = grid(
28
11
  'Selection',
29
12
  'State',
30
- [
31
- { key: 'selected', label: 'Selected' },
32
- { key: 'not-selected', label: 'Not selected' },
33
- ],
13
+ {
14
+ prop: 'variant',
15
+ items: [
16
+ { key: 'selected', label: 'Selected' },
17
+ { key: 'not_selected', label: 'Not selected' },
18
+ ],
19
+ },
34
20
  [
35
21
  { key: 'icon-text', label: 'Icon + Text' },
36
22
  { key: 'icon-only', label: 'Icon-only' },