@dxos/brand 0.8.3 → 0.8.4-main.03d5cd7b56

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 (95) hide show
  1. package/README.md +15 -0
  2. package/dist/lib/browser/index.mjs +147 -304
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/types/src/components/experimental/ComposerLogo.d.ts +7 -0
  6. package/dist/types/src/components/experimental/ComposerLogo.d.ts.map +1 -0
  7. package/dist/types/src/components/experimental/Logo.stories.d.ts +15 -0
  8. package/dist/types/src/components/experimental/Logo.stories.d.ts.map +1 -0
  9. package/dist/types/src/components/{ComposerLogo/ComposerLogo.d.ts → experimental/experimental.d.ts} +3 -3
  10. package/dist/types/src/components/experimental/experimental.d.ts.map +1 -0
  11. package/dist/types/src/components/experimental/rive.stories.d.ts +14 -0
  12. package/dist/types/src/components/experimental/rive.stories.d.ts.map +1 -0
  13. package/dist/types/src/components/icons/Composer.d.ts +3 -0
  14. package/dist/types/src/components/icons/Composer.d.ts.map +1 -0
  15. package/dist/types/src/components/icons/DXNS.d.ts.map +1 -0
  16. package/dist/types/src/components/icons/DXOS.d.ts.map +1 -0
  17. package/dist/types/src/components/icons/ECHO.d.ts.map +1 -0
  18. package/dist/types/src/components/icons/HALO.d.ts.map +1 -0
  19. package/dist/types/src/components/icons/Icons.stories.d.ts +13 -0
  20. package/dist/types/src/components/icons/Icons.stories.d.ts.map +1 -0
  21. package/dist/types/src/components/icons/KUBE.d.ts.map +1 -0
  22. package/dist/types/src/components/icons/MESH.d.ts.map +1 -0
  23. package/dist/types/src/components/icons/index.d.ts.map +1 -0
  24. package/dist/types/src/components/index.d.ts +2 -1
  25. package/dist/types/src/components/index.d.ts.map +1 -1
  26. package/dist/types/src/components/logotypes/DXOSHorizontalType.d.ts.map +1 -0
  27. package/dist/types/src/components/logotypes/DXOSType.d.ts.map +1 -0
  28. package/dist/types/src/components/logotypes/DXOSVerticalType.d.ts.map +1 -0
  29. package/dist/types/src/components/logotypes/Logotypes.stories.d.ts +10 -0
  30. package/dist/types/src/components/logotypes/Logotypes.stories.d.ts.map +1 -0
  31. package/dist/types/src/components/logotypes/index.d.ts.map +1 -0
  32. package/dist/types/src/index.d.ts +0 -2
  33. package/dist/types/src/index.d.ts.map +1 -1
  34. package/dist/types/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +19 -16
  36. package/src/components/experimental/ComposerLogo.tsx +66 -0
  37. package/src/components/experimental/Logo.stories.tsx +279 -0
  38. package/src/components/{ComposerLogo/ComposerLogo.tsx → experimental/experimental.tsx} +29 -27
  39. package/src/components/experimental/rive.stories.tsx +103 -0
  40. package/src/{icons → components/icons}/Composer.tsx +19 -5
  41. package/src/{icons → components/icons}/DXNS.tsx +3 -2
  42. package/src/{icons → components/icons}/DXOS.tsx +3 -2
  43. package/src/{icons → components/icons}/ECHO.tsx +3 -2
  44. package/src/{icons → components/icons}/HALO.tsx +3 -2
  45. package/src/components/icons/Icons.stories.tsx +68 -0
  46. package/src/{icons → components/icons}/KUBE.tsx +3 -2
  47. package/src/{icons → components/icons}/MESH.tsx +3 -2
  48. package/src/components/index.ts +3 -2
  49. package/src/{Logotypes.stories.tsx → components/logotypes/Logotypes.stories.tsx} +19 -14
  50. package/src/index.ts +0 -2
  51. package/src/types.d.ts +9 -0
  52. package/dist/types/src/Icons.stories.d.ts +0 -12
  53. package/dist/types/src/Icons.stories.d.ts.map +0 -1
  54. package/dist/types/src/Logotypes.stories.d.ts +0 -12
  55. package/dist/types/src/Logotypes.stories.d.ts.map +0 -1
  56. package/dist/types/src/components/ComposerLogo/ComposerLogo.d.ts.map +0 -1
  57. package/dist/types/src/components/ComposerLogo/ComposerLogo.stories.d.ts +0 -30
  58. package/dist/types/src/components/ComposerLogo/ComposerLogo.stories.d.ts.map +0 -1
  59. package/dist/types/src/components/ComposerLogo/index.d.ts +0 -2
  60. package/dist/types/src/components/ComposerLogo/index.d.ts.map +0 -1
  61. package/dist/types/src/components/rive.stories.d.ts +0 -9
  62. package/dist/types/src/components/rive.stories.d.ts.map +0 -1
  63. package/dist/types/src/icons/Composer.d.ts +0 -4
  64. package/dist/types/src/icons/Composer.d.ts.map +0 -1
  65. package/dist/types/src/icons/DXNS.d.ts.map +0 -1
  66. package/dist/types/src/icons/DXOS.d.ts.map +0 -1
  67. package/dist/types/src/icons/ECHO.d.ts.map +0 -1
  68. package/dist/types/src/icons/HALO.d.ts.map +0 -1
  69. package/dist/types/src/icons/KUBE.d.ts.map +0 -1
  70. package/dist/types/src/icons/MESH.d.ts.map +0 -1
  71. package/dist/types/src/icons/index.d.ts.map +0 -1
  72. package/dist/types/src/logotypes/DXOSHorizontalType.d.ts.map +0 -1
  73. package/dist/types/src/logotypes/DXOSType.d.ts.map +0 -1
  74. package/dist/types/src/logotypes/DXOSVerticalType.d.ts.map +0 -1
  75. package/dist/types/src/logotypes/index.d.ts.map +0 -1
  76. package/src/Icons.stories.tsx +0 -47
  77. package/src/components/ComposerLogo/ComposerLogo.stories.tsx +0 -207
  78. package/src/components/ComposerLogo/index.ts +0 -5
  79. package/src/components/rive.stories.tsx +0 -84
  80. /package/dist/types/src/{icons → components/icons}/DXNS.d.ts +0 -0
  81. /package/dist/types/src/{icons → components/icons}/DXOS.d.ts +0 -0
  82. /package/dist/types/src/{icons → components/icons}/ECHO.d.ts +0 -0
  83. /package/dist/types/src/{icons → components/icons}/HALO.d.ts +0 -0
  84. /package/dist/types/src/{icons → components/icons}/KUBE.d.ts +0 -0
  85. /package/dist/types/src/{icons → components/icons}/MESH.d.ts +0 -0
  86. /package/dist/types/src/{icons → components/icons}/index.d.ts +0 -0
  87. /package/dist/types/src/{logotypes → components/logotypes}/DXOSHorizontalType.d.ts +0 -0
  88. /package/dist/types/src/{logotypes → components/logotypes}/DXOSType.d.ts +0 -0
  89. /package/dist/types/src/{logotypes → components/logotypes}/DXOSVerticalType.d.ts +0 -0
  90. /package/dist/types/src/{logotypes → components/logotypes}/index.d.ts +0 -0
  91. /package/src/{icons → components/icons}/index.ts +0 -0
  92. /package/src/{logotypes → components/logotypes}/DXOSHorizontalType.tsx +0 -0
  93. /package/src/{logotypes → components/logotypes}/DXOSType.tsx +0 -0
  94. /package/src/{logotypes → components/logotypes}/DXOSVerticalType.tsx +0 -0
  95. /package/src/{logotypes → components/logotypes}/index.ts +0 -0
