@oxyhq/bloom 0.5.0 → 0.6.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/lib/commonjs/error-boundary/ErrorBoundary.js +27 -7
- package/lib/commonjs/error-boundary/ErrorBoundary.js.map +1 -1
- package/lib/commonjs/fonts/FontLoader.js +6 -5
- package/lib/commonjs/fonts/FontLoader.js.map +1 -1
- package/lib/commonjs/fonts/apply-font-faces.js +4 -4
- package/lib/commonjs/fonts/apply-font-faces.web.js +13 -12
- package/lib/commonjs/fonts/apply-font-faces.web.js.map +1 -1
- package/lib/commonjs/fonts/font-assets.js +2 -2
- package/lib/commonjs/fonts/font-data.web.js +22 -0
- package/lib/commonjs/fonts/font-data.web.js.map +1 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/skeleton/index.js +30 -0
- package/lib/commonjs/skeleton/index.js.map +1 -1
- package/lib/module/error-boundary/ErrorBoundary.js +27 -7
- package/lib/module/error-boundary/ErrorBoundary.js.map +1 -1
- package/lib/module/fonts/FontLoader.js +6 -5
- package/lib/module/fonts/FontLoader.js.map +1 -1
- package/lib/module/fonts/apply-font-faces.js +4 -4
- package/lib/module/fonts/apply-font-faces.web.js +13 -10
- package/lib/module/fonts/apply-font-faces.web.js.map +1 -1
- package/lib/module/fonts/font-assets.js +2 -2
- package/lib/module/fonts/font-data.web.js +18 -0
- package/lib/module/fonts/font-data.web.js.map +1 -0
- package/lib/module/fonts/index.web.js +4 -4
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/skeleton/index.js +29 -0
- package/lib/module/skeleton/index.js.map +1 -1
- package/lib/typescript/commonjs/error-boundary/ErrorBoundary.d.ts +3 -1
- package/lib/typescript/commonjs/error-boundary/ErrorBoundary.d.ts.map +1 -1
- package/lib/typescript/commonjs/error-boundary/index.d.ts +1 -1
- package/lib/typescript/commonjs/error-boundary/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/error-boundary/types.d.ts +41 -2
- package/lib/typescript/commonjs/error-boundary/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/fonts/FontLoader.d.ts.map +1 -1
- package/lib/typescript/commonjs/fonts/apply-font-faces.web.d.ts +8 -1
- package/lib/typescript/commonjs/fonts/apply-font-faces.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/fonts/font-data.web.d.ts +5 -0
- package/lib/typescript/commonjs/fonts/font-data.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +1 -1
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/skeleton/index.d.ts +24 -1
- package/lib/typescript/commonjs/skeleton/index.d.ts.map +1 -1
- package/lib/typescript/module/error-boundary/ErrorBoundary.d.ts +3 -1
- package/lib/typescript/module/error-boundary/ErrorBoundary.d.ts.map +1 -1
- package/lib/typescript/module/error-boundary/index.d.ts +1 -1
- package/lib/typescript/module/error-boundary/index.d.ts.map +1 -1
- package/lib/typescript/module/error-boundary/types.d.ts +41 -2
- package/lib/typescript/module/error-boundary/types.d.ts.map +1 -1
- package/lib/typescript/module/fonts/FontLoader.d.ts.map +1 -1
- package/lib/typescript/module/fonts/apply-font-faces.web.d.ts +8 -1
- package/lib/typescript/module/fonts/apply-font-faces.web.d.ts.map +1 -1
- package/lib/typescript/module/fonts/font-data.web.d.ts +5 -0
- package/lib/typescript/module/fonts/font-data.web.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +1 -1
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/skeleton/index.d.ts +24 -1
- package/lib/typescript/module/skeleton/index.d.ts.map +1 -1
- package/package.json +36 -5
- package/src/__tests__/ErrorBoundary.test.tsx +217 -0
- package/src/__tests__/Skeleton.test.tsx +63 -0
- package/src/avatar/Avatar.stories.tsx +69 -0
- package/src/bottom-sheet/BottomSheet.stories.tsx +92 -0
- package/src/button/Button.stories.tsx +94 -0
- package/src/context-menu/ContextMenu.stories.tsx +71 -0
- package/src/dialog/Dialog.stories.tsx +112 -0
- package/src/error-boundary/ErrorBoundary.tsx +28 -5
- package/src/error-boundary/index.ts +5 -1
- package/src/error-boundary/types.ts +45 -2
- package/src/fonts/FontLoader.tsx +6 -5
- package/src/fonts/apply-font-faces.ts +4 -4
- package/src/fonts/apply-font-faces.web.ts +18 -10
- package/src/fonts/font-assets.ts +2 -2
- package/src/fonts/font-data.web.ts +15 -0
- package/src/fonts/index.web.ts +4 -4
- package/src/index.ts +5 -1
- package/src/index.web.ts +5 -1
- package/src/loading/Loading.stories.tsx +60 -0
- package/src/menu/Menu.stories.tsx +79 -0
- package/src/prompt-input/PromptInput.stories.tsx +82 -0
- package/src/select/Select.stories.tsx +84 -0
- package/src/settings-list/SettingsList.stories.tsx +106 -0
- package/src/skeleton/index.tsx +54 -1
- package/src/text-field/TextField.stories.tsx +90 -0
- package/src/toast/Toast.stories.tsx +109 -0
- package/lib/commonjs/fonts/assets/BlomusModernus-Bold.woff2 +0 -0
- package/lib/commonjs/fonts/assets/BlomusModernus-Regular.woff2 +0 -0
- package/lib/commonjs/fonts/assets/GeistMono-Variable.woff2 +0 -0
- package/lib/commonjs/fonts/assets/InterVariable.woff2 +0 -0
- package/lib/module/fonts/assets/BlomusModernus-Bold.woff2 +0 -0
- package/lib/module/fonts/assets/BlomusModernus-Regular.woff2 +0 -0
- package/lib/module/fonts/assets/GeistMono-Variable.woff2 +0 -0
- package/lib/module/fonts/assets/InterVariable.woff2 +0 -0
- package/lib/typescript/commonjs/__tests__/BloomThemeProvider.fonts-web.test.d.ts +0 -5
- package/lib/typescript/commonjs/__tests__/BloomThemeProvider.fonts-web.test.d.ts.map +0 -1
- package/lib/typescript/commonjs/__tests__/BloomThemeProvider.test.d.ts +0 -2
- package/lib/typescript/commonjs/__tests__/BloomThemeProvider.test.d.ts.map +0 -1
- package/lib/typescript/commonjs/__tests__/BottomSheet.test.d.ts +0 -2
- package/lib/typescript/commonjs/__tests__/BottomSheet.test.d.ts.map +0 -1
- package/lib/typescript/commonjs/__tests__/Button.test.d.ts +0 -2
- package/lib/typescript/commonjs/__tests__/Button.test.d.ts.map +0 -1
- package/lib/typescript/commonjs/__tests__/Code.test.d.ts +0 -2
- package/lib/typescript/commonjs/__tests__/Code.test.d.ts.map +0 -1
- package/lib/typescript/commonjs/__tests__/Dialog.test.d.ts +0 -2
- package/lib/typescript/commonjs/__tests__/Dialog.test.d.ts.map +0 -1
- package/lib/typescript/commonjs/__tests__/FontLoader.native.test.d.ts +0 -2
- package/lib/typescript/commonjs/__tests__/FontLoader.native.test.d.ts.map +0 -1
- package/lib/typescript/commonjs/__tests__/Pre.test.d.ts +0 -2
- package/lib/typescript/commonjs/__tests__/Pre.test.d.ts.map +0 -1
- package/lib/typescript/commonjs/__tests__/SettingsList.test.d.ts +0 -2
- package/lib/typescript/commonjs/__tests__/SettingsList.test.d.ts.map +0 -1
- package/lib/typescript/commonjs/__tests__/apply-font-faces.test.d.ts +0 -5
- package/lib/typescript/commonjs/__tests__/apply-font-faces.test.d.ts.map +0 -1
- package/lib/typescript/commonjs/__tests__/theme.test.d.ts +0 -2
- package/lib/typescript/commonjs/__tests__/theme.test.d.ts.map +0 -1
- package/lib/typescript/module/__tests__/BloomThemeProvider.fonts-web.test.d.ts +0 -5
- package/lib/typescript/module/__tests__/BloomThemeProvider.fonts-web.test.d.ts.map +0 -1
- package/lib/typescript/module/__tests__/BloomThemeProvider.test.d.ts +0 -2
- package/lib/typescript/module/__tests__/BloomThemeProvider.test.d.ts.map +0 -1
- package/lib/typescript/module/__tests__/BottomSheet.test.d.ts +0 -2
- package/lib/typescript/module/__tests__/BottomSheet.test.d.ts.map +0 -1
- package/lib/typescript/module/__tests__/Button.test.d.ts +0 -2
- package/lib/typescript/module/__tests__/Button.test.d.ts.map +0 -1
- package/lib/typescript/module/__tests__/Code.test.d.ts +0 -2
- package/lib/typescript/module/__tests__/Code.test.d.ts.map +0 -1
- package/lib/typescript/module/__tests__/Dialog.test.d.ts +0 -2
- package/lib/typescript/module/__tests__/Dialog.test.d.ts.map +0 -1
- package/lib/typescript/module/__tests__/FontLoader.native.test.d.ts +0 -2
- package/lib/typescript/module/__tests__/FontLoader.native.test.d.ts.map +0 -1
- package/lib/typescript/module/__tests__/Pre.test.d.ts +0 -2
- package/lib/typescript/module/__tests__/Pre.test.d.ts.map +0 -1
- package/lib/typescript/module/__tests__/SettingsList.test.d.ts +0 -2
- package/lib/typescript/module/__tests__/SettingsList.test.d.ts.map +0 -1
- package/lib/typescript/module/__tests__/apply-font-faces.test.d.ts +0 -5
- package/lib/typescript/module/__tests__/apply-font-faces.test.d.ts.map +0 -1
- package/lib/typescript/module/__tests__/theme.test.d.ts +0 -2
- package/lib/typescript/module/__tests__/theme.test.d.ts.map +0 -1
package/src/fonts/index.web.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Web variant of the `./fonts` barrel.
|
|
2
2
|
//
|
|
3
3
|
// The default barrel (`./index.ts`) re-exports `applyFontFaces` from
|
|
4
|
-
// `./apply-font-faces`, which on native
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
4
|
+
// `./apply-font-faces`, which on native is a no-op stub. The web fork
|
|
5
|
+
// explicitly reaches for `./apply-font-faces.web`, which performs the real
|
|
6
|
+
// `@font-face` injection using inlined base64 data URLs (see
|
|
7
|
+
// `font-data.web.ts`).
|
|
8
8
|
//
|
|
9
9
|
// Web bundlers select this file via the `"browser"` condition in
|
|
10
10
|
// `package.json`'s `exports['./fonts']`; native bundlers fall through to
|
package/src/index.ts
CHANGED
|
@@ -40,7 +40,11 @@ export * from './divider';
|
|
|
40
40
|
export * from './radio-indicator';
|
|
41
41
|
export * from './collapsible';
|
|
42
42
|
export { ErrorBoundary } from './error-boundary';
|
|
43
|
-
export type {
|
|
43
|
+
export type {
|
|
44
|
+
ErrorBoundaryProps,
|
|
45
|
+
ErrorBoundaryFallback,
|
|
46
|
+
ErrorBoundaryFallbackContext,
|
|
47
|
+
} from './error-boundary';
|
|
44
48
|
export * from './avatar';
|
|
45
49
|
export * from './loading';
|
|
46
50
|
export * as PromptInput from './prompt-input';
|
package/src/index.web.ts
CHANGED
|
@@ -45,7 +45,11 @@ export * from './divider';
|
|
|
45
45
|
export * from './radio-indicator';
|
|
46
46
|
export * from './collapsible';
|
|
47
47
|
export { ErrorBoundary } from './error-boundary';
|
|
48
|
-
export type {
|
|
48
|
+
export type {
|
|
49
|
+
ErrorBoundaryProps,
|
|
50
|
+
ErrorBoundaryFallback,
|
|
51
|
+
ErrorBoundaryFallbackContext,
|
|
52
|
+
} from './error-boundary';
|
|
49
53
|
export * from './avatar';
|
|
50
54
|
export * from './loading';
|
|
51
55
|
export * as PromptInput from './prompt-input';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
|
+
|
|
5
|
+
import { Loading } from './Loading';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Loading> = {
|
|
8
|
+
title: 'Components/Loading',
|
|
9
|
+
component: Loading,
|
|
10
|
+
argTypes: {
|
|
11
|
+
variant: {
|
|
12
|
+
control: 'select',
|
|
13
|
+
options: ['spinner', 'top', 'skeleton', 'inline'],
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
control: 'select',
|
|
17
|
+
options: ['small', 'medium', 'large'],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
|
|
24
|
+
type Story = StoryObj<typeof Loading>;
|
|
25
|
+
|
|
26
|
+
export const Basic: Story = {
|
|
27
|
+
args: { variant: 'spinner' },
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const Spinner: Story = {
|
|
31
|
+
args: { variant: 'spinner', text: 'Loading…', showText: true },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const Inline: Story = {
|
|
35
|
+
args: { variant: 'inline', text: 'Saving' },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const Skeleton: Story = {
|
|
39
|
+
args: { variant: 'skeleton', lines: 4 },
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const Sizes: Story = {
|
|
43
|
+
render: () => (
|
|
44
|
+
<View style={{ flexDirection: 'row', gap: 24, alignItems: 'center' }}>
|
|
45
|
+
<Loading variant="spinner" size="small" />
|
|
46
|
+
<Loading variant="spinner" size="medium" />
|
|
47
|
+
<Loading variant="spinner" size="large" />
|
|
48
|
+
</View>
|
|
49
|
+
),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const Composition: Story = {
|
|
53
|
+
render: () => (
|
|
54
|
+
<View style={{ gap: 24, alignItems: 'flex-start' }}>
|
|
55
|
+
<Loading variant="spinner" size="small" text="Small" />
|
|
56
|
+
<Loading variant="inline" text="Saving changes" />
|
|
57
|
+
<Loading variant="skeleton" lines={3} />
|
|
58
|
+
</View>
|
|
59
|
+
),
|
|
60
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
import * as Menu from './index';
|
|
7
|
+
|
|
8
|
+
const meta: Meta = {
|
|
9
|
+
title: 'Components/Menu',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
|
|
14
|
+
type Story = StoryObj;
|
|
15
|
+
|
|
16
|
+
function BasicMenu() {
|
|
17
|
+
return (
|
|
18
|
+
<Menu.Root>
|
|
19
|
+
<Menu.Trigger label="Open menu">
|
|
20
|
+
{({ props }) => (
|
|
21
|
+
<Button onPress={props.onPress}>Open menu</Button>
|
|
22
|
+
)}
|
|
23
|
+
</Menu.Trigger>
|
|
24
|
+
<Menu.Outer>
|
|
25
|
+
<Menu.Group>
|
|
26
|
+
<Menu.Item label="Profile" onPress={() => {}}>
|
|
27
|
+
<Menu.ItemText>Profile</Menu.ItemText>
|
|
28
|
+
</Menu.Item>
|
|
29
|
+
<Menu.Item label="Settings" onPress={() => {}}>
|
|
30
|
+
<Menu.ItemText>Settings</Menu.ItemText>
|
|
31
|
+
</Menu.Item>
|
|
32
|
+
<Menu.Item label="Sign out" onPress={() => {}}>
|
|
33
|
+
<Menu.ItemText>Sign out</Menu.ItemText>
|
|
34
|
+
</Menu.Item>
|
|
35
|
+
</Menu.Group>
|
|
36
|
+
</Menu.Outer>
|
|
37
|
+
</Menu.Root>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function MenuWithDisabledItem() {
|
|
42
|
+
return (
|
|
43
|
+
<Menu.Root>
|
|
44
|
+
<Menu.Trigger label="Open menu with disabled item">
|
|
45
|
+
{({ props }) => <Button onPress={props.onPress}>Open</Button>}
|
|
46
|
+
</Menu.Trigger>
|
|
47
|
+
<Menu.Outer>
|
|
48
|
+
<Menu.Group>
|
|
49
|
+
<Menu.Item label="Edit" onPress={() => {}}>
|
|
50
|
+
<Menu.ItemText>Edit</Menu.ItemText>
|
|
51
|
+
</Menu.Item>
|
|
52
|
+
<Menu.Item label="Duplicate" onPress={() => {}} disabled>
|
|
53
|
+
<Menu.ItemText>Duplicate (disabled)</Menu.ItemText>
|
|
54
|
+
</Menu.Item>
|
|
55
|
+
<Menu.Item label="Delete" onPress={() => {}}>
|
|
56
|
+
<Menu.ItemText>Delete</Menu.ItemText>
|
|
57
|
+
</Menu.Item>
|
|
58
|
+
</Menu.Group>
|
|
59
|
+
</Menu.Outer>
|
|
60
|
+
</Menu.Root>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const Basic: Story = {
|
|
65
|
+
render: () => <BasicMenu />,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const WithDisabled: Story = {
|
|
69
|
+
render: () => <MenuWithDisabledItem />,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const Composition: Story = {
|
|
73
|
+
render: () => (
|
|
74
|
+
<View style={{ gap: 12, alignItems: 'flex-start' }}>
|
|
75
|
+
<BasicMenu />
|
|
76
|
+
<MenuWithDisabledItem />
|
|
77
|
+
</View>
|
|
78
|
+
),
|
|
79
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
|
+
|
|
5
|
+
import { PromptInput } from './PromptInput';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof PromptInput> = {
|
|
8
|
+
title: 'Components/PromptInput',
|
|
9
|
+
component: PromptInput,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
|
|
14
|
+
type Story = StoryObj<typeof PromptInput>;
|
|
15
|
+
|
|
16
|
+
function BasicPrompt() {
|
|
17
|
+
const [value, setValue] = useState('');
|
|
18
|
+
return (
|
|
19
|
+
<View style={{ width: 480 }}>
|
|
20
|
+
<PromptInput
|
|
21
|
+
value={value}
|
|
22
|
+
onValueChange={setValue}
|
|
23
|
+
placeholder="Ask anything"
|
|
24
|
+
onSubmit={() => {
|
|
25
|
+
// submitted
|
|
26
|
+
setValue('');
|
|
27
|
+
}}
|
|
28
|
+
/>
|
|
29
|
+
</View>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function LoadingPrompt() {
|
|
34
|
+
const [value, setValue] = useState('Generating response...');
|
|
35
|
+
return (
|
|
36
|
+
<View style={{ width: 480 }}>
|
|
37
|
+
<PromptInput
|
|
38
|
+
value={value}
|
|
39
|
+
onValueChange={setValue}
|
|
40
|
+
placeholder="Ask anything"
|
|
41
|
+
isLoading
|
|
42
|
+
onSubmit={() => {}}
|
|
43
|
+
onStop={() => {}}
|
|
44
|
+
/>
|
|
45
|
+
</View>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function DisabledPrompt() {
|
|
50
|
+
return (
|
|
51
|
+
<View style={{ width: 480 }}>
|
|
52
|
+
<PromptInput
|
|
53
|
+
value=""
|
|
54
|
+
placeholder="Disabled"
|
|
55
|
+
disabled
|
|
56
|
+
onSubmit={() => {}}
|
|
57
|
+
/>
|
|
58
|
+
</View>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const Basic: Story = {
|
|
63
|
+
render: () => <BasicPrompt />,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const Loading: Story = {
|
|
67
|
+
render: () => <LoadingPrompt />,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const Disabled: Story = {
|
|
71
|
+
render: () => <DisabledPrompt />,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const Composition: Story = {
|
|
75
|
+
render: () => (
|
|
76
|
+
<View style={{ gap: 16 }}>
|
|
77
|
+
<BasicPrompt />
|
|
78
|
+
<LoadingPrompt />
|
|
79
|
+
<DisabledPrompt />
|
|
80
|
+
</View>
|
|
81
|
+
),
|
|
82
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
|
+
|
|
5
|
+
import * as Select from './index';
|
|
6
|
+
|
|
7
|
+
const meta: Meta = {
|
|
8
|
+
title: 'Components/Select',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj;
|
|
14
|
+
|
|
15
|
+
type Option = { value: string; label: string };
|
|
16
|
+
|
|
17
|
+
const FRUITS: Option[] = [
|
|
18
|
+
{ value: 'apple', label: 'Apple' },
|
|
19
|
+
{ value: 'banana', label: 'Banana' },
|
|
20
|
+
{ value: 'cherry', label: 'Cherry' },
|
|
21
|
+
{ value: 'durian', label: 'Durian' },
|
|
22
|
+
{ value: 'elderberry', label: 'Elderberry' },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
function BasicSelect() {
|
|
26
|
+
const [value, setValue] = useState<string>('apple');
|
|
27
|
+
return (
|
|
28
|
+
<Select.Root value={value} onValueChange={setValue}>
|
|
29
|
+
<Select.Trigger label="Pick a fruit">
|
|
30
|
+
<Select.ValueText placeholder="Pick a fruit" />
|
|
31
|
+
<Select.Icon />
|
|
32
|
+
</Select.Trigger>
|
|
33
|
+
<Select.Content
|
|
34
|
+
label="Pick a fruit"
|
|
35
|
+
items={FRUITS}
|
|
36
|
+
renderItem={(item) => (
|
|
37
|
+
<Select.Item value={item.value} label={item.label}>
|
|
38
|
+
<Select.ItemIndicator />
|
|
39
|
+
<Select.ItemText>{item.label}</Select.ItemText>
|
|
40
|
+
</Select.Item>
|
|
41
|
+
)}
|
|
42
|
+
/>
|
|
43
|
+
</Select.Root>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function UncontrolledSelect() {
|
|
48
|
+
const [value, setValue] = useState<string | undefined>(undefined);
|
|
49
|
+
return (
|
|
50
|
+
<Select.Root value={value} onValueChange={setValue}>
|
|
51
|
+
<Select.Trigger label="Pick a fruit">
|
|
52
|
+
<Select.ValueText placeholder="No fruit selected" />
|
|
53
|
+
<Select.Icon />
|
|
54
|
+
</Select.Trigger>
|
|
55
|
+
<Select.Content
|
|
56
|
+
label="Pick a fruit"
|
|
57
|
+
items={FRUITS}
|
|
58
|
+
renderItem={(item) => (
|
|
59
|
+
<Select.Item value={item.value} label={item.label}>
|
|
60
|
+
<Select.ItemIndicator />
|
|
61
|
+
<Select.ItemText>{item.label}</Select.ItemText>
|
|
62
|
+
</Select.Item>
|
|
63
|
+
)}
|
|
64
|
+
/>
|
|
65
|
+
</Select.Root>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const Basic: Story = {
|
|
70
|
+
render: () => <BasicSelect />,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const WithPlaceholder: Story = {
|
|
74
|
+
render: () => <UncontrolledSelect />,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const Composition: Story = {
|
|
78
|
+
render: () => (
|
|
79
|
+
<View style={{ gap: 16 }}>
|
|
80
|
+
<BasicSelect />
|
|
81
|
+
<UncontrolledSelect />
|
|
82
|
+
</View>
|
|
83
|
+
),
|
|
84
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
|
+
|
|
5
|
+
import { Switch } from '../switch';
|
|
6
|
+
import {
|
|
7
|
+
SettingsListGroup,
|
|
8
|
+
SettingsListItem,
|
|
9
|
+
SettingsListDivider,
|
|
10
|
+
} from './SettingsList';
|
|
11
|
+
|
|
12
|
+
const meta: Meta = {
|
|
13
|
+
title: 'Components/SettingsList',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default meta;
|
|
17
|
+
|
|
18
|
+
type Story = StoryObj;
|
|
19
|
+
|
|
20
|
+
function BasicList() {
|
|
21
|
+
return (
|
|
22
|
+
<View style={{ width: 360 }}>
|
|
23
|
+
<SettingsListGroup title="Account">
|
|
24
|
+
<SettingsListItem
|
|
25
|
+
title="Profile"
|
|
26
|
+
description="Manage your account profile"
|
|
27
|
+
onPress={() => {}}
|
|
28
|
+
/>
|
|
29
|
+
<SettingsListItem
|
|
30
|
+
title="Email"
|
|
31
|
+
value="nate@oxy.so"
|
|
32
|
+
onPress={() => {}}
|
|
33
|
+
/>
|
|
34
|
+
<SettingsListItem
|
|
35
|
+
title="Sign out"
|
|
36
|
+
destructive
|
|
37
|
+
onPress={() => {}}
|
|
38
|
+
/>
|
|
39
|
+
</SettingsListGroup>
|
|
40
|
+
</View>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function WithToggles() {
|
|
45
|
+
const [notifs, setNotifs] = useState(true);
|
|
46
|
+
const [dark, setDark] = useState(false);
|
|
47
|
+
return (
|
|
48
|
+
<View style={{ width: 360 }}>
|
|
49
|
+
<SettingsListGroup title="Preferences" footer="Changes apply immediately.">
|
|
50
|
+
<SettingsListItem
|
|
51
|
+
title="Push notifications"
|
|
52
|
+
rightElement={
|
|
53
|
+
<Switch value={notifs} onValueChange={setNotifs} />
|
|
54
|
+
}
|
|
55
|
+
showChevron={false}
|
|
56
|
+
/>
|
|
57
|
+
<SettingsListItem
|
|
58
|
+
title="Dark mode"
|
|
59
|
+
rightElement={<Switch value={dark} onValueChange={setDark} />}
|
|
60
|
+
showChevron={false}
|
|
61
|
+
/>
|
|
62
|
+
</SettingsListGroup>
|
|
63
|
+
</View>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function DescriptionList() {
|
|
68
|
+
return (
|
|
69
|
+
<View style={{ width: 360 }}>
|
|
70
|
+
<SettingsListGroup title="Security">
|
|
71
|
+
<SettingsListItem
|
|
72
|
+
title="Two-factor authentication"
|
|
73
|
+
description="Add an extra layer of security to your account."
|
|
74
|
+
onPress={() => {}}
|
|
75
|
+
/>
|
|
76
|
+
<SettingsListItem
|
|
77
|
+
title="Active sessions"
|
|
78
|
+
description="View where your account is signed in."
|
|
79
|
+
onPress={() => {}}
|
|
80
|
+
/>
|
|
81
|
+
</SettingsListGroup>
|
|
82
|
+
</View>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const Basic: Story = {
|
|
87
|
+
render: () => <BasicList />,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const WithRightElements: Story = {
|
|
91
|
+
render: () => <WithToggles />,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const WithDescriptions: Story = {
|
|
95
|
+
render: () => <DescriptionList />,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const Composition: Story = {
|
|
99
|
+
render: () => (
|
|
100
|
+
<View style={{ gap: 24, width: 360 }}>
|
|
101
|
+
<DescriptionList />
|
|
102
|
+
<SettingsListDivider />
|
|
103
|
+
<WithToggles />
|
|
104
|
+
</View>
|
|
105
|
+
),
|
|
106
|
+
};
|
package/src/skeleton/index.tsx
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Animated,
|
|
4
|
+
View,
|
|
5
|
+
type ViewStyle,
|
|
6
|
+
type TextStyle,
|
|
7
|
+
type DimensionValue,
|
|
8
|
+
StyleSheet,
|
|
9
|
+
} from 'react-native';
|
|
3
10
|
|
|
4
11
|
import { useTheme } from '../theme/use-theme';
|
|
5
12
|
import { SUPPORTS_NATIVE_DRIVER } from '../styles/native-driver';
|
|
@@ -129,6 +136,52 @@ export function Pill({
|
|
|
129
136
|
}
|
|
130
137
|
Pill.displayName = 'Skeleton.Pill';
|
|
131
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Generic rectangular shimmering placeholder. Use for image/card/banner
|
|
141
|
+
* placeholders where neither {@link Pill} (forced borderRadius: 999) nor
|
|
142
|
+
* {@link Circle} fit. Defaults to a small borderRadius (8). Pass `0` for
|
|
143
|
+
* sharp corners or any larger number / percentage string for custom shapes.
|
|
144
|
+
*/
|
|
145
|
+
export function Box({
|
|
146
|
+
width,
|
|
147
|
+
height,
|
|
148
|
+
borderRadius = 8,
|
|
149
|
+
blend,
|
|
150
|
+
style,
|
|
151
|
+
}: {
|
|
152
|
+
/** Width as a number, percentage string, or any valid RN dimension. */
|
|
153
|
+
width?: DimensionValue;
|
|
154
|
+
/** Height as a number, percentage string, or any valid RN dimension. */
|
|
155
|
+
height?: DimensionValue;
|
|
156
|
+
/**
|
|
157
|
+
* Corner radius — number or percentage string. Defaults to 8. Pass 0 for
|
|
158
|
+
* sharp corners.
|
|
159
|
+
*/
|
|
160
|
+
borderRadius?: ViewStyle['borderRadius'];
|
|
161
|
+
/** When true, dampens shimmer opacity (use for nested skeletons). */
|
|
162
|
+
blend?: boolean;
|
|
163
|
+
style?: ViewStyle | ViewStyle[];
|
|
164
|
+
}) {
|
|
165
|
+
const { colors } = useTheme();
|
|
166
|
+
const shimmer = useShimmer();
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<Animated.View
|
|
170
|
+
style={[
|
|
171
|
+
{
|
|
172
|
+
backgroundColor: colors.contrast50,
|
|
173
|
+
width,
|
|
174
|
+
height,
|
|
175
|
+
borderRadius,
|
|
176
|
+
opacity: Animated.multiply(shimmer, blend ? 0.6 : 1),
|
|
177
|
+
},
|
|
178
|
+
style,
|
|
179
|
+
]}
|
|
180
|
+
/>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
Box.displayName = 'Skeleton.Box';
|
|
184
|
+
|
|
132
185
|
export function Col({
|
|
133
186
|
children,
|
|
134
187
|
style,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
|
+
|
|
5
|
+
import * as TextField from './index';
|
|
6
|
+
|
|
7
|
+
const meta: Meta = {
|
|
8
|
+
title: 'Components/TextField',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj;
|
|
14
|
+
|
|
15
|
+
function ControlledField({
|
|
16
|
+
label,
|
|
17
|
+
placeholder,
|
|
18
|
+
initial = '',
|
|
19
|
+
isInvalid,
|
|
20
|
+
editable = true,
|
|
21
|
+
}: {
|
|
22
|
+
label: string;
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
initial?: string;
|
|
25
|
+
isInvalid?: boolean;
|
|
26
|
+
editable?: boolean;
|
|
27
|
+
}) {
|
|
28
|
+
const [value, setValue] = useState(initial);
|
|
29
|
+
return (
|
|
30
|
+
<View style={{ width: 320 }}>
|
|
31
|
+
<TextField.LabelText>{label}</TextField.LabelText>
|
|
32
|
+
<TextField.Root isInvalid={isInvalid}>
|
|
33
|
+
<TextField.Input
|
|
34
|
+
label={label}
|
|
35
|
+
placeholder={placeholder}
|
|
36
|
+
value={value}
|
|
37
|
+
onChangeText={setValue}
|
|
38
|
+
editable={editable}
|
|
39
|
+
isInvalid={isInvalid}
|
|
40
|
+
/>
|
|
41
|
+
</TextField.Root>
|
|
42
|
+
</View>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const Basic: Story = {
|
|
47
|
+
render: () => (
|
|
48
|
+
<ControlledField label="Username" placeholder="oxylander" />
|
|
49
|
+
),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const WithValue: Story = {
|
|
53
|
+
render: () => (
|
|
54
|
+
<ControlledField label="Email" initial="nate@oxy.so" />
|
|
55
|
+
),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const Error: Story = {
|
|
59
|
+
render: () => (
|
|
60
|
+
<ControlledField
|
|
61
|
+
label="Email"
|
|
62
|
+
initial="not-an-email"
|
|
63
|
+
isInvalid
|
|
64
|
+
/>
|
|
65
|
+
),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const Disabled: Story = {
|
|
69
|
+
render: () => (
|
|
70
|
+
<ControlledField
|
|
71
|
+
label="Username"
|
|
72
|
+
initial="oxylander"
|
|
73
|
+
editable={false}
|
|
74
|
+
/>
|
|
75
|
+
),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const Composition: Story = {
|
|
79
|
+
render: () => (
|
|
80
|
+
<View style={{ gap: 16 }}>
|
|
81
|
+
<ControlledField label="First name" placeholder="Ada" />
|
|
82
|
+
<ControlledField label="Last name" placeholder="Lovelace" />
|
|
83
|
+
<ControlledField
|
|
84
|
+
label="Email"
|
|
85
|
+
initial="invalid-email"
|
|
86
|
+
isInvalid
|
|
87
|
+
/>
|
|
88
|
+
</View>
|
|
89
|
+
),
|
|
90
|
+
};
|