tinywidgets 0.0.0 → 0.0.2
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/.eslintrc.json +91 -0
- package/.prettierrc +5 -0
- package/bun.lockb +0 -0
- package/index.css.ts +1 -0
- package/index.ts +11 -0
- package/media.ts +1 -0
- package/package.json +27 -2
- package/src/Avatar/index.css.ts +17 -0
- package/src/Avatar/index.tsx +28 -0
- package/src/Axis/index.css.ts +19 -0
- package/src/Axis/index.tsx +38 -0
- package/src/Button/index.css.ts +57 -0
- package/src/Button/index.tsx +65 -0
- package/src/Card/index.css.ts +9 -0
- package/src/Card/index.tsx +15 -0
- package/src/Collapsible/index.css.ts +34 -0
- package/src/Collapsible/index.tsx +69 -0
- package/src/Detail/index.css.ts +19 -0
- package/src/Detail/index.tsx +28 -0
- package/src/Hr/index.css.ts +10 -0
- package/src/Hr/index.tsx +10 -0
- package/src/Metric/index.css.ts +18 -0
- package/src/Metric/index.tsx +28 -0
- package/src/Summary/index.css.ts +17 -0
- package/src/Summary/index.tsx +34 -0
- package/src/Tag/index.css.ts +27 -0
- package/src/Tag/index.tsx +28 -0
- package/src/Ui/Layout/Header/DarkButton/index.tsx +20 -0
- package/src/Ui/Layout/Header/SideNav/index.css.ts +23 -0
- package/src/Ui/Layout/Header/SideNav/index.tsx +16 -0
- package/src/Ui/Layout/Header/SideNavButton/index.css.ts +4 -0
- package/src/Ui/Layout/Header/SideNavButton/index.tsx +17 -0
- package/src/Ui/Layout/Header/Title/index.css.ts +10 -0
- package/src/Ui/Layout/Header/Title/index.tsx +10 -0
- package/src/Ui/Layout/Header/TopNav/index.css.ts +9 -0
- package/src/Ui/Layout/Header/TopNav/index.tsx +19 -0
- package/src/Ui/Layout/Header/index.css.ts +18 -0
- package/src/Ui/Layout/Header/index.tsx +33 -0
- package/src/Ui/Layout/Main/Article/index.css.ts +13 -0
- package/src/Ui/Layout/Main/Article/index.tsx +10 -0
- package/src/Ui/Layout/Main/Footer/index.css.ts +12 -0
- package/src/Ui/Layout/Main/Footer/index.tsx +10 -0
- package/src/Ui/Layout/Main/index.css.ts +16 -0
- package/src/Ui/Layout/Main/index.tsx +26 -0
- package/src/Ui/Layout/index.css.ts +9 -0
- package/src/Ui/Layout/index.tsx +52 -0
- package/src/Ui/LocalStore.tsx +55 -0
- package/src/Ui/SessionStore.tsx +60 -0
- package/src/Ui/index.tsx +26 -0
- package/src/index.css.ts +125 -0
- package/src/index.ts +12 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"parser": "@typescript-eslint/parser",
|
|
3
|
+
"plugins": ["@typescript-eslint", "react", "react-hooks", "react-refresh"],
|
|
4
|
+
"extends": [
|
|
5
|
+
"eslint:recommended",
|
|
6
|
+
"plugin:react/all",
|
|
7
|
+
"plugin:react-hooks/recommended",
|
|
8
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
|
9
|
+
"plugin:@typescript-eslint/recommended",
|
|
10
|
+
"prettier"
|
|
11
|
+
],
|
|
12
|
+
"env": {"browser": true, "es6": true, "node": true},
|
|
13
|
+
"globals": {"page": true, "browser": true, "context": true},
|
|
14
|
+
"parserOptions": {
|
|
15
|
+
"ecmaVersion": 7,
|
|
16
|
+
"ecmaFeatures": {
|
|
17
|
+
"experimentalObjectRestSpread": true,
|
|
18
|
+
"globalReturn": true,
|
|
19
|
+
"jsx": true
|
|
20
|
+
},
|
|
21
|
+
"sourceType": "module"
|
|
22
|
+
},
|
|
23
|
+
"settings": {"react": {"version": "18.3.0"}},
|
|
24
|
+
"rules": {
|
|
25
|
+
"@typescript-eslint/no-explicit-any": 0,
|
|
26
|
+
"@typescript-eslint/no-var-requires": 0,
|
|
27
|
+
"@typescript-eslint/no-unused-vars": [
|
|
28
|
+
2,
|
|
29
|
+
{"argsIgnorePattern": "^_.*", "varsIgnorePattern": "^_.*"}
|
|
30
|
+
],
|
|
31
|
+
"max-len": [2, {"code": 80, "ignorePattern": "^(im|ex)ports?\\W.*"}],
|
|
32
|
+
"no-var": 2,
|
|
33
|
+
"no-console": 2,
|
|
34
|
+
"object-curly-spacing": [2, "never"],
|
|
35
|
+
"comma-dangle": [
|
|
36
|
+
2,
|
|
37
|
+
{
|
|
38
|
+
"arrays": "always-multiline",
|
|
39
|
+
"objects": "always-multiline",
|
|
40
|
+
"imports": "always-multiline",
|
|
41
|
+
"exports": "always-multiline",
|
|
42
|
+
"functions": "always-multiline"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"indent": 0,
|
|
46
|
+
"no-empty": [2, {"allowEmptyCatch": true}],
|
|
47
|
+
"linebreak-style": [2, "unix"],
|
|
48
|
+
"space-infix-ops": 2,
|
|
49
|
+
"quotes": [2, "single", {"allowTemplateLiterals": true}],
|
|
50
|
+
"semi": [2, "always"],
|
|
51
|
+
"sort-keys": 0,
|
|
52
|
+
"sort-imports": 2,
|
|
53
|
+
"no-multiple-empty-lines": [2, {"max": 1}],
|
|
54
|
+
"react/function-component-definition": [
|
|
55
|
+
2,
|
|
56
|
+
{
|
|
57
|
+
"namedComponents": "arrow-function",
|
|
58
|
+
"unnamedComponents": "arrow-function"
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"react/no-multi-comp": [2, {"ignoreStateless": true}],
|
|
62
|
+
"react/no-find-dom-node": 0,
|
|
63
|
+
"react/no-set-state": 0,
|
|
64
|
+
"react/no-unsafe": 2,
|
|
65
|
+
"jsx-quotes": [2, "prefer-double"],
|
|
66
|
+
"react-hooks/exhaustive-deps": 2,
|
|
67
|
+
"react-hooks/rules-of-hooks": 2,
|
|
68
|
+
"react/destructuring-assignment": 0,
|
|
69
|
+
"react/display-name": 0,
|
|
70
|
+
"react/jsx-boolean-value": 0,
|
|
71
|
+
"react/jsx-filename-extension": 0,
|
|
72
|
+
"react/jsx-first-prop-new-line": [2, "multiline"],
|
|
73
|
+
"react/jsx-handler-names": [
|
|
74
|
+
2,
|
|
75
|
+
{"eventHandlerPrefix": "_handle", "eventHandlerPropPrefix": "on"}
|
|
76
|
+
],
|
|
77
|
+
"react/jsx-indent": 0,
|
|
78
|
+
"react/jsx-indent-props": [2, 2],
|
|
79
|
+
"react/jsx-max-depth": [2, {"max": 5}],
|
|
80
|
+
"react/jsx-max-props-per-line": [2, {"maximum": 1, "when": "multiline"}],
|
|
81
|
+
"react/jsx-newline": 0,
|
|
82
|
+
"react/jsx-no-literals": 0,
|
|
83
|
+
"react/jsx-one-expression-per-line": 0,
|
|
84
|
+
"react/jsx-props-no-spreading": 0,
|
|
85
|
+
"react/jsx-sort-props": 0,
|
|
86
|
+
"react/require-default-props": 0,
|
|
87
|
+
"react/sort-comp": 0,
|
|
88
|
+
"react/forbid-component-props": 0,
|
|
89
|
+
"react/button-has-type": 0
|
|
90
|
+
}
|
|
91
|
+
}
|
package/.prettierrc
ADDED
package/bun.lockb
ADDED
|
Binary file
|
package/index.css.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {accent, accentContrast, theme} from './src/index.css.ts';
|
package/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export {Avatar} from './src/Avatar/index.tsx';
|
|
2
|
+
export {Axis} from './src/Axis/index.tsx';
|
|
3
|
+
export {Button} from './src/Button/index.tsx';
|
|
4
|
+
export {Card} from './src/Card/index.tsx';
|
|
5
|
+
export {Collapsible} from './src/Collapsible/index.tsx';
|
|
6
|
+
export {Detail} from './src/Detail/index.tsx';
|
|
7
|
+
export {Hr} from './src/Hr/index.tsx';
|
|
8
|
+
export {Metric} from './src/Metric/index.tsx';
|
|
9
|
+
export {Summary} from './src/Summary/index.tsx';
|
|
10
|
+
export {Tag} from './src/Tag/index.tsx';
|
|
11
|
+
export {Ui} from './src/Ui/index.tsx';
|
package/media.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {large} from './src/index.ts';
|
package/package.json
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tinywidgets",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"author": "jamesgpearce",
|
|
5
5
|
"repository": "github:tinyplex/tinywidgets",
|
|
6
|
+
"module": "index.ts",
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@types/react": "^18.3.3",
|
|
9
|
+
"@types/react-dom": "^18.3.0",
|
|
10
|
+
"@typescript-eslint/eslint-plugin": "^7.16.1",
|
|
11
|
+
"@typescript-eslint/parser": "^7.16.1",
|
|
12
|
+
"eslint": "^8.57.0",
|
|
13
|
+
"eslint-config-prettier": "^9.1.0",
|
|
14
|
+
"eslint-plugin-react": "^7.34.4",
|
|
15
|
+
"eslint-plugin-react-hooks": "^4.6.2",
|
|
16
|
+
"prettier": "^3.3.3",
|
|
17
|
+
"typescript": "^5.5.3",
|
|
18
|
+
"@vanilla-extract/css": "^1.15.5",
|
|
19
|
+
"react": "^18.3.1",
|
|
20
|
+
"react-dom": "^18.3.1",
|
|
21
|
+
"tinybase": "^5.1.0"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
".": "./index.ts",
|
|
25
|
+
"./media": "./media.ts",
|
|
26
|
+
"./css": "./index.css.ts"
|
|
27
|
+
},
|
|
28
|
+
"description": "reserved",
|
|
6
29
|
"license": "MIT",
|
|
7
|
-
"
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"lucide-react": "^0.438.0"
|
|
32
|
+
}
|
|
8
33
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {style} from '@vanilla-extract/css';
|
|
2
|
+
import {theme} from '../index.css';
|
|
3
|
+
|
|
4
|
+
export const avatar = style({
|
|
5
|
+
display: 'inline-block',
|
|
6
|
+
width: '2rem',
|
|
7
|
+
height: '2rem',
|
|
8
|
+
boxShadow: theme.shadow,
|
|
9
|
+
borderRadius: '50%',
|
|
10
|
+
border: `1px solid ${theme.border}`,
|
|
11
|
+
flexShrink: 0,
|
|
12
|
+
selectors: {
|
|
13
|
+
'&:hover': {
|
|
14
|
+
backgroundColor: theme.backgroundHover,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** @jsx createElement */
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import {classNames} from '../index.ts';
|
|
5
|
+
import {avatar} from './index.css.ts';
|
|
6
|
+
|
|
7
|
+
const {createElement} = React;
|
|
8
|
+
|
|
9
|
+
export const Avatar = ({
|
|
10
|
+
src,
|
|
11
|
+
title,
|
|
12
|
+
onClick,
|
|
13
|
+
className,
|
|
14
|
+
}: {
|
|
15
|
+
src: string;
|
|
16
|
+
title?: string;
|
|
17
|
+
onClick?: () => void;
|
|
18
|
+
className?: string;
|
|
19
|
+
}) => {
|
|
20
|
+
return (
|
|
21
|
+
<img
|
|
22
|
+
src={src}
|
|
23
|
+
title={title}
|
|
24
|
+
onClick={onClick}
|
|
25
|
+
className={classNames(avatar, className)}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {style} from '@vanilla-extract/css';
|
|
2
|
+
import {dimensions} from '../index.css';
|
|
3
|
+
|
|
4
|
+
export const axis = style({
|
|
5
|
+
display: 'flex',
|
|
6
|
+
alignItems: 'center',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export const justifyStyle = style({
|
|
10
|
+
justifyContent: 'space-between',
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const verticalStyle = style({
|
|
14
|
+
flexDirection: 'column',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const gapStyle = style({
|
|
18
|
+
gap: dimensions.padding,
|
|
19
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** @jsx createElement */
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import {classNames} from '../';
|
|
5
|
+
import {axis, gapStyle, justifyStyle, verticalStyle} from './index.css';
|
|
6
|
+
|
|
7
|
+
const {createElement} = React;
|
|
8
|
+
|
|
9
|
+
export const Axis = ({
|
|
10
|
+
justify = true,
|
|
11
|
+
gap = true,
|
|
12
|
+
vertical,
|
|
13
|
+
children,
|
|
14
|
+
className,
|
|
15
|
+
title,
|
|
16
|
+
}: {
|
|
17
|
+
justify?: boolean;
|
|
18
|
+
gap?: boolean;
|
|
19
|
+
vertical?: boolean;
|
|
20
|
+
children: React.ReactNode;
|
|
21
|
+
className?: string;
|
|
22
|
+
title?: string;
|
|
23
|
+
}) => {
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
className={classNames(
|
|
27
|
+
axis,
|
|
28
|
+
justify && justifyStyle,
|
|
29
|
+
vertical && verticalStyle,
|
|
30
|
+
gap && gapStyle,
|
|
31
|
+
className,
|
|
32
|
+
)}
|
|
33
|
+
title={title}
|
|
34
|
+
>
|
|
35
|
+
{children}
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {style, styleVariants} from '@vanilla-extract/css';
|
|
2
|
+
import {axisLike, borderLike, radiusLike, theme} from '../index.css';
|
|
3
|
+
|
|
4
|
+
const ghostLike = {
|
|
5
|
+
backgroundColor: 'transparent',
|
|
6
|
+
border: 0,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const button = style([
|
|
10
|
+
axisLike,
|
|
11
|
+
radiusLike,
|
|
12
|
+
{
|
|
13
|
+
textAlign: 'left',
|
|
14
|
+
cursor: 'pointer',
|
|
15
|
+
padding: '0.5rem 1rem',
|
|
16
|
+
outlineOffset: '-2px',
|
|
17
|
+
backgroundColor: theme.background,
|
|
18
|
+
color: 'inherit',
|
|
19
|
+
overflow: 'hidden',
|
|
20
|
+
whiteSpace: 'nowrap',
|
|
21
|
+
transition: 'background-color 0.2s,border-color 0.2s',
|
|
22
|
+
flexShrink: 0,
|
|
23
|
+
selectors: {
|
|
24
|
+
'&:hover': {
|
|
25
|
+
backgroundColor: theme.backgroundHover,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
export const buttonVariant = styleVariants({
|
|
32
|
+
default: borderLike,
|
|
33
|
+
accent: {
|
|
34
|
+
...borderLike,
|
|
35
|
+
backgroundColor: theme.accent,
|
|
36
|
+
color: theme.accentContrast,
|
|
37
|
+
border: 0,
|
|
38
|
+
selectors: {
|
|
39
|
+
'&:hover': {
|
|
40
|
+
backgroundColor: theme.accentHover,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
ghost: ghostLike,
|
|
45
|
+
item: {...ghostLike, width: '100%'},
|
|
46
|
+
icon: {...ghostLike, padding: '0.25rem'},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export const highlight = style({
|
|
50
|
+
backgroundColor: theme.backgroundHover,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export const labelStyle = style({
|
|
54
|
+
flex: 1,
|
|
55
|
+
overflow: 'hidden',
|
|
56
|
+
textOverflow: 'ellipsis',
|
|
57
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** @jsx createElement */
|
|
2
|
+
/** @jsxFrag Fragment */
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import {iconSize} from '../index.css.ts';
|
|
6
|
+
import {classNames} from '../index.ts';
|
|
7
|
+
import {button, buttonVariant, highlight, labelStyle} from './index.css.ts';
|
|
8
|
+
|
|
9
|
+
const {createElement, useCallback, forwardRef} = React;
|
|
10
|
+
|
|
11
|
+
export const Button = forwardRef(
|
|
12
|
+
(
|
|
13
|
+
{
|
|
14
|
+
icon: Icon,
|
|
15
|
+
label,
|
|
16
|
+
labelRight,
|
|
17
|
+
iconRight: IconRight,
|
|
18
|
+
onClick,
|
|
19
|
+
variant = 'default',
|
|
20
|
+
className,
|
|
21
|
+
href,
|
|
22
|
+
title,
|
|
23
|
+
current,
|
|
24
|
+
}: {
|
|
25
|
+
icon?: React.ComponentType<{className?: string}>;
|
|
26
|
+
label?: React.ReactNode;
|
|
27
|
+
labelRight?: React.ReactNode;
|
|
28
|
+
iconRight?: React.ComponentType<{className?: string}>;
|
|
29
|
+
onClick?: () => void;
|
|
30
|
+
variant?: keyof typeof buttonVariant;
|
|
31
|
+
className?: string;
|
|
32
|
+
href?: string;
|
|
33
|
+
title?: string;
|
|
34
|
+
current?: boolean;
|
|
35
|
+
},
|
|
36
|
+
ref: React.Ref<HTMLButtonElement>,
|
|
37
|
+
) => {
|
|
38
|
+
const icon = Icon ? <Icon className={iconSize} /> : null;
|
|
39
|
+
const iconRight = IconRight ? <IconRight className={iconSize} /> : null;
|
|
40
|
+
|
|
41
|
+
const hrefClick = useCallback(
|
|
42
|
+
() => (href ? open(href, '_blank', 'noreferrer') : null),
|
|
43
|
+
[href],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<button
|
|
48
|
+
className={classNames(
|
|
49
|
+
button,
|
|
50
|
+
buttonVariant[variant],
|
|
51
|
+
current && highlight,
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
54
|
+
onClick={onClick ?? hrefClick}
|
|
55
|
+
title={title}
|
|
56
|
+
ref={ref}
|
|
57
|
+
>
|
|
58
|
+
{icon}
|
|
59
|
+
{label ? <span className={labelStyle}>{label}</span> : null}
|
|
60
|
+
{labelRight}
|
|
61
|
+
{iconRight}
|
|
62
|
+
</button>
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** @jsx createElement */
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import {classNames} from '../';
|
|
5
|
+
import {card} from './index.css';
|
|
6
|
+
|
|
7
|
+
const {createElement} = React;
|
|
8
|
+
|
|
9
|
+
export const Card = ({
|
|
10
|
+
children,
|
|
11
|
+
className,
|
|
12
|
+
}: {
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
className?: string;
|
|
15
|
+
}) => <div className={classNames(card, className)}>{children}</div>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {style} from '@vanilla-extract/css';
|
|
2
|
+
import {borderLike, dimensions, paddingLike, radiusLike} from '../index.css';
|
|
3
|
+
|
|
4
|
+
export const collapsible = style([
|
|
5
|
+
borderLike,
|
|
6
|
+
radiusLike,
|
|
7
|
+
{
|
|
8
|
+
display: 'grid',
|
|
9
|
+
gridTemplateRows: 'max-content minmax(0, 0fr)',
|
|
10
|
+
transition: '.2s grid-template-rows ease-in-out',
|
|
11
|
+
overflow: 'hidden',
|
|
12
|
+
marginBottom: dimensions.padding,
|
|
13
|
+
selectors: {
|
|
14
|
+
'&:last-child': {
|
|
15
|
+
marginBottom: 0,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
export const collapsibleOpen = style({
|
|
22
|
+
gridTemplateRows: 'max-content minmax(0, 1fr)',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const button = style({
|
|
26
|
+
margin: '-1px',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const buttonOpen = style({
|
|
30
|
+
borderBottomLeftRadius: 0,
|
|
31
|
+
borderBottomRightRadius: 0,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const content = style([paddingLike, {overflow: 'hidden'}]);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** @jsx createElement */
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import {ChevronDown, ChevronRight} from 'lucide-react';
|
|
5
|
+
import {Button} from '../Button/index.tsx';
|
|
6
|
+
import {classNames} from '../index.ts';
|
|
7
|
+
import {
|
|
8
|
+
useCollapsibleOpen,
|
|
9
|
+
useSetCollapsibleOpen,
|
|
10
|
+
} from '../Ui/SessionStore.tsx';
|
|
11
|
+
import {
|
|
12
|
+
button,
|
|
13
|
+
buttonOpen,
|
|
14
|
+
collapsible,
|
|
15
|
+
collapsibleOpen,
|
|
16
|
+
content,
|
|
17
|
+
} from './index.css.ts';
|
|
18
|
+
|
|
19
|
+
const {createElement, useState, useCallback, useRef} = React;
|
|
20
|
+
|
|
21
|
+
export const Collapsible = ({
|
|
22
|
+
id = '',
|
|
23
|
+
icon: Icon,
|
|
24
|
+
label = <div />,
|
|
25
|
+
labelRight = <div />,
|
|
26
|
+
className,
|
|
27
|
+
children,
|
|
28
|
+
}: {
|
|
29
|
+
id?: string;
|
|
30
|
+
icon?: React.ComponentType<{className?: string}>;
|
|
31
|
+
label?: React.ReactNode;
|
|
32
|
+
labelRight?: React.ReactNode;
|
|
33
|
+
className?: string;
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
}) => {
|
|
36
|
+
// State is in session Store if id is present, otherwise here in component.
|
|
37
|
+
const storedIsOpen = useCollapsibleOpen(id) ?? false;
|
|
38
|
+
const setStoredIsOpen = useSetCollapsibleOpen(id);
|
|
39
|
+
const [stateIsOpen, setStateIsOpen] = useState(false);
|
|
40
|
+
|
|
41
|
+
const isOpen = id ? storedIsOpen : stateIsOpen;
|
|
42
|
+
const setIsOpen = id ? setStoredIsOpen : setStateIsOpen;
|
|
43
|
+
|
|
44
|
+
const [render, setRender] = useState(isOpen);
|
|
45
|
+
const timer = useRef<Timer>();
|
|
46
|
+
|
|
47
|
+
const toggle = useCallback(() => {
|
|
48
|
+
setIsOpen(!isOpen);
|
|
49
|
+
clearTimeout(timer.current);
|
|
50
|
+
timer.current = setTimeout(() => setRender(!isOpen), isOpen ? 200 : 0);
|
|
51
|
+
}, [id, isOpen]);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className={classNames(collapsible, isOpen && collapsibleOpen)}>
|
|
55
|
+
<Button
|
|
56
|
+
onClick={toggle}
|
|
57
|
+
icon={Icon}
|
|
58
|
+
label={label}
|
|
59
|
+
labelRight={labelRight}
|
|
60
|
+
iconRight={isOpen ? ChevronDown : ChevronRight}
|
|
61
|
+
className={classNames(button, render && buttonOpen)}
|
|
62
|
+
current={render}
|
|
63
|
+
/>
|
|
64
|
+
{render ? (
|
|
65
|
+
<div className={classNames(content, className)}>{children}</div>
|
|
66
|
+
) : null}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {style} from '@vanilla-extract/css';
|
|
2
|
+
import {theme} from '../index.css';
|
|
3
|
+
|
|
4
|
+
export const detailTable = style({
|
|
5
|
+
width: '100%',
|
|
6
|
+
borderCollapse: 'collapse',
|
|
7
|
+
margin: '-.5rem 0',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const detailRow = style({
|
|
11
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
12
|
+
selectors: {'&:last-child': {borderBottom: 'none'}},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const detailCell = style({
|
|
16
|
+
padding: '0.5rem 1rem',
|
|
17
|
+
verticalAlign: 'top',
|
|
18
|
+
selectors: {'&:is(th)': {textAlign: 'right'}},
|
|
19
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** @jsx createElement */
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import {classNames} from '../';
|
|
5
|
+
import {detailCell, detailRow, detailTable} from './index.css';
|
|
6
|
+
|
|
7
|
+
const {createElement} = React;
|
|
8
|
+
|
|
9
|
+
export const Detail = ({
|
|
10
|
+
data,
|
|
11
|
+
className,
|
|
12
|
+
}: {
|
|
13
|
+
data: Record<string, React.ReactNode>;
|
|
14
|
+
className?: string;
|
|
15
|
+
}) => {
|
|
16
|
+
return (
|
|
17
|
+
<table className={classNames(detailTable, className)}>
|
|
18
|
+
<tbody>
|
|
19
|
+
{Object.entries(data).map(([key, value]) => (
|
|
20
|
+
<tr key={key} className={detailRow}>
|
|
21
|
+
<th className={detailCell}>{key}:</th>
|
|
22
|
+
<td className={detailCell}>{value}</td>
|
|
23
|
+
</tr>
|
|
24
|
+
))}
|
|
25
|
+
</tbody>
|
|
26
|
+
</table>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {style} from '@vanilla-extract/css';
|
|
2
|
+
import {dimensions, theme} from '../index.css';
|
|
3
|
+
|
|
4
|
+
export const hr = style({
|
|
5
|
+
border: 'none',
|
|
6
|
+
borderBottom: `solid 1px ${theme.border}`,
|
|
7
|
+
margin: `${dimensions.padding} 0`,
|
|
8
|
+
height: '1px',
|
|
9
|
+
width: '100%',
|
|
10
|
+
});
|
package/src/Hr/index.tsx
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {style} from '@vanilla-extract/css';
|
|
2
|
+
|
|
3
|
+
export const metric = style({
|
|
4
|
+
display: 'flex',
|
|
5
|
+
flexDirection: 'column',
|
|
6
|
+
gap: '0.5rem',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export const metricLabel = style({
|
|
10
|
+
display: 'flex',
|
|
11
|
+
alignItems: 'center',
|
|
12
|
+
gap: '0.5rem',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const metricNumber = style({
|
|
16
|
+
fontSize: '1.5rem',
|
|
17
|
+
fontWeight: 'bold',
|
|
18
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** @jsx createElement */
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import {classNames} from '../';
|
|
5
|
+
import {iconSize} from '../index.css';
|
|
6
|
+
import {metric, metricLabel, metricNumber} from './index.css';
|
|
7
|
+
|
|
8
|
+
const {createElement} = React;
|
|
9
|
+
|
|
10
|
+
export const Metric = ({
|
|
11
|
+
icon: Icon,
|
|
12
|
+
label,
|
|
13
|
+
number,
|
|
14
|
+
className,
|
|
15
|
+
}: {
|
|
16
|
+
icon?: React.ComponentType<{className?: string}>;
|
|
17
|
+
label: React.ReactNode;
|
|
18
|
+
number: React.ReactNode;
|
|
19
|
+
className?: string;
|
|
20
|
+
}) => (
|
|
21
|
+
<div className={classNames(metric, className)}>
|
|
22
|
+
<div className={metricLabel}>
|
|
23
|
+
{Icon ? <Icon className={iconSize} /> : null}
|
|
24
|
+
{label}
|
|
25
|
+
</div>
|
|
26
|
+
<div className={metricNumber}>{number}</div>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {style} from '@vanilla-extract/css';
|
|
2
|
+
|
|
3
|
+
export const summary = style({
|
|
4
|
+
display: 'flex',
|
|
5
|
+
flexDirection: 'row',
|
|
6
|
+
gap: '1rem',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export const summaryImage = style({
|
|
10
|
+
flex: '0 0 6rem',
|
|
11
|
+
width: '6rem',
|
|
12
|
+
height: '6rem',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const summaryContent = style({
|
|
16
|
+
flex: 1,
|
|
17
|
+
});
|