package/package.json CHANGED
@@ -1,23 +1,25 @@
1
1
  {
2
2
  "name": "@dxos/brand",
3
- "version": "0.8.3",
3
+ "version": "0.8.4-main.03d5cd7b56",
4
4
  "description": "DXOS brand assets.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "DXOS.org",
9
- "sideEffects": true,
13
+ "sideEffects": false,
10
14
  "type": "module",
11
15
  "exports": {
12
16
  ".": {
17
+ "source": "./src/index.ts",
13
18
  "types": "./dist/types/src/index.d.ts",
14
19
  "browser": "./dist/lib/browser/index.mjs"
15
20
  }
16
21
  },
17
22
  "types": "dist/types/src/index.d.ts",
18
- "typesVersions": {
19
- "*": {}
20
- },
21
23
  "files": [
22
24
  "assets",
23
25
  "dist",
@@ -28,20 +30,21 @@
28
30
  },
29
31
  "devDependencies": {
30
32
  "@fontsource/k2d": "^5.0.18",
31
- "@phosphor-icons/react": "^2.1.5",
33
+ "@phosphor-icons/react": "2.1.10",
32
34
  "@rive-app/react-canvas": "^4.14.0",
33
35
  "@types/d3": "^7.4.3",
34
- "react": "~18.2.0",
35
- "react-dom": "~18.2.0",
36
- "@dxos/react-ui": "0.8.3",
37
- "@dxos/react-ui-theme": "0.8.3",
38
- "@dxos/storybook-utils": "0.8.3"
36
+ "react": "~19.2.3",
37
+ "react-dom": "~19.2.3",
38
+ "@dxos/log": "0.8.4-main.03d5cd7b56",
39
+ "@dxos/ui-theme": "0.8.4-main.03d5cd7b56",
40
+ "@dxos/storybook-utils": "0.8.4-main.03d5cd7b56",
41
+ "@dxos/react-ui": "0.8.4-main.03d5cd7b56"
39
42
  },
40
43
  "peerDependencies": {
41
- "@phosphor-icons/react": "^2.1.5",
42
- "react": "~18.2.0",
43
- "react-dom": "~18.2.0",
44
- "@dxos/react-ui": "0.8.3",
45
- "@dxos/react-ui-theme": "0.8.3"
44
+ "@phosphor-icons/react": "2.1.10",
45
+ "react": "~19.2.3",
46
+ "react-dom": "~19.2.3",
47
+ "@dxos/react-ui": "0.8.4-main.03d5cd7b56",
48
+ "@dxos/ui-theme": "0.8.4-main.03d5cd7b56"
46
49
  }
47
50
  }
