@oxyhq/bloom 0.5.0 → 0.5.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.
Files changed (98) hide show
  1. package/lib/commonjs/fonts/FontLoader.js +6 -5
  2. package/lib/commonjs/fonts/FontLoader.js.map +1 -1
  3. package/lib/commonjs/fonts/apply-font-faces.js +4 -4
  4. package/lib/commonjs/fonts/apply-font-faces.web.js +13 -12
  5. package/lib/commonjs/fonts/apply-font-faces.web.js.map +1 -1
  6. package/lib/commonjs/fonts/font-assets.js +2 -2
  7. package/lib/commonjs/fonts/font-data.web.js +22 -0
  8. package/lib/commonjs/fonts/font-data.web.js.map +1 -0
  9. package/lib/module/fonts/FontLoader.js +6 -5
  10. package/lib/module/fonts/FontLoader.js.map +1 -1
  11. package/lib/module/fonts/apply-font-faces.js +4 -4
  12. package/lib/module/fonts/apply-font-faces.web.js +13 -10
  13. package/lib/module/fonts/apply-font-faces.web.js.map +1 -1
  14. package/lib/module/fonts/font-assets.js +2 -2
  15. package/lib/module/fonts/font-data.web.js +18 -0
  16. package/lib/module/fonts/font-data.web.js.map +1 -0
  17. package/lib/module/fonts/index.web.js +4 -4
  18. package/lib/typescript/commonjs/fonts/FontLoader.d.ts.map +1 -1
  19. package/lib/typescript/commonjs/fonts/apply-font-faces.web.d.ts +8 -1
  20. package/lib/typescript/commonjs/fonts/apply-font-faces.web.d.ts.map +1 -1
  21. package/lib/typescript/commonjs/fonts/font-data.web.d.ts +5 -0
  22. package/lib/typescript/commonjs/fonts/font-data.web.d.ts.map +1 -0
  23. package/lib/typescript/module/fonts/FontLoader.d.ts.map +1 -1
  24. package/lib/typescript/module/fonts/apply-font-faces.web.d.ts +8 -1
  25. package/lib/typescript/module/fonts/apply-font-faces.web.d.ts.map +1 -1
  26. package/lib/typescript/module/fonts/font-data.web.d.ts +5 -0
  27. package/lib/typescript/module/fonts/font-data.web.d.ts.map +1 -0
  28. package/package.json +36 -5
  29. package/src/avatar/Avatar.stories.tsx +69 -0
  30. package/src/bottom-sheet/BottomSheet.stories.tsx +92 -0
  31. package/src/button/Button.stories.tsx +94 -0
  32. package/src/context-menu/ContextMenu.stories.tsx +71 -0
  33. package/src/dialog/Dialog.stories.tsx +112 -0
  34. package/src/fonts/FontLoader.tsx +6 -5
  35. package/src/fonts/apply-font-faces.ts +4 -4
  36. package/src/fonts/apply-font-faces.web.ts +18 -10
  37. package/src/fonts/font-assets.ts +2 -2
  38. package/src/fonts/font-data.web.ts +15 -0
  39. package/src/fonts/index.web.ts +4 -4
  40. package/src/loading/Loading.stories.tsx +60 -0
  41. package/src/menu/Menu.stories.tsx +79 -0
  42. package/src/prompt-input/PromptInput.stories.tsx +82 -0
  43. package/src/select/Select.stories.tsx +84 -0
  44. package/src/settings-list/SettingsList.stories.tsx +106 -0
  45. package/src/text-field/TextField.stories.tsx +90 -0
  46. package/src/toast/Toast.stories.tsx +109 -0
  47. package/lib/commonjs/fonts/assets/BlomusModernus-Bold.woff2 +0 -0
  48. package/lib/commonjs/fonts/assets/BlomusModernus-Regular.woff2 +0 -0
  49. package/lib/commonjs/fonts/assets/GeistMono-Variable.woff2 +0 -0
  50. package/lib/commonjs/fonts/assets/InterVariable.woff2 +0 -0
  51. package/lib/module/fonts/assets/BlomusModernus-Bold.woff2 +0 -0
  52. package/lib/module/fonts/assets/BlomusModernus-Regular.woff2 +0 -0
  53. package/lib/module/fonts/assets/GeistMono-Variable.woff2 +0 -0
  54. package/lib/module/fonts/assets/InterVariable.woff2 +0 -0
  55. package/lib/typescript/commonjs/__tests__/BloomThemeProvider.fonts-web.test.d.ts +0 -5
  56. package/lib/typescript/commonjs/__tests__/BloomThemeProvider.fonts-web.test.d.ts.map +0 -1
  57. package/lib/typescript/commonjs/__tests__/BloomThemeProvider.test.d.ts +0 -2
  58. package/lib/typescript/commonjs/__tests__/BloomThemeProvider.test.d.ts.map +0 -1
  59. package/lib/typescript/commonjs/__tests__/BottomSheet.test.d.ts +0 -2
  60. package/lib/typescript/commonjs/__tests__/BottomSheet.test.d.ts.map +0 -1
  61. package/lib/typescript/commonjs/__tests__/Button.test.d.ts +0 -2
  62. package/lib/typescript/commonjs/__tests__/Button.test.d.ts.map +0 -1
  63. package/lib/typescript/commonjs/__tests__/Code.test.d.ts +0 -2
  64. package/lib/typescript/commonjs/__tests__/Code.test.d.ts.map +0 -1
  65. package/lib/typescript/commonjs/__tests__/Dialog.test.d.ts +0 -2
  66. package/lib/typescript/commonjs/__tests__/Dialog.test.d.ts.map +0 -1
  67. package/lib/typescript/commonjs/__tests__/FontLoader.native.test.d.ts +0 -2
  68. package/lib/typescript/commonjs/__tests__/FontLoader.native.test.d.ts.map +0 -1
  69. package/lib/typescript/commonjs/__tests__/Pre.test.d.ts +0 -2
  70. package/lib/typescript/commonjs/__tests__/Pre.test.d.ts.map +0 -1
  71. package/lib/typescript/commonjs/__tests__/SettingsList.test.d.ts +0 -2
  72. package/lib/typescript/commonjs/__tests__/SettingsList.test.d.ts.map +0 -1
  73. package/lib/typescript/commonjs/__tests__/apply-font-faces.test.d.ts +0 -5
  74. package/lib/typescript/commonjs/__tests__/apply-font-faces.test.d.ts.map +0 -1
  75. package/lib/typescript/commonjs/__tests__/theme.test.d.ts +0 -2
  76. package/lib/typescript/commonjs/__tests__/theme.test.d.ts.map +0 -1
  77. package/lib/typescript/module/__tests__/BloomThemeProvider.fonts-web.test.d.ts +0 -5
  78. package/lib/typescript/module/__tests__/BloomThemeProvider.fonts-web.test.d.ts.map +0 -1
  79. package/lib/typescript/module/__tests__/BloomThemeProvider.test.d.ts +0 -2
  80. package/lib/typescript/module/__tests__/BloomThemeProvider.test.d.ts.map +0 -1
  81. package/lib/typescript/module/__tests__/BottomSheet.test.d.ts +0 -2
  82. package/lib/typescript/module/__tests__/BottomSheet.test.d.ts.map +0 -1
  83. package/lib/typescript/module/__tests__/Button.test.d.ts +0 -2
  84. package/lib/typescript/module/__tests__/Button.test.d.ts.map +0 -1
  85. package/lib/typescript/module/__tests__/Code.test.d.ts +0 -2
  86. package/lib/typescript/module/__tests__/Code.test.d.ts.map +0 -1
  87. package/lib/typescript/module/__tests__/Dialog.test.d.ts +0 -2
  88. package/lib/typescript/module/__tests__/Dialog.test.d.ts.map +0 -1
  89. package/lib/typescript/module/__tests__/FontLoader.native.test.d.ts +0 -2
  90. package/lib/typescript/module/__tests__/FontLoader.native.test.d.ts.map +0 -1
  91. package/lib/typescript/module/__tests__/Pre.test.d.ts +0 -2
  92. package/lib/typescript/module/__tests__/Pre.test.d.ts.map +0 -1
  93. package/lib/typescript/module/__tests__/SettingsList.test.d.ts +0 -2
  94. package/lib/typescript/module/__tests__/SettingsList.test.d.ts.map +0 -1
  95. package/lib/typescript/module/__tests__/apply-font-faces.test.d.ts +0 -5
  96. package/lib/typescript/module/__tests__/apply-font-faces.test.d.ts.map +0 -1
  97. package/lib/typescript/module/__tests__/theme.test.d.ts +0 -2
  98. package/lib/typescript/module/__tests__/theme.test.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"names":["blomusModernusRegular","blomusModernusBold","interVariable","geistMonoVariable"],"sourceRoot":"../../../src","sources":["fonts/font-data.web.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAO,MAAMA,qBAA6B,GAAG,ioqGAAioqG;AAC9qqG,OAAO,MAAMC,kBAA0B,GAAG,qlqGAAqlqG;AAC/nqG,OAAO,MAAMC,aAAqB,GAAG,i39DAAi39D;AACt59D,OAAO,MAAMC,iBAAyB,GAAG,i9tCAAi9tC","ignoreList":[]}
