tinywidgets 0.0.10 → 1.0.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/.eslintrc.json +2 -1
- package/bun.lockb +0 -0
- package/package.json +19 -14
- package/src/common/functions.tsx +54 -0
- package/src/components/App/index.css.ts +81 -0
- package/src/components/App/index.tsx +177 -0
- package/src/components/Button/index.css.ts +66 -0
- package/src/components/Button/index.tsx +206 -0
- package/src/components/Card/index.css.ts +12 -0
- package/src/components/Card/index.tsx +34 -0
- package/src/components/Code/index.css.ts +49 -0
- package/src/components/Code/index.tsx +89 -0
- package/src/components/Collapsible/index.css.ts +37 -0
- package/src/components/Collapsible/index.tsx +132 -0
- package/src/{Detail → components/Detail}/index.css.ts +3 -3
- package/src/components/Detail/index.tsx +48 -0
- package/src/{Hr → components/Hr}/index.css.ts +3 -2
- package/src/components/Hr/index.tsx +26 -0
- package/src/components/Image/index.css.ts +38 -0
- package/src/components/Image/index.tsx +85 -0
- package/src/components/Metric/index.tsx +59 -0
- package/src/components/Row/index.css.ts +30 -0
- package/src/components/Row/index.tsx +104 -0
- package/src/components/Summary/index.css.ts +16 -0
- package/src/components/Summary/index.tsx +59 -0
- package/src/{Tag → components/Tag}/index.css.ts +9 -6
- package/src/components/Tag/index.tsx +73 -0
- package/src/css/code.css.ts +112 -0
- package/src/css/colors.css.ts +124 -0
- package/src/css/dimensions.css.ts +67 -0
- package/src/css/global.css.ts +11 -0
- package/src/css/screens.ts +15 -0
- package/src/index.css.ts +4 -137
- package/src/index.ts +16 -24
- package/src/stores/LocalStore.tsx +52 -19
- package/src/stores/RouteStore.tsx +103 -0
- package/src/stores/SessionStore.tsx +40 -25
- package/src/stores/common.ts +6 -0
- package/tsconfig.json +19 -0
- package/.prettierrc +0 -5
- package/index.css.ts +0 -1
- package/index.ts +0 -12
- package/src/App/index.tsx +0 -18
- package/src/Avatar/index.css.ts +0 -17
- package/src/Avatar/index.tsx +0 -25
- package/src/Axis/index.css.ts +0 -19
- package/src/Axis/index.tsx +0 -36
- package/src/Button/index.css.ts +0 -57
- package/src/Button/index.tsx +0 -63
- package/src/Card/index.css.ts +0 -9
- package/src/Card/index.tsx +0 -13
- package/src/Collapsible/index.css.ts +0 -34
- package/src/Collapsible/index.tsx +0 -75
- package/src/Detail/index.tsx +0 -26
- package/src/Hr/index.tsx +0 -8
- package/src/Metric/index.tsx +0 -26
- package/src/Summary/index.css.ts +0 -17
- package/src/Summary/index.tsx +0 -32
- package/src/Tag/index.tsx +0 -26
- package/src/Ui/Header/DarkButton/index.tsx +0 -19
- package/src/Ui/Header/SideNav/index.css.ts +0 -23
- package/src/Ui/Header/SideNav/index.tsx +0 -14
- package/src/Ui/Header/SideNavButton/index.css.ts +0 -4
- package/src/Ui/Header/SideNavButton/index.tsx +0 -19
- package/src/Ui/Header/Title/index.css.ts +0 -10
- package/src/Ui/Header/Title/index.tsx +0 -9
- package/src/Ui/Header/TopNav/index.css.ts +0 -9
- package/src/Ui/Header/TopNav/index.tsx +0 -18
- package/src/Ui/Header/index.css.ts +0 -18
- package/src/Ui/Header/index.tsx +0 -32
- package/src/Ui/Main/Article/index.css.ts +0 -13
- package/src/Ui/Main/Article/index.tsx +0 -9
- package/src/Ui/Main/Footer/index.css.ts +0 -12
- package/src/Ui/Main/Footer/index.tsx +0 -9
- package/src/Ui/Main/index.css.ts +0 -16
- package/src/Ui/Main/index.tsx +0 -24
- package/src/Ui/index.css.ts +0 -9
- package/src/Ui/index.tsx +0 -50
- package/utils.ts +0 -1
- /package/src/{Metric → components/Metric}/index.css.ts +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {globalStyle, style} from '@vanilla-extract/css';
|
|
2
|
+
import {code} from '../../css/code.css';
|
|
3
|
+
import {colors} from '../../css/colors.css';
|
|
4
|
+
import {dimensions} from '../../css/dimensions.css';
|
|
5
|
+
|
|
6
|
+
export const pre = style({
|
|
7
|
+
padding: dimensions.padding,
|
|
8
|
+
borderRadius: dimensions.radius,
|
|
9
|
+
background: colors.background2,
|
|
10
|
+
border: colors.border,
|
|
11
|
+
lineHeight: '1.25rem',
|
|
12
|
+
overflowX: 'auto',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
globalStyle('.token.comment, .token.prolog, .token.cdata', {
|
|
16
|
+
color: code['mono-3'],
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
globalStyle('.token.doctype, .token.punctuation, .token.entity', {
|
|
20
|
+
color: code['mono-1'],
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
globalStyle(
|
|
24
|
+
// eslint-disable-next-line max-len
|
|
25
|
+
'.token.attr-name, .token.class-name, .token.boolean, .token.constant, .token.number, .token.atrule',
|
|
26
|
+
{color: code['hue-6']},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
globalStyle('.token.keyword', {color: code['hue-3']});
|
|
30
|
+
|
|
31
|
+
globalStyle(
|
|
32
|
+
// eslint-disable-next-line max-len
|
|
33
|
+
'.token.property, .token.tag, .token.symbol, .token.deleted, .token.important',
|
|
34
|
+
{color: code['hue-5']},
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
globalStyle(
|
|
38
|
+
// eslint-disable-next-line max-len
|
|
39
|
+
'.token.selector, .token.string, .token.char, .token.builtin, .token.inserted, .token.regex, .token.attr-value, .token.attr-value > .token.punctuation',
|
|
40
|
+
{color: code['hue-4']},
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
globalStyle('.token.variable, .token.operator, .token.function', {
|
|
44
|
+
color: code['hue-2'],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
globalStyle('.token.url', {
|
|
48
|
+
color: code['hue-1'],
|
|
49
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
import 'prismjs';
|
|
3
|
+
import 'prismjs/components/prism-jsx';
|
|
4
|
+
import 'prismjs/components/prism-typescript';
|
|
5
|
+
import 'prismjs/components/prism-tsx';
|
|
6
|
+
import Prism from 'prismjs';
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import {classNames} from '../../common/functions.tsx';
|
|
9
|
+
import {pre} from './index.css.ts';
|
|
10
|
+
|
|
11
|
+
const {highlight, languages} = Prism;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The `Code` component displays a block of pre-formatted code, and uses PrismJS
|
|
15
|
+
* to parse its syntax. The coloring is based on the [prism-one-dark](https://github.com/PrismJS/prism-themes/blob/master/themes/prism-one-dark.css) and [prism-one-light](https://github.com/PrismJS/prism-themes/blob/master/themes/prism-one-light.css) themes.
|
|
16
|
+
*
|
|
17
|
+
* This component supports the default PrismJS languages (`markup`, `html`
|
|
18
|
+
* , `xml`, `svg`, `mathml`, `ssml`, `atom`, `rss`, `css`, `clike`, `javascript`
|
|
19
|
+
* , `js`), and specific additional languages (`jsx`, `typescript`, `ts`
|
|
20
|
+
* , `tsx`).
|
|
21
|
+
* Others can be added if there is demand! Please open an issue on GitHub.
|
|
22
|
+
*
|
|
23
|
+
* @param props The props for the component.
|
|
24
|
+
* @returns The Code component.
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <Code
|
|
28
|
+
* code={`
|
|
29
|
+
* import React from 'react';
|
|
30
|
+
* const App = () => (
|
|
31
|
+
* <div>Hello, world!</div>
|
|
32
|
+
* );
|
|
33
|
+
* `}
|
|
34
|
+
* />
|
|
35
|
+
* ```
|
|
36
|
+
* This example shows a simple block of code, defaulting to the `jsx` language.
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* <Code
|
|
40
|
+
* code={`
|
|
41
|
+
* h1 {
|
|
42
|
+
* font-size: 1.5rem;
|
|
43
|
+
* }
|
|
44
|
+
* `}
|
|
45
|
+
* language="css"
|
|
46
|
+
* />
|
|
47
|
+
* ```
|
|
48
|
+
* This example shows the use of the `css` language.
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* <Code
|
|
52
|
+
* code={`
|
|
53
|
+
* const a: number = 1;
|
|
54
|
+
* `}
|
|
55
|
+
* language="typescript"
|
|
56
|
+
* />
|
|
57
|
+
* ```
|
|
58
|
+
* This example shows the use of the `tsx` language.
|
|
59
|
+
* @icon Lucide.SquareCode
|
|
60
|
+
*/
|
|
61
|
+
export const Code = ({
|
|
62
|
+
code,
|
|
63
|
+
language = 'jsx',
|
|
64
|
+
className,
|
|
65
|
+
}: {
|
|
66
|
+
/**
|
|
67
|
+
* The code to display, as a string.
|
|
68
|
+
*/
|
|
69
|
+
readonly code: string;
|
|
70
|
+
/**
|
|
71
|
+
* An optional indication of the language. Defaults to `jsx`.
|
|
72
|
+
*/
|
|
73
|
+
readonly language?: string;
|
|
74
|
+
/**
|
|
75
|
+
* An extra CSS class name for the component.
|
|
76
|
+
*/
|
|
77
|
+
readonly className?: string;
|
|
78
|
+
}) => {
|
|
79
|
+
return (
|
|
80
|
+
<pre className={classNames(pre, className)}>
|
|
81
|
+
<code
|
|
82
|
+
// eslint-disable-next-line react/no-danger
|
|
83
|
+
dangerouslySetInnerHTML={{
|
|
84
|
+
__html: highlight(code.trim(), languages[language], language),
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
</pre>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {colors} from '../../css/colors.css';
|
|
2
|
+
import {dimensions} from '../../css/dimensions.css';
|
|
3
|
+
import {style} from '@vanilla-extract/css';
|
|
4
|
+
|
|
5
|
+
export const collapsible = style({
|
|
6
|
+
width: '100%',
|
|
7
|
+
alignSelf: 'start',
|
|
8
|
+
borderRadius: dimensions.radius,
|
|
9
|
+
boxShadow: colors.shadow,
|
|
10
|
+
border: colors.border,
|
|
11
|
+
display: 'grid',
|
|
12
|
+
gridTemplateRows: 'max-content minmax(0, 0fr)',
|
|
13
|
+
transition: '.2s grid-template-rows ease-in-out',
|
|
14
|
+
overflow: 'hidden',
|
|
15
|
+
marginBottom: dimensions.padding,
|
|
16
|
+
selectors: {
|
|
17
|
+
'&:last-child': {
|
|
18
|
+
marginBottom: 0,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const collapsibleOpen = style({
|
|
24
|
+
gridTemplateRows: 'max-content minmax(0, 1fr)',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const button = style({
|
|
28
|
+
margin: '-1px',
|
|
29
|
+
boxShadow: 'none',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const buttonOpen = style({
|
|
33
|
+
borderBottomLeftRadius: 0,
|
|
34
|
+
borderBottomRightRadius: 0,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const content = style({padding: dimensions.padding, overflow: 'hidden'});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {ChevronDown, ChevronRight} from 'lucide-react';
|
|
2
|
+
import type {ComponentType, ReactNode} from 'react';
|
|
3
|
+
import React, {useCallback, useRef, useState} from 'react';
|
|
4
|
+
import {
|
|
5
|
+
button,
|
|
6
|
+
buttonOpen,
|
|
7
|
+
collapsible,
|
|
8
|
+
collapsibleOpen,
|
|
9
|
+
content,
|
|
10
|
+
} from './index.css.ts';
|
|
11
|
+
import {
|
|
12
|
+
useCollapsibleIsOpen,
|
|
13
|
+
useSetCollapsibleIsOpenCallback,
|
|
14
|
+
} from '../../stores/SessionStore.tsx';
|
|
15
|
+
import {Button} from '../Button/index.tsx';
|
|
16
|
+
import {classNames} from '../../common/functions.tsx';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The `Collapsible` component displays a titled box that can be expanded or
|
|
20
|
+
* collapsed. If the `id` prop is provided, its state is preserved between page
|
|
21
|
+
* reloads.
|
|
22
|
+
*
|
|
23
|
+
* @param props The props for the component.
|
|
24
|
+
* @returns The Collapsible component.
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <Collapsible
|
|
28
|
+
* title="TinyWidgets"
|
|
29
|
+
* >
|
|
30
|
+
* <p>Peekaboo!</p>
|
|
31
|
+
* </Collapsible>
|
|
32
|
+
* ```
|
|
33
|
+
* This example shows the basic Collapsible component.
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* <Collapsible
|
|
37
|
+
* title="TinyWidgets"
|
|
38
|
+
* icon={Lucide.Grid3x3}
|
|
39
|
+
* startOpen={true}
|
|
40
|
+
* >
|
|
41
|
+
* <p>Already open</p>
|
|
42
|
+
* </Collapsible>
|
|
43
|
+
* ```
|
|
44
|
+
* This example shows a Collapsible component with an icon and which starts
|
|
45
|
+
* open.
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* <Collapsible
|
|
49
|
+
* title="TinyWidgets"
|
|
50
|
+
* id="example"
|
|
51
|
+
* >
|
|
52
|
+
* <p>Reload the window</p>
|
|
53
|
+
* </Collapsible>
|
|
54
|
+
* ```
|
|
55
|
+
* This example shows a Collapsible component where its state is preserved
|
|
56
|
+
* between page reloads.
|
|
57
|
+
* @icon Lucide.PanelTopOpen
|
|
58
|
+
*/
|
|
59
|
+
export const Collapsible = ({
|
|
60
|
+
icon: Icon,
|
|
61
|
+
title,
|
|
62
|
+
titleRight,
|
|
63
|
+
startOpen = false,
|
|
64
|
+
id = '',
|
|
65
|
+
className,
|
|
66
|
+
children,
|
|
67
|
+
}: {
|
|
68
|
+
/**
|
|
69
|
+
* An optional component which renders an icon for the top of the collapsible
|
|
70
|
+
* component, and which must accept a className prop.
|
|
71
|
+
*/
|
|
72
|
+
readonly icon?: ComponentType<{className?: string}>;
|
|
73
|
+
/**
|
|
74
|
+
* An optional component, element, or string which renders the title of
|
|
75
|
+
* the top of the component.
|
|
76
|
+
*/
|
|
77
|
+
readonly title?: ComponentType | ReactNode;
|
|
78
|
+
/**
|
|
79
|
+
* An optional component, element, or string which renders a second title
|
|
80
|
+
* on the right side of the top of the component.
|
|
81
|
+
*/
|
|
82
|
+
readonly titleRight?: ComponentType | ReactNode;
|
|
83
|
+
/**
|
|
84
|
+
* Whether the section should start opened up.
|
|
85
|
+
*/
|
|
86
|
+
readonly startOpen?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* An Id which will allow the state to be preserved between page reloads.
|
|
89
|
+
*/
|
|
90
|
+
readonly id?: string;
|
|
91
|
+
/**
|
|
92
|
+
* An extra CSS class name for the component.
|
|
93
|
+
*/
|
|
94
|
+
readonly className?: string;
|
|
95
|
+
/**
|
|
96
|
+
* The children of the component, that go inside the collapsible section.
|
|
97
|
+
*/
|
|
98
|
+
readonly children: ReactNode;
|
|
99
|
+
}) => {
|
|
100
|
+
const storedIsOpen = useCollapsibleIsOpen(id) ?? startOpen;
|
|
101
|
+
const setStoredIsOpen = useSetCollapsibleIsOpenCallback(id);
|
|
102
|
+
const [stateIsOpen, setStateIsOpen] = useState(startOpen);
|
|
103
|
+
|
|
104
|
+
const isOpen = id ? storedIsOpen : stateIsOpen;
|
|
105
|
+
const setIsOpen = id ? setStoredIsOpen : setStateIsOpen;
|
|
106
|
+
|
|
107
|
+
const [render, setRender] = useState(isOpen);
|
|
108
|
+
const timer = useRef<ReturnType<typeof setTimeout>>();
|
|
109
|
+
|
|
110
|
+
const toggle = useCallback(() => {
|
|
111
|
+
setIsOpen(!isOpen);
|
|
112
|
+
clearTimeout(timer.current);
|
|
113
|
+
timer.current = setTimeout(() => setRender(!isOpen), isOpen ? 200 : 0);
|
|
114
|
+
}, [setIsOpen, isOpen]);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<div className={classNames(collapsible, isOpen && collapsibleOpen)}>
|
|
118
|
+
<Button
|
|
119
|
+
onClick={toggle}
|
|
120
|
+
icon={Icon}
|
|
121
|
+
title={title}
|
|
122
|
+
titleRight={titleRight}
|
|
123
|
+
iconRight={isOpen ? ChevronDown : ChevronRight}
|
|
124
|
+
className={classNames(button, render && buttonOpen)}
|
|
125
|
+
current={render}
|
|
126
|
+
/>
|
|
127
|
+
{render ? (
|
|
128
|
+
<div className={classNames(content, className)}>{children}</div>
|
|
129
|
+
) : null}
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import {colors} from '../../css/colors.css';
|
|
1
2
|
import {style} from '@vanilla-extract/css';
|
|
2
|
-
import {theme} from '../index.css';
|
|
3
3
|
|
|
4
4
|
export const detailTable = style({
|
|
5
5
|
width: '100%',
|
|
@@ -8,12 +8,12 @@ export const detailTable = style({
|
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
export const detailRow = style({
|
|
11
|
-
borderBottom:
|
|
11
|
+
borderBottom: colors.border,
|
|
12
12
|
selectors: {'&:last-child': {borderBottom: 'none'}},
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
export const detailCell = style({
|
|
16
16
|
padding: '0.5rem 1rem',
|
|
17
17
|
verticalAlign: 'top',
|
|
18
|
-
selectors: {'&:is(th)': {textAlign: 'right'}},
|
|
18
|
+
selectors: {'&:is(th)': {textAlign: 'right', width: '30%'}},
|
|
19
19
|
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {detailCell, detailRow, detailTable} from './index.css';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type {ReactNode} from 'react';
|
|
4
|
+
import {classNames} from '../../common/functions';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The `Detail` component displays a set of key-value pairs in a two-column
|
|
8
|
+
* table.
|
|
9
|
+
*
|
|
10
|
+
* @param props The props for the component.
|
|
11
|
+
* @returns The Detail component.
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <Detail
|
|
15
|
+
* data={{
|
|
16
|
+
* normal: 'lorem ipsum',
|
|
17
|
+
* italic: <i>lorem ipsum</i>,
|
|
18
|
+
* underline: <u>lorem ipsum</u>,
|
|
19
|
+
* }}
|
|
20
|
+
* />
|
|
21
|
+
* ```
|
|
22
|
+
* This example shows the basic Collapsible component.
|
|
23
|
+
* @icon Lucide.Table
|
|
24
|
+
*/
|
|
25
|
+
export const Detail = ({
|
|
26
|
+
data,
|
|
27
|
+
className,
|
|
28
|
+
}: {
|
|
29
|
+
/**
|
|
30
|
+
* The data to display in the detail table. The values can be any React node.
|
|
31
|
+
*/
|
|
32
|
+
readonly data: Record<string, ReactNode>;
|
|
33
|
+
/**
|
|
34
|
+
* An extra CSS class name for the component.
|
|
35
|
+
*/
|
|
36
|
+
readonly className?: string;
|
|
37
|
+
}) => (
|
|
38
|
+
<table className={classNames(detailTable, className)}>
|
|
39
|
+
<tbody>
|
|
40
|
+
{Object.entries(data).map(([key, value]) => (
|
|
41
|
+
<tr key={key} className={detailRow}>
|
|
42
|
+
<th className={detailCell}>{key}</th>
|
|
43
|
+
<td className={detailCell}>{value}</td>
|
|
44
|
+
</tr>
|
|
45
|
+
))}
|
|
46
|
+
</tbody>
|
|
47
|
+
</table>
|
|
48
|
+
);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import {colors} from '../../css/colors.css';
|
|
2
|
+
import {dimensions} from '../../css/dimensions.css';
|
|
1
3
|
import {style} from '@vanilla-extract/css';
|
|
2
|
-
import {dimensions, theme} from '../index.css';
|
|
3
4
|
|
|
4
5
|
export const hr = style({
|
|
5
6
|
border: 'none',
|
|
6
|
-
borderBottom:
|
|
7
|
+
borderBottom: colors.border,
|
|
7
8
|
margin: `${dimensions.padding} 0`,
|
|
8
9
|
height: '1px',
|
|
9
10
|
width: '100%',
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {classNames} from '../../common/functions';
|
|
3
|
+
import {hr} from './index.css';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The `Hr` component displays a styled horizontal rule.
|
|
7
|
+
*
|
|
8
|
+
* @param props The props for the component.
|
|
9
|
+
* @returns The Hr component.
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <Hr />
|
|
13
|
+
* ```
|
|
14
|
+
* This example shows the basic Hr component.
|
|
15
|
+
* @icon Lucide.Minus
|
|
16
|
+
*/
|
|
17
|
+
export const Hr = ({
|
|
18
|
+
className,
|
|
19
|
+
}: {
|
|
20
|
+
/**
|
|
21
|
+
* An extra CSS class name for the component.
|
|
22
|
+
*/
|
|
23
|
+
readonly className?: string;
|
|
24
|
+
}) => {
|
|
25
|
+
return <hr className={classNames(hr, className)} />;
|
|
26
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {style, styleVariants} from '@vanilla-extract/css';
|
|
2
|
+
import {colors} from '../../css/colors.css';
|
|
3
|
+
import {dimensions} from '../../css/dimensions.css';
|
|
4
|
+
|
|
5
|
+
export const image = style({
|
|
6
|
+
display: 'inline-block',
|
|
7
|
+
flexShrink: 0,
|
|
8
|
+
width: '100%',
|
|
9
|
+
maxHeight: 'inherit',
|
|
10
|
+
selectors: {
|
|
11
|
+
'&:hover': {
|
|
12
|
+
backgroundColor: colors.backgroundHover,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const imageVariants = styleVariants({
|
|
18
|
+
default: {
|
|
19
|
+
borderRadius: dimensions.radius,
|
|
20
|
+
boxShadow: colors.shadow,
|
|
21
|
+
},
|
|
22
|
+
logo: {
|
|
23
|
+
width: dimensions.logo,
|
|
24
|
+
height: dimensions.logo,
|
|
25
|
+
},
|
|
26
|
+
avatar: {
|
|
27
|
+
borderRadius: '50%',
|
|
28
|
+
border: colors.border,
|
|
29
|
+
width: dimensions.avatar,
|
|
30
|
+
height: dimensions.avatar,
|
|
31
|
+
boxShadow: colors.shadow,
|
|
32
|
+
},
|
|
33
|
+
icon: {
|
|
34
|
+
border: colors.border,
|
|
35
|
+
width: dimensions.icon,
|
|
36
|
+
height: dimensions.icon,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {image, imageVariants} from './index.css.ts';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import {classNames} from '../../common/functions.tsx';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The `Image` component displays an image, with a number of common variants.
|
|
7
|
+
*
|
|
8
|
+
* @param props The props for the component.
|
|
9
|
+
* @returns The Image component.
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <Image
|
|
13
|
+
* src="/favicon.svg"
|
|
14
|
+
* onClick={() => alert('Clicked!')}
|
|
15
|
+
* alt="TinyWidgets"
|
|
16
|
+
* />
|
|
17
|
+
* ```
|
|
18
|
+
* This example shows the `default` variant of the Image component.
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <Image
|
|
22
|
+
* src="/favicon.svg"
|
|
23
|
+
* variant="logo"
|
|
24
|
+
* />
|
|
25
|
+
* ```
|
|
26
|
+
* This example shows the `logo` variant of the Image component.
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <Image
|
|
30
|
+
* src="/favicon.svg"
|
|
31
|
+
* variant="avatar"
|
|
32
|
+
* />
|
|
33
|
+
* ```
|
|
34
|
+
* This example shows the `avatar` variant of the Image component.
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* <Image
|
|
38
|
+
* src="/favicon.svg"
|
|
39
|
+
* variant="icon"
|
|
40
|
+
* />
|
|
41
|
+
* ```
|
|
42
|
+
* This example shows the `icon` variant of the Image component.
|
|
43
|
+
* @icon Lucide.Image
|
|
44
|
+
*/
|
|
45
|
+
export const Image = ({
|
|
46
|
+
src,
|
|
47
|
+
onClick,
|
|
48
|
+
variant = 'default',
|
|
49
|
+
alt,
|
|
50
|
+
className,
|
|
51
|
+
}: {
|
|
52
|
+
/**
|
|
53
|
+
* The source of the image.
|
|
54
|
+
*/
|
|
55
|
+
readonly src: string;
|
|
56
|
+
/**
|
|
57
|
+
* A handler called when the user clicks on the image.
|
|
58
|
+
*/
|
|
59
|
+
readonly onClick?: () => void;
|
|
60
|
+
/**
|
|
61
|
+
* A variant of the image, one of:
|
|
62
|
+
* - `default`
|
|
63
|
+
* - `logo`
|
|
64
|
+
* - `avatar`
|
|
65
|
+
* - `icon`
|
|
66
|
+
*/
|
|
67
|
+
readonly variant?: keyof typeof imageVariants;
|
|
68
|
+
/**
|
|
69
|
+
* Alternative text shown when the user hovers over the image.
|
|
70
|
+
*/
|
|
71
|
+
readonly alt?: string;
|
|
72
|
+
/**
|
|
73
|
+
* An extra CSS class name for the component.
|
|
74
|
+
*/
|
|
75
|
+
readonly className?: string;
|
|
76
|
+
}) => {
|
|
77
|
+
return (
|
|
78
|
+
<img
|
|
79
|
+
src={src}
|
|
80
|
+
onClick={onClick}
|
|
81
|
+
title={alt}
|
|
82
|
+
className={classNames(image, imageVariants[variant], className)}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type {ComponentType, ReactNode} from 'react';
|
|
2
|
+
import {classNames, renderComponentOrNode} from '../../common/functions';
|
|
3
|
+
import {metric, metricLabel, metricNumber} from './index.css';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import {iconSize} from '../../css/dimensions.css';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The `Metric` component displays a metric as a prominent numerical value with
|
|
9
|
+
* a label above.
|
|
10
|
+
*
|
|
11
|
+
* @param props The props for the component.
|
|
12
|
+
* @returns The Metric component.
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <Metric
|
|
16
|
+
* icon={Lucide.ChartLine}
|
|
17
|
+
* title="Number"
|
|
18
|
+
* number="57"
|
|
19
|
+
* />
|
|
20
|
+
* ```
|
|
21
|
+
* This example shows the basic usage of the Metric component.
|
|
22
|
+
* @icon Lucide.SquareSigma
|
|
23
|
+
*/
|
|
24
|
+
export const Metric = ({
|
|
25
|
+
icon: Icon,
|
|
26
|
+
title: titleComponentOrNode,
|
|
27
|
+
number: numberComponentOrNode,
|
|
28
|
+
className,
|
|
29
|
+
}: {
|
|
30
|
+
/**
|
|
31
|
+
* An optional component which renders an icon for the metric panel, and which
|
|
32
|
+
* must accept a className prop.
|
|
33
|
+
*/
|
|
34
|
+
readonly icon?: ComponentType<{className?: string}>;
|
|
35
|
+
/**
|
|
36
|
+
* An optional component, element, or string which renders the title of
|
|
37
|
+
* the metric panel.
|
|
38
|
+
*/
|
|
39
|
+
readonly title?: ComponentType | ReactNode;
|
|
40
|
+
/**
|
|
41
|
+
* An optional component, element, or string which renders the number of
|
|
42
|
+
* the metric panel.
|
|
43
|
+
*/
|
|
44
|
+
readonly number?: ComponentType | ReactNode;
|
|
45
|
+
/**
|
|
46
|
+
* An extra CSS class name for the component.
|
|
47
|
+
*/
|
|
48
|
+
readonly className?: string;
|
|
49
|
+
}) => (
|
|
50
|
+
<div className={classNames(metric, className)}>
|
|
51
|
+
<div className={metricLabel}>
|
|
52
|
+
{Icon ? <Icon className={iconSize} /> : null}
|
|
53
|
+
{renderComponentOrNode(titleComponentOrNode)}
|
|
54
|
+
</div>
|
|
55
|
+
<div className={metricNumber}>
|
|
56
|
+
{renderComponentOrNode(numberComponentOrNode)}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {style, styleVariants} from '@vanilla-extract/css';
|
|
2
|
+
import {dimensions} from '../../css/dimensions.css.ts';
|
|
3
|
+
import {notLarge} from '../../common/functions.tsx';
|
|
4
|
+
|
|
5
|
+
export const row = style({
|
|
6
|
+
display: 'grid',
|
|
7
|
+
width: '100%',
|
|
8
|
+
gap: dimensions.padding,
|
|
9
|
+
...notLarge({gridTemplateColumns: '1fr'}),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export const rowVariants = styleVariants({
|
|
13
|
+
'1|1': {gridTemplateColumns: `calc((100% - ${dimensions.padding})/2) 1fr`},
|
|
14
|
+
'1|2': {
|
|
15
|
+
gridTemplateColumns: `calc((100% - ${dimensions.padding}*2)/3) 1fr`,
|
|
16
|
+
},
|
|
17
|
+
'2|1': {
|
|
18
|
+
gridTemplateColumns: `1fr calc((100% - ${dimensions.padding}*2)/3)`,
|
|
19
|
+
},
|
|
20
|
+
'1|1|1': {
|
|
21
|
+
gridTemplateColumns: `1fr 1fr 1fr`,
|
|
22
|
+
},
|
|
23
|
+
'1|3': {
|
|
24
|
+
gridTemplateColumns: `1fr calc((100% - ${dimensions.padding}*3)/4)`,
|
|
25
|
+
},
|
|
26
|
+
'3|1': {
|
|
27
|
+
gridTemplateColumns: `calc((100% - ${dimensions.padding}*3)/4) 1fr`,
|
|
28
|
+
},
|
|
29
|
+
'1|1|1|1': {gridTemplateColumns: '1fr 1fr 1fr 1fr'},
|
|
30
|
+
});
|