@@ -0,0 +1,66 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { ThemedClassName } from '@dxos/react-ui';
8
+ import { mx } from '@dxos/ui-theme';
9
+
10
+ // Brand icon colors, outer → inner.
11
+ export const brandColors = ['rgb(5,40,61)', 'rgb(10,75,105)', 'rgb(1,122,183)', 'rgb(6,197,253)'];
12
+
13
+ export const ComposerLogo = ({ classNames, size = 512 }: ThemedClassName<{ size?: number }>) => {
14
+ const n = brandColors.length;
15
+ const cx = size / 2;
16
+ const cy = size / 2;
17
+ const ringWidth = size / 2 / (n + 0.5);
18
+
19
+ return (
20
+ <svg aria-hidden='true' width={size} height={size} className={mx(classNames)}>
21
+ {brandColors.map((color, i) => {
22
+ const outerR = size / 2 - i * ringWidth;
23
+ const innerR = outerR - ringWidth;
24
+ // TODO(burdon): Animate bottom length.
25
+ const mirrorOuterR = size - (n - 1 - i) * ringWidth;
26
+ const mirrorInnerR = mirrorOuterR - ringWidth;
27
+ return <path key={i} fill={color} d={makeBrandLayerPath(cx, cy, outerR, innerR, mirrorOuterR, mirrorInnerR)} />;
28
+ })}
29
+ </svg>
30
+ );
31
+ };
32
+
33
+ /**
34
+ * Shape: left semicircle ring (6 → 9 → 12 o'clock) with a
35
+ * horizontal-then-diagonal stepped edge at each open end, matching the
36
+ * Composer brand icon geometry. Two 90° arcs avoid the 180° SVG ambiguity.
37
+ */
38
+ const makeBrandLayerPath = (cx: number, cy: number, r1: number, r2: number, r3: number, r4: number): string => {
39
+ const frac = 0.3;
40
+ const topOuter = r1 * frac;
41
+ const topInner = r2 * frac;
42
+ const botOuter = r4 * frac;
43
+ const botInner = r3 * frac;
44
+ return [
45
+ // Start at outer top-right.
46
+ `M ${cx + topOuter} ${cy - r1}`,
47
+ // Move left to 12-oclock.
48
+ `L ${cx} ${cy - r1}`,
49
+ // Outer arc CW: 12 → 9 → 6 o'clock (through the west side).
50
+ `A ${r1} ${r1} 0 0 0 ${cx - r1} ${cy}`,
51
+ `A ${r1} ${r1} 0 0 0 ${cx} ${cy + r1}`,
52
+ // Bottom outer: extend right.
53
+ `L ${cx + botOuter} ${cy + r1}`,
54
+ // Diagonal to inner bottom-right.
55
+ `L ${cx + botInner} ${cy + r2}`,
56
+ // Move left to inner 6-oclock.
57
+ `L ${cx} ${cy + r2}`,
58
+ // Inner arc CCW: 6 → 9 → 12 o'clock (back through the west side).
59
+ `A ${r2} ${r2} 0 0 1 ${cx - r2} ${cy}`,
60
+ `A ${r2} ${r2} 0 0 1 ${cx} ${cy - r2}`,
61
+ // Top inner: extend right.
62
+ `L ${cx + topInner} ${cy - r2}`,
63
+ // Close: diagonal back to start.
64
+ 'Z',
65
+ ].join(' ');
66
+ };
@@ -0,0 +1,279 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import { arc } from 'd3';
7
+ import React, { useRef, useState } from 'react';
8
+
9
+ import { Button, Icon, IconButton } from '@dxos/react-ui';
10
+ import { withTheme } from '@dxos/react-ui/testing';
11
+ import { mx } from '@dxos/ui-theme';
12
+
13
+ import ident from '../../../assets/sounds/ident-2.mp3';
14
+ import { DXOS } from '../icons';
15
+ import { ComposerLogo as AltComposerLogo, brandColors } from './ComposerLogo';
16
+ import { type AnimationController, ComposerLogo, ComposerSpinner } from './experimental';
17
+
18
+ // https://pixabay.com/sound-effects/search/logo/?pagi=2
19
+
20
+ const meta = {
21
+ title: 'ui/brand/experimental/Logo',
22
+ decorators: [withTheme()],
23
+ } satisfies Meta;
24
+
25
+ export default meta;
26
+
27
+ type Story = StoryObj<typeof meta>;
28
+
29
+ // TODO(burdon): Get from theme?
30
+ const colors = {
31
+ gray: '#888888',
32
+ purple: '#AA23D3',
33
+ orange: '#CA6346',
34
+ green: '#4DA676',
35
+ blue: '#539ACD',
36
+ };
37
+
38
+ export const Default: Story = {
39
+ render: () => {
40
+ const controller = useRef<AnimationController>(null);
41
+ const [logo, setLogo] = useState(false);
42
+ const handleSpin = async () => {
43
+ const audio = new Audio(ident);
44
+ try {
45
+ await audio.play();
46
+ } catch (err) {
47
+ console.warn('Audio playback failed:', err);
48
+ }
49
+ setTimeout(() => {
50
+ setLogo(true);
51
+ }, 1_500);
52
+
53
+ controller.current?.spin();
54
+ };
55
+
56
+ return (
57
+ <div className='absolute flex inset-0 items-center justify-center'>
58
+ <div className='absolute left-4 top-4'>
59
+ <Button onClick={handleSpin}>Spin</Button>
60
+ </div>
61
+
62
+ <div>
63
+ <div className='flex justify-center'>
64
+ <ComposerLogo ref={controller} size={256} />
65
+ </div>
66
+
67
+ <div className={mx('transition opacity-0 duration-1000', logo && 'opacity-100')}>
68
+ <div className={mx('text-[100px] text-teal-400')} style={{ fontFamily: 'Poiret One' }}>
69
+ composer
70
+ </div>
71
+ <div className={mx('flex items-center -mt-[20px] text-neutral-700')}>
72
+ <span className='ml-[210px] mt-[2px] mr-2'>Powered by DXOS</span>
73
+ <div>
74
+ <DXOS className='w-[32px] h-[32px]' />
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ );
81
+ },
82
+ };
83
+
84
+ export const Colors: Story = {
85
+ render: () => {
86
+ const colors = [
87
+ ['fill-teal-400', 'fill-teal-500', 'fill-teal-600'],
88
+ ['fill-orange-400', 'fill-orange-500', 'fill-orange-600'],
89
+ ['fill-cyan-400', 'fill-cyan-500', 'fill-cyan-600'],
90
+ ['fill-purple-400', 'fill-purple-500', 'fill-purple-600'],
91
+ ['fill-blue-500', 'fill-blue-600', 'fill-blue-700'],
92
+ ['fill-slate-500', 'fill-slate-600', 'fill-slate-700'],
93
+ ['fill-blue-500', 'fill-neutral-100', 'fill-red-500'],
94
+ ['fill-stone-400', 'fill-stone-500', 'fill-stone-600'],
95
+ ['fill-neutral-500', 'fill-neutral-600', 'fill-neutral-700'],
96
+ ];
97
+
98
+ return (
99
+ <div className='absolute inset-0 flex justify-center items-center'>
100
+ <div className='grid grid-cols-3 gap-20 w-[800px]'>
101
+ {colors.map((classNames, i) => (
102
+ <div key={i} className='flex justify-center items-center'>
103
+ <ComposerLogo animate={false} size={160} classNames={classNames} />
104
+ </div>
105
+ ))}
106
+ </div>
107
+ </div>
108
+ );
109
+ },
110
+ };
111
+
112
+ export const Pacman: Story = {
113
+ render: () => {
114
+ return (
115
+ <div className='absolute inset-0 flex flex-col justify-center'>
116
+ <div className='flex flex-col'>
117
+ <div className='flex items-center p-4'>
118
+ <div className='flex ml-8 mr-[100px]'>
119
+ <Icon icon='ph--ghost--duotone' classNames='w-[180px] h-[180px] text-blue-500' />
120
+ <Icon icon='ph--ghost--duotone' classNames='w-[180px] h-[180px] text-purple-500' />
121
+ <Icon icon='ph--ghost--duotone' classNames='w-[180px] h-[180px] text-red-500' />
122
+ </div>
123
+
124
+ <div className='w-[180px]'>
125
+ <ComposerLogo size={145} classNames={['fill-yellow-200', 'fill-yellow-300', 'fill-yellow-400']} />
126
+ </div>
127
+
128
+ <div className='flex -ml-10'>
129
+ {Array.from({ length: 6 }).map((_, i) => (
130
+ <div key={i} className='p-4'>
131
+ <Icon icon='ph--circle--duotone' classNames='w-6 h-6 text-yellow-200' />
132
+ </div>
133
+ ))}
134
+ </div>
135
+ </div>
136
+
137
+ <div className='flex justify-center font-mono font-light text-[60px] mt-8 text-neutral-200'>
138
+ <div>Ready Player 1</div>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ );
143
+ },
144
+ };
145
+
146
+ const SpinnerContainer = () => {
147
+ const [spinning, setSpinning] = useState(false);
148
+ return (
149
+ <div className='flex flex-col gap-20'>
150
+ <div className='absolute left-4 top-4'>
151
+ {(spinning && <Button onClick={() => setSpinning(false)}>Stop</Button>) || (
152
+ <Button onClick={() => setSpinning(true)}>Start</Button>
153
+ )}
154
+ </div>
155
+ <div className='grid grid-cols-3 gap-20'>
156
+ <div className='flex justify-center items-center'>
157
+ <ComposerSpinner animate={spinning} gap={1} size={200} color={colors.blue} />
158
+ </div>
159
+ <div className='flex justify-center items-center'>
160
+ <ComposerSpinner animate={spinning} gap={1} size={200} color={colors.green} />
161
+ </div>
162
+ <div className='flex justify-center items-center'>
163
+ <ComposerSpinner animate={spinning} gap={1} size={200} color={colors.orange} />
164
+ </div>
165
+ </div>
166
+ <div className='flex gap-20'>
167
+ <div className='flex justify-center items-center'>
168
+ <ComposerSpinner animate={spinning} gap={1} size={200} color={colors.blue} />
169
+ </div>
170
+ <div className='flex justify-center items-center'>
171
+ <ComposerSpinner animate={spinning} gap={1} size={100} color={colors.green} />
172
+ </div>
173
+ <div className='flex justify-center items-center'>
174
+ <ComposerSpinner animate={spinning} gap={1} size={40} color={colors.orange} />
175
+ </div>
176
+ <div className='flex justify-center items-center'>
177
+ <ComposerSpinner animate={spinning} gap={1} size={24} color={colors.orange} />
178
+ </div>
179
+ </div>
180
+ </div>
181
+ );
182
+ };
183
+
184
+ export const Spinner: Story = {
185
+ render: () => {
186
+ return (
187
+ <div className='absolute inset-0 flex items-center justify-center'>
188
+ <SpinnerContainer />
189
+ </div>
190
+ );
191
+ },
192
+ };
193
+
194
+ // https://github.com/grafana/grafana/blob/main/packages/grafana-ui/src/components/LoadingBar/LoadingBar.tsx
195
+ export const Linear: Story = {
196
+ render: () => {
197
+ return (
198
+ <div className='absolute flex flex-col inset-0 bg-black'>
199
+ <div
200
+ className={'h-[1px] translateX(-100%) animate-progress-linear'}
201
+ style={{
202
+ background:
203
+ 'linear-gradient(90deg, rgba(110, 159, 255, 0) 0%, #6E9FFF 80.75%, rgba(110, 159, 255, 0) 100%)',
204
+ }}
205
+ />
206
+ </div>
207
+ );
208
+ },
209
+ };
210
+
211
+ export const Radial: Story = {
212
+ render: () => {
213
+ const size = 256;
214
+ const totalRadius = size / 2;
215
+ const n = brandColors.length;
216
+ const ringWidth = totalRadius / (n + 1);
217
+ const gap = 0;
218
+ const startAngle = (1 / 4) * Math.PI;
219
+ const endAngle = -(5 / 4) * Math.PI;
220
+
221
+ return (
222
+ <div className='absolute inset-0 flex items-center justify-center'>
223
+ <svg width={size} height={size}>
224
+ <g transform={`translate(${totalRadius}, ${totalRadius})`}>
225
+ {brandColors.map((color, i) => {
226
+ const outerRadius = totalRadius - i * ringWidth;
227
+ const innerRadius = outerRadius - ringWidth + gap;
228
+ const d = arc<any, any>()
229
+ .innerRadius(innerRadius)
230
+ .outerRadius(outerRadius)
231
+ .startAngle(startAngle)
232
+ .endAngle(endAngle)({}) as string;
233
+ return <path key={i} d={d} fill={color} />;
234
+ })}
235
+ </g>
236
+ </svg>
237
+ </div>
238
+ );
239
+ },
240
+ };
241
+
242
+ const useToggle = (initial = false) => {
243
+ const [toggled, setToggled] = useState(initial);
244
+ return [toggled, () => setToggled(!toggled)] as const;
245
+ };
246
+
247
+ export const Oblique: Story = {
248
+ render: () => {
249
+ const [visible, setVisible] = useToggle(true);
250
+ const size = 512;
251
+
252
+ return (
253
+ <div className='absolute inset-0 grid place-items-center'>
254
+ <div className='absolute top-4 left-4'>
255
+ <IconButton icon='ph--square--duotone' label='Visibility' onClick={() => setVisible()} />
256
+ </div>
257
+ <div className='absolute grid place-items-center'>
258
+ <AltComposerLogo
259
+ size={size}
260
+ classNames={mx(
261
+ 'opacity-0 scale-10 transition-all rotate-[540deg] __translate-x-[980px] duration-500 delay-0 ease-in',
262
+ visible && 'delay-200 opacity-10 scale-100 rotate-[180deg] translate-x-[8]',
263
+ )}
264
+ />
265
+ </div>
266
+ <div className='absolute grid place-items-center'>
267
+ <AltComposerLogo
268
+ size={size}
269
+ classNames={mx(
270
+ 'opacity-0 blur-xs scale-10 rotate-360 __translate-y-[-100px]',
271
+ 'transition-[opacity,filter,scale,rotate] duration-[500ms,500ms,500ms,750ms] ease-in-out',
272
+ visible && 'opacity-100 blur-none scale-100 rotate-0 translate-y-0',
273
+ )}
274
+ />
275
+ </div>
276
+ </div>
277
+ );
278
+ },
279
+ };
@@ -2,7 +2,7 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { IconBase, type IconProps, type IconWeight } from '@phosphor-icons/react';
5
+ import { type IconWeight } from '@phosphor-icons/react';
6
6
  import { arc, interpolateString, select } from 'd3';
