@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.
- package/README.md +15 -0
- package/dist/lib/browser/index.mjs +147 -304
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/experimental/ComposerLogo.d.ts +7 -0
- package/dist/types/src/components/experimental/ComposerLogo.d.ts.map +1 -0
- package/dist/types/src/components/experimental/Logo.stories.d.ts +15 -0
- package/dist/types/src/components/experimental/Logo.stories.d.ts.map +1 -0
- package/dist/types/src/components/{ComposerLogo/ComposerLogo.d.ts → experimental/experimental.d.ts} +3 -3
- package/dist/types/src/components/experimental/experimental.d.ts.map +1 -0
- package/dist/types/src/components/experimental/rive.stories.d.ts +14 -0
- package/dist/types/src/components/experimental/rive.stories.d.ts.map +1 -0
- package/dist/types/src/components/icons/Composer.d.ts +3 -0
- package/dist/types/src/components/icons/Composer.d.ts.map +1 -0
- package/dist/types/src/components/icons/DXNS.d.ts.map +1 -0
- package/dist/types/src/components/icons/DXOS.d.ts.map +1 -0
- package/dist/types/src/components/icons/ECHO.d.ts.map +1 -0
- package/dist/types/src/components/icons/HALO.d.ts.map +1 -0
- package/dist/types/src/components/icons/Icons.stories.d.ts +13 -0
- package/dist/types/src/components/icons/Icons.stories.d.ts.map +1 -0
- package/dist/types/src/components/icons/KUBE.d.ts.map +1 -0
- package/dist/types/src/components/icons/MESH.d.ts.map +1 -0
- package/dist/types/src/components/icons/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +2 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/logotypes/DXOSHorizontalType.d.ts.map +1 -0
- package/dist/types/src/components/logotypes/DXOSType.d.ts.map +1 -0
- package/dist/types/src/components/logotypes/DXOSVerticalType.d.ts.map +1 -0
- package/dist/types/src/components/logotypes/Logotypes.stories.d.ts +10 -0
- package/dist/types/src/components/logotypes/Logotypes.stories.d.ts.map +1 -0
- package/dist/types/src/components/logotypes/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +0 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +19 -16
- package/src/components/experimental/ComposerLogo.tsx +66 -0
- package/src/components/experimental/Logo.stories.tsx +279 -0
- package/src/components/{ComposerLogo/ComposerLogo.tsx → experimental/experimental.tsx} +29 -27
- package/src/components/experimental/rive.stories.tsx +103 -0
- package/src/{icons → components/icons}/Composer.tsx +19 -5
- package/src/{icons → components/icons}/DXNS.tsx +3 -2
- package/src/{icons → components/icons}/DXOS.tsx +3 -2
- package/src/{icons → components/icons}/ECHO.tsx +3 -2
- package/src/{icons → components/icons}/HALO.tsx +3 -2
- package/src/components/icons/Icons.stories.tsx +68 -0
- package/src/{icons → components/icons}/KUBE.tsx +3 -2
- package/src/{icons → components/icons}/MESH.tsx +3 -2
- package/src/components/index.ts +3 -2
- package/src/{Logotypes.stories.tsx → components/logotypes/Logotypes.stories.tsx} +19 -14
- package/src/index.ts +0 -2
- package/src/types.d.ts +9 -0
- package/dist/types/src/Icons.stories.d.ts +0 -12
- package/dist/types/src/Icons.stories.d.ts.map +0 -1
- package/dist/types/src/Logotypes.stories.d.ts +0 -12
- package/dist/types/src/Logotypes.stories.d.ts.map +0 -1
- package/dist/types/src/components/ComposerLogo/ComposerLogo.d.ts.map +0 -1
- package/dist/types/src/components/ComposerLogo/ComposerLogo.stories.d.ts +0 -30
- package/dist/types/src/components/ComposerLogo/ComposerLogo.stories.d.ts.map +0 -1
- package/dist/types/src/components/ComposerLogo/index.d.ts +0 -2
- package/dist/types/src/components/ComposerLogo/index.d.ts.map +0 -1
- package/dist/types/src/components/rive.stories.d.ts +0 -9
- package/dist/types/src/components/rive.stories.d.ts.map +0 -1
- package/dist/types/src/icons/Composer.d.ts +0 -4
- package/dist/types/src/icons/Composer.d.ts.map +0 -1
- package/dist/types/src/icons/DXNS.d.ts.map +0 -1
- package/dist/types/src/icons/DXOS.d.ts.map +0 -1
- package/dist/types/src/icons/ECHO.d.ts.map +0 -1
- package/dist/types/src/icons/HALO.d.ts.map +0 -1
- package/dist/types/src/icons/KUBE.d.ts.map +0 -1
- package/dist/types/src/icons/MESH.d.ts.map +0 -1
- package/dist/types/src/icons/index.d.ts.map +0 -1
- package/dist/types/src/logotypes/DXOSHorizontalType.d.ts.map +0 -1
- package/dist/types/src/logotypes/DXOSType.d.ts.map +0 -1
- package/dist/types/src/logotypes/DXOSVerticalType.d.ts.map +0 -1
- package/dist/types/src/logotypes/index.d.ts.map +0 -1
- package/src/Icons.stories.tsx +0 -47
- package/src/components/ComposerLogo/ComposerLogo.stories.tsx +0 -207
- package/src/components/ComposerLogo/index.ts +0 -5
- package/src/components/rive.stories.tsx +0 -84
- /package/dist/types/src/{icons → components/icons}/DXNS.d.ts +0 -0
- /package/dist/types/src/{icons → components/icons}/DXOS.d.ts +0 -0
- /package/dist/types/src/{icons → components/icons}/ECHO.d.ts +0 -0
- /package/dist/types/src/{icons → components/icons}/HALO.d.ts +0 -0
- /package/dist/types/src/{icons → components/icons}/KUBE.d.ts +0 -0
- /package/dist/types/src/{icons → components/icons}/MESH.d.ts +0 -0
- /package/dist/types/src/{icons → components/icons}/index.d.ts +0 -0
- /package/dist/types/src/{logotypes → components/logotypes}/DXOSHorizontalType.d.ts +0 -0
- /package/dist/types/src/{logotypes → components/logotypes}/DXOSType.d.ts +0 -0
- /package/dist/types/src/{logotypes → components/logotypes}/DXOSVerticalType.d.ts +0 -0
- /package/dist/types/src/{logotypes → components/logotypes}/index.d.ts +0 -0
- /package/src/{icons → components/icons}/index.ts +0 -0
- /package/src/{logotypes → components/logotypes}/DXOSHorizontalType.tsx +0 -0
- /package/src/{logotypes → components/logotypes}/DXOSType.tsx +0 -0
- /package/src/{logotypes → components/logotypes}/DXOSVerticalType.tsx +0 -0
- /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
|
+
"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":
|
|
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": "
|
|
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": "~
|
|
35
|
-
"react-dom": "~
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/storybook-utils": "0.8.
|
|
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": "
|
|
42
|
-
"react": "~
|
|
43
|
-
"react-dom": "~
|
|
44
|
-
"@dxos/react-ui": "0.8.
|
|
45
|
-
"@dxos/
|
|
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 {
|
|
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/
|
|
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,
|
|
32
|
-
|
|
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('
|
|
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
|
|
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', ((
|
|
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(
|
|
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 {
|
|
6
|
-
import 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,
|
|
40
|
-
|
|
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 {
|
|
6
|
-
import
|
|
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
|
[
|