@@ -3,10 +3,10 @@
3
3
  // Web variant of the `./fonts` barrel.
4
4
  //
5
5
  // The default barrel (`./index.ts`) re-exports `applyFontFaces` from
6
- // `./apply-font-faces`, which on native resolves to a no-op stub Metro
7
- // cannot parse `.woff2` module-level imports. The web fork explicitly
8
- // reaches for `./apply-font-faces.web`, which performs the real `@font-face`
9
- // injection.
6
+ // `./apply-font-faces`, which on native is a no-op stub. The web fork
7
+ // explicitly reaches for `./apply-font-faces.web`, which performs the real
8
+ // `@font-face` injection using inlined base64 data URLs (see
9
+ // `font-data.web.ts`).
10
10
  //
11
11
  // Web bundlers select this file via the `"browser"` condition in
12
12
  // `package.json`'s `exports['./fonts']`; native bundlers fall through to
@@ -1 +1 @@
1
- {"version":3,"file":"FontLoader.d.ts","sourceRoot":"","sources":["../../../../src/fonts/FontLoader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAStC,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAOhE"}
1
+ {"version":3,"file":"FontLoader.d.ts","sourceRoot":"","sources":["../../../../src/fonts/FontLoader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAUtC,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAOhE"}
@@ -3,10 +3,17 @@
3
3
  *
4
4
  * Web-only. The native counterpart in `apply-font-faces.ts` is a no-op
5
5
  * stub — Metro cannot parse `.woff2` imports, so the file split keeps
6
- * those imports out of the native bundle entirely. Idempotent: safe to
6
+ * the font payload out of the native bundle entirely. Idempotent: safe to
7
7
  * call multiple times; subsequent calls early-return after the
8
8
  * `<style id="bloom-fonts">` tag has been mounted. SSR-safe via the
9
9
  * `typeof document === 'undefined'` guard.
10
+ *
11
+ * The font URLs come from `./font-data.web`, a generated module that
12
+ * embeds the woff2 bytes as base64 data URLs. Inlining (rather than
13
+ * relying on bundler asset loaders for `.woff2`) means consumers do not
14
+ * have to extend Metro's `assetExts` or add a webpack/Vite asset rule —
15
+ * the published JS is self-contained. See `scripts/generate-font-data.mjs`
16
+ * for the rationale and trade-offs.
10
17
  */
11
18
  export declare function applyFontFaces(): void;
12
19
  //# sourceMappingURL=apply-font-faces.web.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"apply-font-faces.web.d.ts","sourceRoot":"","sources":["../../../../src/fonts/apply-font-faces.web.ts"],"names":[],"mappings":"AASA;;;;;;;;;GASG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAkBrC"}
1
+ {"version":3,"file":"apply-font-faces.web.d.ts","sourceRoot":"","sources":["../../../../src/fonts/apply-font-faces.web.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAkBrC"}
@@ -0,0 +1,5 @@
1
+ export declare const blomusModernusRegular: string;
2
+ export declare const blomusModernusBold: string;
3
+ export declare const interVariable: string;
4
+ export declare const geistMonoVariable: string;
5
+ //# sourceMappingURL=font-data.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-data.web.d.ts","sourceRoot":"","sources":["../../../../src/fonts/font-data.web.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,qBAAqB,EAAE,MAA0oqG,CAAC;AAC/qqG,eAAO,MAAM,kBAAkB,EAAE,MAA8lqG,CAAC;AAChoqG,eAAO,MAAM,aAAa,EAAE,MAA039D,CAAC;AACv59D,eAAO,MAAM,iBAAiB,EAAE,MAA09tC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"FontLoader.d.ts","sourceRoot":"","sources":["../../../../src/fonts/FontLoader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAStC,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAOhE"}
1
+ {"version":3,"file":"FontLoader.d.ts","sourceRoot":"","sources":["../../../../src/fonts/FontLoader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAUtC,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAOhE"}
@@ -3,10 +3,17 @@
3
3
  *
4
4
  * Web-only. The native counterpart in `apply-font-faces.ts` is a no-op
5
5
  * stub — Metro cannot parse `.woff2` imports, so the file split keeps
6
- * those imports out of the native bundle entirely. Idempotent: safe to
6
+ * the font payload out of the native bundle entirely. Idempotent: safe to
7
7
  * call multiple times; subsequent calls early-return after the
8
8
  * `<style id="bloom-fonts">` tag has been mounted. SSR-safe via the
9
9
  * `typeof document === 'undefined'` guard.
10
+ *
11
+ * The font URLs come from `./font-data.web`, a generated module that
12
+ * embeds the woff2 bytes as base64 data URLs. Inlining (rather than
13
+ * relying on bundler asset loaders for `.woff2`) means consumers do not
14
+ * have to extend Metro's `assetExts` or add a webpack/Vite asset rule —
15
+ * the published JS is self-contained. See `scripts/generate-font-data.mjs`
16
+ * for the rationale and trade-offs.
10
17
  */
11
18
  export declare function applyFontFaces(): void;
12
19
  //# sourceMappingURL=apply-font-faces.web.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"apply-font-faces.web.d.ts","sourceRoot":"","sources":["../../../../src/fonts/apply-font-faces.web.ts"],"names":[],"mappings":"AASA;;;;;;;;;GASG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAkBrC"}
1
+ {"version":3,"file":"apply-font-faces.web.d.ts","sourceRoot":"","sources":["../../../../src/fonts/apply-font-faces.web.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAkBrC"}
@@ -0,0 +1,5 @@
1
+ export declare const blomusModernusRegular: string;
2
+ export declare const blomusModernusBold: string;
3
+ export declare const interVariable: string;
4
+ export declare const geistMonoVariable: string;
5
+ //# sourceMappingURL=font-data.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-data.web.d.ts","sourceRoot":"","sources":["../../../../src/fonts/font-data.web.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,qBAAqB,EAAE,MAA0oqG,CAAC;AAC/qqG,eAAO,MAAM,kBAAkB,EAAE,MAA8lqG,CAAC;AAChoqG,eAAO,MAAM,aAAa,EAAE,MAA039D,CAAC;AACv59D,eAAO,MAAM,iBAAiB,EAAE,MAA09tC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -582,20 +582,42 @@
582
582
  "license": "MIT",
583
583
  "scripts": {
584
584
  "generate:exports": "node scripts/generate-platform-exports.mjs",
585
- "prebuild": "node scripts/generate-platform-exports.mjs",
585
+ "generate:font-data": "node scripts/generate-font-data.mjs",
586
+ "prebuild": "node scripts/generate-platform-exports.mjs && node scripts/generate-font-data.mjs",
586
587
  "build": "bob build",
587
588
  "test": "jest",
589
+ "pretest": "node scripts/generate-font-data.mjs",
588
590
  "typescript": "tsc --noEmit",
591
+ "pretypescript": "node scripts/generate-font-data.mjs",
589
592
  "clean": "rm -rf lib",
590
593
  "prepare": "bob build || true",
591
- "release": "rm -rf lib && npm run build && release-it"
594
+ "release": "rm -rf lib && bun run build && release-it",
595
+ "storybook": "storybook dev -p 6006 --no-open",
596
+ "storybook:build": "storybook build -o storybook-static"
597
+ },
598
+ "release-it": {
599
+ "git": {
600
+ "tagName": "@oxyhq/bloom@${version}",
601
+ "tagAnnotation": "Release @oxyhq/bloom@${version}",
602
+ "commitMessage": "chore: release @oxyhq/bloom@${version}"
603
+ },
604
+ "github": {
605
+ "release": true,
606
+ "releaseName": "@oxyhq/bloom@${version}"
607
+ },
608
+ "npm": {
609
+ "publish": true
610
+ }
592
611
  },
593
612
  "devDependencies": {
613
+ "@storybook/addon-docs": "^10",
614
+ "@storybook/react-vite": "^10",
594
615
  "@testing-library/react-native": "^13.3.3",
595
616
  "@types/jest": "^30.0.0",
596
617
  "@types/react": "~19.1.0",
597
618
  "@types/react-dom": "^19.2.3",
598
619
  "@types/react-native": "*",
620
+ "@vitejs/plugin-react": "^6.0.2",
599
621
  "expo-font": "^56.0.5",
600
622
  "jest": "^30.3.0",
601
623
  "jest-environment-jsdom": "^30.4.1",
@@ -607,12 +629,15 @@
607
629
  "react-native-reanimated": "^4.2.2",
608
630
  "react-native-safe-area-context": "~5.6.0",
609
631
  "react-native-svg": "^15.15.3",
632
+ "react-native-web": "^0.21.2",
610
633
  "react-test-renderer": "^19.2.0",
611
634
  "release-it": "^19.0.6",
612
635
  "sonner": "^2.0.3",
613
636
  "sonner-native": "^0.23.1",
637
+ "storybook": "^10",
614
638
  "ts-jest": "^29.4.6",
615
- "typescript": "~5.9.2"
639
+ "typescript": "~5.9.2",
640
+ "vite": "^7"
616
641
  },
617
642
  "peerDependencies": {
618
643
  "expo": "*",
@@ -656,6 +681,7 @@
656
681
  "react-native-builder-bob": {
657
682
  "source": "src",
658
683
  "output": "lib",
684
+ "exclude": "{**/{__tests__,__fixtures__,__mocks__}/**,**/*.stories.{ts,tsx},**/*.{test,spec}.{ts,tsx},**/*.woff2}",
659
685
  "targets": [
660
686
  [
661
687
  "commonjs",
@@ -669,7 +695,12 @@
669
695
  "esm": true
670
696
  }
671
697
  ],
672
- "typescript"
698
+ [
699
+ "typescript",
700
+ {
701
+ "project": "tsconfig.build.json"
702
+ }
703
+ ]
673
704
  ]
674
705
  },
675
706
  "dependencies": {
@@ -0,0 +1,69 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { Avatar } from './Avatar';
6
+
7
+ const meta: Meta<typeof Avatar> = {
8
+ title: 'Components/Avatar',
9
+ component: Avatar,
10
+ argTypes: {
11
+ size: {
12
+ control: { type: 'number', min: 16, max: 256, step: 4 },
13
+ },
14
+ shape: {
15
+ control: 'select',
16
+ options: ['circle', 'squircle'],
17
+ },
18
+ verified: { control: 'boolean' },
19
+ },
20
+ };
21
+
22
+ export default meta;
23
+
24
+ type Story = StoryObj<typeof Avatar>;
25
+
26
+ const SAMPLE_URI =
27
+ 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=200&h=200&fit=crop';
28
+
29
+ export const Basic: Story = {
30
+ args: { size: 64, name: 'Nate Isern' },
31
+ };
32
+
33
+ export const FromUri: Story = {
34
+ args: { size: 64, uri: SAMPLE_URI },
35
+ name: 'From URI',
36
+ };
37
+
38
+ export const Initials: Story = {
39
+ args: { size: 64, name: 'Ada Lovelace' },
40
+ };
41
+
42
+ export const Squircle: Story = {
43
+ args: { size: 64, name: 'Ada Lovelace', shape: 'squircle' },
44
+ };
45
+
46
+ export const Sizes: Story = {
47
+ render: () => (
48
+ <View style={{ flexDirection: 'row', gap: 12, alignItems: 'center' }}>
49
+ <Avatar size={24} name="Ada" />
50
+ <Avatar size={32} name="Ada" />
51
+ <Avatar size={40} name="Ada" />
52
+ <Avatar size={56} name="Ada" />
53
+ <Avatar size={80} name="Ada" />
54
+ <Avatar size={120} name="Ada" />
55
+ </View>
56
+ ),
57
+ };
58
+
59
+ export const Composition: Story = {
60
+ render: () => (
61
+ <View style={{ flexDirection: 'row', gap: 12, alignItems: 'center' }}>
62
+ <Avatar size={48} name="Ada Lovelace" />
63
+ <Avatar size={48} name="Grace Hopper" />
64
+ <Avatar size={48} name="Alan Turing" />
65
+ <Avatar size={48} name="Linus Torvalds" />
66
+ <Avatar size={48} name="Margaret Hamilton" />
67
+ </View>
68
+ ),
69
+ };
@@ -0,0 +1,92 @@
1
+ import React, { useRef } from 'react';
2
+ import { Text, View } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { Button } from '../button';
6
+ import { BottomSheet, type BottomSheetRef } from './index';
7
+
8
+ const meta: Meta<typeof BottomSheet> = {
9
+ title: 'Components/BottomSheet',
10
+ component: BottomSheet,
11
+ };
12
+
13
+ export default meta;
14
+
15
+ type Story = StoryObj<typeof BottomSheet>;
16
+
17
+ function BasicSheet() {
18
+ const ref = useRef<BottomSheetRef>(null);
19
+ return (
20
+ <>
21
+ <Button onPress={() => ref.current?.present()}>Open sheet</Button>
22
+ <BottomSheet ref={ref}>
23
+ <View style={{ padding: 24, gap: 12 }}>
24
+ <Text style={{ fontSize: 20, fontWeight: '700' }}>Bottom sheet</Text>
25
+ <Text>
26
+ Pan down to close, or tap the backdrop. This is the default sheet
27
+ (flush, rounded top corners only).
28
+ </Text>
29
+ </View>
30
+ </BottomSheet>
31
+ </>
32
+ );
33
+ }
34
+
35
+ function DetachedSheet() {
36
+ const ref = useRef<BottomSheetRef>(null);
37
+ return (
38
+ <>
39
+ <Button onPress={() => ref.current?.present()}>Open detached</Button>
40
+ <BottomSheet ref={ref} detached>
41
+ <View style={{ padding: 24, gap: 12 }}>
42
+ <Text style={{ fontSize: 20, fontWeight: '700' }}>Detached</Text>
43
+ <Text>
44
+ Floating card with margins and rounded corners on all sides.
45
+ </Text>
46
+ </View>
47
+ </BottomSheet>
48
+ </>
49
+ );
50
+ }
51
+
52
+ function NonScrollableSheet() {
53
+ const ref = useRef<BottomSheetRef>(null);
54
+ return (
55
+ <>
56
+ <Button onPress={() => ref.current?.present()}>Open non-scrollable</Button>
57
+ <BottomSheet ref={ref} scrollable={false}>
58
+ <View style={{ padding: 24, gap: 12 }}>
59
+ <Text style={{ fontSize: 20, fontWeight: '700' }}>
60
+ Non-scrollable
61
+ </Text>
62
+ <Text>
63
+ Use when the body owns its own VirtualizedList (FlatList,
64
+ SectionList, etc.).
65
+ </Text>
66
+ </View>
67
+ </BottomSheet>
68
+ </>
69
+ );
70
+ }
71
+
72
+ export const Basic: Story = {
73
+ render: () => <BasicSheet />,
74
+ };
75
+
76
+ export const Detached: Story = {
77
+ render: () => <DetachedSheet />,
78
+ };
79
+
80
+ export const NonScrollable: Story = {
81
+ render: () => <NonScrollableSheet />,
82
+ };
83
+
84
+ export const Composition: Story = {
85
+ render: () => (
86
+ <View style={{ gap: 12, alignItems: 'flex-start' }}>
87
+ <BasicSheet />
88
+ <DetachedSheet />
89
+ <NonScrollableSheet />
90
+ </View>
91
+ ),
92
+ };
@@ -0,0 +1,94 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { Button } from './Button';
6
+
7
+ const meta: Meta<typeof Button> = {
8
+ title: 'Components/Button',
9
+ component: Button,
10
+ args: {
11
+ children: 'Button',
12
+ onPress: () => {},
13
+ },
14
+ argTypes: {
15
+ variant: {
16
+ control: 'select',
17
+ options: ['primary', 'secondary', 'inverse', 'icon', 'ghost', 'text'],
18
+ },
19
+ size: {
20
+ control: 'select',
21
+ options: ['small', 'medium', 'large'],
22
+ },
23
+ disabled: { control: 'boolean' },
24
+ loading: { control: 'boolean' },
25
+ },
26
+ };
27
+
28
+ export default meta;
29
+
30
+ type Story = StoryObj<typeof Button>;
31
+
32
+ export const Basic: Story = {
33
+ args: { children: 'Save' },
34
+ };
35
+
36
+ export const Primary: Story = {
37
+ args: { variant: 'primary', children: 'Primary' },
38
+ };
39
+
40
+ export const Secondary: Story = {
41
+ args: { variant: 'secondary', children: 'Secondary' },
42
+ };
43
+
44
+ export const Ghost: Story = {
45
+ args: { variant: 'ghost', children: 'Ghost' },
46
+ };
47
+
48
+ export const TextOnly: Story = {
49
+ args: { variant: 'text', children: 'Text button' },
50
+ name: 'Text',
51
+ };
52
+
53
+ export const Inverse: Story = {
54
+ args: { variant: 'inverse', children: 'Inverse' },
55
+ };
56
+
57
+ export const Variants: Story = {
58
+ render: () => (
59
+ <View style={{ gap: 12, alignItems: 'flex-start' }}>
60
+ <Button variant="primary">Primary</Button>
61
+ <Button variant="secondary">Secondary</Button>
62
+ <Button variant="inverse">Inverse</Button>
63
+ <Button variant="ghost">Ghost</Button>
64
+ <Button variant="text">Text</Button>
65
+ </View>
66
+ ),
67
+ };
68
+
69
+ export const Sizes: Story = {
70
+ render: () => (
71
+ <View style={{ gap: 12, alignItems: 'flex-start' }}>
72
+ <Button size="small">Small</Button>
73
+ <Button size="medium">Medium</Button>
74
+ <Button size="large">Large</Button>
75
+ </View>
76
+ ),
77
+ };
78
+
79
+ export const Loading: Story = {
80
+ args: { loading: true, children: 'Submitting' },
81
+ };
82
+
83
+ export const Disabled: Story = {
84
+ args: { disabled: true, children: 'Disabled' },
85
+ };
86
+
87
+ export const Composition: Story = {
88
+ render: () => (
89
+ <View style={{ flexDirection: 'row', gap: 12, flexWrap: 'wrap' }}>
90
+ <Button variant="primary">Save</Button>
91
+ <Button variant="secondary">Cancel</Button>
92
+ </View>
93
+ ),
94
+ };
@@ -0,0 +1,71 @@
1
+ import React from 'react';
2
+ import { Pressable, Text, View } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { useTheme } from '../theme/use-theme';
6
+ import * as ContextMenu from './index';
7
+
8
+ const meta: Meta = {
9
+ title: 'Components/ContextMenu',
10
+ };
11
+
12
+ export default meta;
13
+
14
+ type Story = StoryObj;
15
+
16
+ function TriggerSurface() {
17
+ const theme = useTheme();
18
+ return (
19
+ <ContextMenu.Root>
20
+ <ContextMenu.Trigger label="Long-press for actions">
21
+ {({ props }) => (
22
+ <Pressable
23
+ onPress={() => props.onPress?.()}
24
+ onLongPress={() => props.onLongPress?.()}
25
+ accessibilityLabel={props.accessibilityLabel}
26
+ accessibilityHint={props.accessibilityHint}
27
+ style={{
28
+ padding: 24,
29
+ borderRadius: 12,
30
+ backgroundColor: theme.colors.backgroundSecondary,
31
+ borderWidth: 1,
32
+ borderColor: theme.colors.borderLight,
33
+ minWidth: 240,
34
+ alignItems: 'center',
35
+ }}
36
+ >
37
+ <Text style={{ color: theme.colors.text }}>
38
+ Long-press / right-click me
39
+ </Text>
40
+ </Pressable>
41
+ )}
42
+ </ContextMenu.Trigger>
43
+ <ContextMenu.Outer>
44
+ <ContextMenu.Group>
45
+ <ContextMenu.Item label="Open" onPress={() => {}}>
46
+ <ContextMenu.ItemText>Open</ContextMenu.ItemText>
47
+ </ContextMenu.Item>
48
+ <ContextMenu.Item label="Rename" onPress={() => {}}>
49
+ <ContextMenu.ItemText>Rename</ContextMenu.ItemText>
50
+ </ContextMenu.Item>
51
+ <ContextMenu.Item label="Delete" onPress={() => {}}>
52
+ <ContextMenu.ItemText>Delete</ContextMenu.ItemText>
53
+ </ContextMenu.Item>
54
+ </ContextMenu.Group>
55
+ </ContextMenu.Outer>
56
+ </ContextMenu.Root>
57
+ );
58
+ }
59
+
60
+ export const Basic: Story = {
61
+ render: () => <TriggerSurface />,
62
+ };
63
+
64
+ export const Composition: Story = {
65
+ render: () => (
66
+ <View style={{ gap: 16 }}>
67
+ <TriggerSurface />
68
+ <TriggerSurface />
69
+ </View>
70
+ ),
71
+ };
@@ -0,0 +1,112 @@
1
+ import React from 'react';
2
+ import { Text, View } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { Button } from '../button';
6
+ import { Dialog } from './Dialog';
7
+ import { useDialogControl } from './context';
8
+ import { alert } from './alert';
9
+
10
+ const meta: Meta<typeof Dialog> = {
11
+ title: 'Components/Dialog',
12
+ component: Dialog,
13
+ };
14
+
15
+ export default meta;
16
+
17
+ type Story = StoryObj<typeof Dialog>;
18
+
19
+ function DeclarativeDemo() {
20
+ const control = useDialogControl();
21
+ return (
22
+ <>
23
+ <Button onPress={() => control.open()}>Open dialog</Button>
24
+ <Dialog
25
+ control={control}
26
+ title="Sign out?"
27
+ description="You'll need to enter your password to sign in again."
28
+ actions={[
29
+ { label: 'Sign out', color: 'destructive' },
30
+ { label: 'Cancel', color: 'cancel' },
31
+ ]}
32
+ />
33
+ </>
34
+ );
35
+ }
36
+
37
+ function CustomChildrenDemo() {
38
+ const control = useDialogControl();
39
+ return (
40
+ <>
41
+ <Button onPress={() => control.open()}>Open custom dialog</Button>
42
+ <Dialog control={control} title="Custom body">
43
+ <View style={{ gap: 12 }}>
44
+ <Text>Render any JSX inside the dialog body.</Text>
45
+ <Button variant="secondary" onPress={() => control.close()}>
46
+ Done
47
+ </Button>
48
+ </View>
49
+ </Dialog>
50
+ </>
51
+ );
52
+ }
53
+
54
+ function AlertDemo() {
55
+ return (
56
+ <Button
57
+ onPress={() =>
58
+ alert('Delete project?', 'This action cannot be undone.', [
59
+ { text: 'Cancel', style: 'cancel' },
60
+ { text: 'Delete', style: 'destructive' },
61
+ ])
62
+ }
63
+ >
64
+ Trigger alert()
65
+ </Button>
66
+ );
67
+ }
68
+
69
+ function ThreeActionDemo() {
70
+ const control = useDialogControl();
71
+ return (
72
+ <>
73
+ <Button onPress={() => control.open()}>Three actions</Button>
74
+ <Dialog
75
+ control={control}
76
+ title="Save changes?"
77
+ description="You have unsaved changes."
78
+ actions={[
79
+ { label: 'Save', color: 'default' },
80
+ { label: 'Discard', color: 'destructive' },
81
+ { label: 'Cancel', color: 'cancel' },
82
+ ]}
83
+ />
84
+ </>
85
+ );
86
+ }
87
+
88
+ export const Basic: Story = {
89
+ render: () => <DeclarativeDemo />,
90
+ };
91
+
92
+ export const CustomChildren: Story = {
93
+ render: () => <CustomChildrenDemo />,
94
+ };
95
+
96
+ export const AlertHelper: Story = {
97
+ render: () => <AlertDemo />,
98
+ };
99
+
100
+ export const ThreeAction: Story = {
101
+ render: () => <ThreeActionDemo />,
102
+ };
103
+
104
+ export const Composition: Story = {
105
+ render: () => (
106
+ <View style={{ gap: 12, alignItems: 'flex-start' }}>
107
+ <DeclarativeDemo />
108
+ <CustomChildrenDemo />
109
+ <AlertDemo />
110
+ </View>
111
+ ),
112
+ };
@@ -1,10 +1,11 @@
1
1
  import React, { useRef } from 'react';
2
2
  // Reach for the web variant explicitly. The default `./apply-font-faces`
3
- // is a no-op stub that exists so Metro never sees the module-level
4
- // `.woff2` imports on native (Metro's default `assetExts` does not include
5
- // `.woff2`). This file (`FontLoader.tsx`) is only picked up by web
6
- // bundlers — Metro selects `FontLoader.native.tsx` on iOS/Android — so it
7
- // is safe to take a direct dependency on the web implementation here.
3
+ // is a no-op stub on native native loads its fonts via
4
+ // `useFonts(FONT_ASSETS)` in `FontLoader.native.tsx` and has no use for
5
+ // CSS `@font-face` injection. This file (`FontLoader.tsx`) is only picked
6
+ // up by web bundlers — Metro selects `FontLoader.native.tsx` on iOS/Android
7
+ // — so it is safe to take a direct dependency on the web implementation
8
+ // here.
8
9
  import { applyFontFaces } from './apply-font-faces.web';
9
10
 
10
11
  export interface FontLoaderProps {
@@ -2,10 +2,10 @@
2
2
  // is selected by bundlers via the package.json conditions. RN consumers
3
3
  // never need font-face injection — useFonts handles loading on native.
4
4
  //
5
- // This file MUST have zero `.woff2` imports. Metro parses module-level
6
- // imports at bundle time and would otherwise fail to resolve the `.woff2`
7
- // assets (not in Metro's default `assetExts`). See
8
- // `apply-font-faces.web.ts` for the real implementation.
5
+ // The web variant does NOT pull in any `.woff2` files via asset imports —
6
+ // it imports base64 data URLs from `font-data.web.ts`. This stub stays
7
+ // here so the file split (`*.ts` vs `*.web.ts`) makes the platform
8
+ // intent explicit and so the runtime API surface matches across platforms.
9
9
  export function applyFontFaces(): void {
10
10
  // intentionally empty
11
11
  }