7
7
  import React, {
8
8
  type CSSProperties,
@@ -17,7 +17,7 @@ import React, {
17
17
  useState,
18
18
  } from 'react';
19
19
 
20
- import { mx } from '@dxos/react-ui-theme';
20
+ import { mx } from '@dxos/ui-theme';
21
21
 
22
22
  const weights = new Map<IconWeight, ReactElement>([
23
23
  [
@@ -28,9 +28,23 @@ const weights = new Map<IconWeight, ReactElement>([
28
28
  ],
29
29
  ]);
30
30
 
31
- const Composer = forwardRef<SVGSVGElement, IconProps>((props, ref) => (
32
- <IconBase ref={ref} {...props} weights={weights} />
33
- ));
31
+ const Composer = forwardRef<SVGSVGElement, any>((props, ref) => {
32
+ const weight = props.weight || 'regular';
33
+ const size = props.size || 256;
34
+ return (
35
+ <svg
36
+ ref={ref}
37
+ {...props}
38
+ width={size}
39
+ height={size}
40
+ viewBox='0 0 256 256'
41
+ fill='currentColor'
42
+ xmlns='http://www.w3.org/2000/svg'
43
+ >
44
+ {weights.get(weight)}
45
+ </svg>
46
+ );
47
+ });
34
48
 
35
49
  export interface AnimationController {
36
50
  spin: () => void;
@@ -103,7 +117,7 @@ export const ComposerLogo = forwardRef<AnimationController, ComposerLogoProps>(
103
117
  >
104
118
  {layers.map(({ inset, spin, style, className }, i) => (
105
119
  <div key={i} className='absolute' style={{ inset: `${inset}px` }}>
106
- <Composer className={mx('w-full h-full', animate && spin, className)} style={style} />
120
+ <Composer className={mx('h-full w-full', animate && spin, className)} style={style} />
107
121
  </div>
108
122
  ))}
109
123
  </div>
@@ -163,7 +177,6 @@ const createSlices = ({
163
177
  /**
164
178
  * Spinning Composer "C" logo.
165
179
  */
166
- // TODO(burdon): Configure stripes.
167
180
  export const ComposerSpinner: FC<{
168
181
  animate?: boolean;
169
182
  size?: number;
@@ -183,13 +196,17 @@ export const ComposerSpinner: FC<{
183
196
  }, [animate]);
184
197
 
185
198
  useEffect(() => {
186
- const svg = select(ref.current)
199
+ const el = ref.current;
200
+ if (!el) {
201
+ return;
202
+ }
203
+
204
+ const svg = select(el)
187
205
  .attr('width', size)
188
206
  .attr('height', size)
189
207
  .append('g')
190
208
  .attr('transform', `translate(${size / 2}, ${size / 2})`);
191
209
 
192
- // TODO(burdon): Pass in.
193
210
  const arcs = createSlices({ radius: size / 2, gap, color });
194
211
 
195
212
  let count = 0;
@@ -204,14 +221,6 @@ export const ComposerSpinner: FC<{
204
221
  }
205
222
  };
206
223
 
207
- // const createArc = ({
208
- // innerRadius,
209
- // outerRadius,
210
- // startAngle = (1 / 4) * Math.PI,
211
- // endAngle = -(5 / 4) * Math.PI,
212
- // }: Slice): ValueFn<SVGPathElement, DefaultArcObject, string | null> =>
213
- // arc().innerRadius(innerRadius).outerRadius(outerRadius).startAngle(startAngle).endAngle(endAngle);
214
-
215
224
  const trigger = arcs.map(
216
225
  ({
217
226
  startAngle = (1 / 4) * Math.PI,
@@ -234,18 +243,11 @@ export const ComposerSpinner: FC<{
234
243
  .transition()
235
244
  .duration(duration)
236
245
  .attrTween('transform', (() => interpolateString('rotate(0)', 'rotate(360)')) as any)
237
- .on('end', ((_: any, i: number, nodes: Node[]) => {
246
+ .on('end', (() => {
238
247
  if (animateRef.current) {
239
248
  rotateArc();
240
249
  } else if (autoFade) {
241
250
  fadeOut();
242
- // d3.select(nodes[i])
243
- // .transition()
244
- // .duration(1000)
245
- // .attrTween('d', () => {
246
- // const interpolate = d3.interpolate(0, Math.PI);
247
- // return (t: number) => createArc(arc);
248
- // });
249
251
  }
250
252
  }) as any);
251
253
  };
@@ -264,9 +266,9 @@ export const ComposerSpinner: FC<{
264
266
  }
265
267
 
266
268
  return () => {
267
- select(ref.current).selectChildren().remove();
269
+ select(el).selectChildren().remove();
268
270
  };
269
- }, []);
271
+ }, [size, gap, color]);
270
272
 
271
273
  return <svg ref={ref} onClick={onClick} />;
272
274
  };
@@ -0,0 +1,103 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Rive, useRive } from '@rive-app/react-canvas';
6
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
+ import React, { useEffect } from 'react';
8
+
9
+ import { log } from '@dxos/log';
10
+ import { useAsyncState } from '@dxos/react-ui';
11
+ import { withLayout, withTheme, Loading } from '@dxos/react-ui/testing';
12
+ import { mx } from '@dxos/ui-theme';
13
+
14
+ const useFlash = (rive: Rive | null, name: string, delay: number, period: number) => {
15
+ useEffect(() => {
16
+ let t: any;
17
+ if (rive) {
18
+ t = setTimeout(() => {
19
+ t = setInterval(() => {
20
+ rive?.play(name);
21
+ }, period);
22
+ }, delay);
23
+ }
24
+
25
+ return () => clearTimeout(t);
26
+ }, [rive]);
27
+ };
28
+
29
+ const Component = ({ buffer }: { buffer: ArrayBuffer }) => {
30
+ // https://rive.app/community/doc/rive-parameters/docHI9ASztXP
31
+ const { rive, RiveComponent } = useRive({ buffer, autoplay: false });
32
+ useFlash(rive, 'flash-1', 500, 3_000);
33
+ useFlash(rive, 'flash-2', 2_000, 2_000);
34
+
35
+ return (
36
+ <div className='m-8 relative flex grow justify-center'>
37
+ <RiveComponent />
38
+ <div className='z-1 absolute inset-0' style={{ background: 'radial-gradient(transparent, black)' }} />
39
+ </div>
40
+ );
41
+ };
42
+
43
+ const DefaultStory = () => {
44
+ const [buffer] = useAsyncState<ArrayBuffer>(async () => {
45
+ // CORS set via dashboard.
46
+ // TODO(wittjosiah): Fetch to external url fails in headless storybook test.
47
+ const response = await fetch('https://dxos.network/dxos.riv', { mode: 'cors' }).catch((error) => {
48
+ log.catch(error);
49
+ });
50
+ if (response?.ok) {
51
+ return await response.arrayBuffer();
52
+ } else if (response) {
53
+ console.log(response.status);
54
+ }
55
+ });
56
+
57
+ if (!buffer) {
58
+ return <Loading data={{ buffer: !!buffer }} />;
59
+ }
60
+
61
+ return (
62
+ <>
63
+ <Component buffer={buffer} />
64
+ {false && (
65
+ <div className='flex absolute left-0 right-0 top-[120px] h-[320px] align-center'>
66
+ <div
67
+ className='z-1 absolute inset-0 w-[800px] m-auto'
68
+ style={{
69
+ background: 'radial-gradient(ellipse 200% 100% at center, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0) 50%)',
70
+ }}
71
+ />
72
+ <div
73
+ className={mx(
74
+ 'z-2 absolute inset-0 items-center w-[720px] m-auto p-2',
75
+ 'text-white text-[60px] leading-tight text-center font-thin',
76
+ )}
77
+ >
78
+ <div className='flex flex-col items-center opacity-0'>
79
+ <div>The new standard</div>
80
+ <div>for collaborative</div>
81
+ <div>local-first software</div>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ )}
86
+ </>
87
+ );
88
+ };
89
+
90
+ const meta = {
91
+ title: 'ui/brand/experimental/Rive',
92
+ render: DefaultStory,
93
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
94
+ parameters: {
95
+ layout: 'fullscreen',
96
+ },
97
+ } satisfies Meta<typeof DefaultStory>;
98
+
99
+ export default meta;
100
+
101
+ type Story = StoryObj<typeof meta>;
102
+
103
+ export const Default: Story = {};
@@ -2,8 +2,8 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { IconBase, type IconProps, type IconWeight } from '@phosphor-icons/react';
6
- import React, { forwardRef, type ReactElement } from 'react';
5
+ import { type IconWeight } from '@phosphor-icons/react';
6
+ import React, { type ReactElement, forwardRef } from 'react';
7
7
 
8
8
  const weights = new Map<IconWeight, ReactElement>([
9
9
  [
@@ -36,8 +36,22 @@ const weights = new Map<IconWeight, ReactElement>([
36
36
  ],
37
37
  ]);
38
38
 
39
- export const Composer = forwardRef<SVGSVGElement, IconProps>((props, ref) => (
40
- <IconBase ref={ref} {...props} weights={weights} />
41
- ));
39
+ export const Composer = forwardRef<SVGSVGElement, any>((props, forwardedRef) => {
40
+ const weight = props.weight || 'regular';
41
+ const size = props.size || 256;
42
+ return (
43
+ <svg
44
+ {...props}
45
+ width={size}
46
+ height={size}
47
+ viewBox='0 0 256 256'
48
+ fill='currentColor'
49
+ xmlns='http://www.w3.org/2000/svg'
50
+ ref={forwardedRef}
51
+ >
52
+ {weights.get(weight)}
53
+ </svg>
54
+ );
55
+ });
42
56
 
43
57
  Composer.displayName = 'Composer';
@@ -2,8 +2,9 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { IconBase, type IconProps, type IconWeight } from '@phosphor-icons/react';
6
- import React, { forwardRef, type ReactElement } from 'react';
5
+ import { type IconProps, type IconWeight } from '@phosphor-icons/react';
6
+ import IconBase from '@phosphor-icons/react/dist/lib/IconBase';
7
+ import React, { type ReactElement, forwardRef } from 'react';
7
8
 
8
9
  const weights = new Map<IconWeight, ReactElement>([
9
10
  [