tinywidgets 1.3.11 → 1.4.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/package.json +3 -3
- package/src/common/functions.tsx +18 -7
- package/src/components/App/index.css.ts +0 -9
- package/src/components/App/index.tsx +7 -6
- package/src/components/Axis/index.css.ts +13 -0
- package/src/components/Axis/index.tsx +82 -0
- package/src/components/ImageLabel/index.css.ts +5 -0
- package/src/components/ImageLabel/index.tsx +72 -0
- package/src/components/Metric/index.css.ts +0 -2
- package/src/components/Metric/index.tsx +3 -2
- package/src/components/Tag/index.css.ts +0 -2
- package/src/components/Tag/index.tsx +3 -2
- package/src/index.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tinywidgets",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"author": "jamesgpearce",
|
|
5
5
|
"repository": "github:tinyplex/tinywidgets",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"./css": "./src/index.css.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@vanilla-extract/css": "^1.
|
|
41
|
+
"@vanilla-extract/css": "^1.20.0",
|
|
42
42
|
"lucide-react": "^0.577.0",
|
|
43
43
|
"react": "^19.2.4",
|
|
44
44
|
"react-dom": "^19.2.4",
|
|
45
|
-
"tinybase": "^8.0.
|
|
45
|
+
"tinybase": "^8.0.2"
|
|
46
46
|
}
|
|
47
47
|
}
|
package/src/common/functions.tsx
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type {StyleRule} from '@vanilla-extract/css';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
createElement,
|
|
4
|
+
isValidElement,
|
|
5
|
+
type ComponentType,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
} from 'react';
|
|
3
8
|
import {screens} from '../css/screens';
|
|
4
9
|
|
|
5
10
|
/**
|
|
@@ -30,12 +35,18 @@ export const classNames = (
|
|
|
30
35
|
export const renderComponentOrNode = (
|
|
31
36
|
ComponentOrNode: ComponentType | ReactNode,
|
|
32
37
|
fallback: ReactNode = null,
|
|
33
|
-
) =>
|
|
34
|
-
ComponentOrNode
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
): ReactNode => {
|
|
39
|
+
if (ComponentOrNode == null) {
|
|
40
|
+
return fallback;
|
|
41
|
+
}
|
|
42
|
+
if (isValidElement(ComponentOrNode)) {
|
|
43
|
+
return ComponentOrNode;
|
|
44
|
+
}
|
|
45
|
+
if (['string', 'number', 'boolean'].includes(typeof ComponentOrNode)) {
|
|
46
|
+
return ComponentOrNode as string | number | boolean;
|
|
47
|
+
}
|
|
48
|
+
return createElement(ComponentOrNode as ComponentType);
|
|
49
|
+
};
|
|
39
50
|
|
|
40
51
|
export const large = (style: StyleRule) => ({
|
|
41
52
|
'@media': {[`screen and (min-width: ${screens.large}px)`]: style},
|
|
@@ -12,9 +12,7 @@ export const appLayout = style({
|
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
export const header = style({
|
|
15
|
-
display: 'flex',
|
|
16
15
|
justifyContent: 'space-between',
|
|
17
|
-
alignItems: 'center',
|
|
18
16
|
gap: dimensions.padding,
|
|
19
17
|
padding: dimensions.padding,
|
|
20
18
|
position: 'fixed',
|
|
@@ -29,9 +27,7 @@ export const header = style({
|
|
|
29
27
|
});
|
|
30
28
|
|
|
31
29
|
export const topNav = style({
|
|
32
|
-
display: 'flex',
|
|
33
30
|
justifyContent: 'space-between',
|
|
34
|
-
alignItems: 'center',
|
|
35
31
|
gap: dimensions.padding,
|
|
36
32
|
flex: 1,
|
|
37
33
|
});
|
|
@@ -39,9 +35,6 @@ export const topNav = style({
|
|
|
39
35
|
export const sideNavButton = style(large({display: 'none!important'}));
|
|
40
36
|
|
|
41
37
|
export const title = style({
|
|
42
|
-
display: 'flex',
|
|
43
|
-
alignItems: 'center',
|
|
44
|
-
gap: dimensions.padding,
|
|
45
38
|
...large({
|
|
46
39
|
width: `calc(${dimensions.sideNavWidth} - 2 * ${dimensions.padding})`,
|
|
47
40
|
}),
|
|
@@ -84,9 +77,7 @@ export const mainHasFooter = style({
|
|
|
84
77
|
});
|
|
85
78
|
|
|
86
79
|
export const footer = style({
|
|
87
|
-
display: 'flex',
|
|
88
80
|
justifyContent: 'right',
|
|
89
|
-
alignItems: 'center',
|
|
90
81
|
gap: dimensions.padding,
|
|
91
82
|
paddingLeft: dimensions.padding,
|
|
92
83
|
paddingRight: dimensions.padding,
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
useSideNavIsOpen,
|
|
26
26
|
useToggleSideNavIsOpenCallback,
|
|
27
27
|
} from '../../stores/SessionStore.tsx';
|
|
28
|
+
import {Axis} from '../Axis/index.tsx';
|
|
28
29
|
import {Button} from '../Button/index.tsx';
|
|
29
30
|
import {
|
|
30
31
|
app,
|
|
@@ -165,7 +166,7 @@ const Layout = ({
|
|
|
165
166
|
<LayoutContext.Provider value={{portal: ref.current}}>
|
|
166
167
|
{sessionStoreIsReady && routeStoreIsReady && localStoreIsReady ? (
|
|
167
168
|
<>
|
|
168
|
-
<header className={header}>
|
|
169
|
+
<Axis as="header" className={header}>
|
|
169
170
|
{hasSideNav ? (
|
|
170
171
|
<Button
|
|
171
172
|
variant="icon"
|
|
@@ -177,10 +178,10 @@ const Layout = ({
|
|
|
177
178
|
<nav className={title}>
|
|
178
179
|
{renderComponentOrNode(titleComponentOrNode)}
|
|
179
180
|
</nav>
|
|
180
|
-
<nav className={topNav}>
|
|
181
|
+
<Axis as="nav" className={topNav}>
|
|
181
182
|
{renderComponentOrNode(topNavLeftComponentOrNode, <div />)}
|
|
182
183
|
{renderComponentOrNode(topNavRightComponentOrNode, <div />)}
|
|
183
|
-
</
|
|
184
|
+
</Axis>
|
|
184
185
|
<Button
|
|
185
186
|
variant="icon"
|
|
186
187
|
onClick={toggleDarkChoice}
|
|
@@ -197,7 +198,7 @@ const Layout = ({
|
|
|
197
198
|
{renderComponentOrNode(sideNavComponentOrNode)}
|
|
198
199
|
</nav>
|
|
199
200
|
) : null}
|
|
200
|
-
</
|
|
201
|
+
</Axis>
|
|
201
202
|
<main
|
|
202
203
|
className={classNames(
|
|
203
204
|
main,
|
|
@@ -208,9 +209,9 @@ const Layout = ({
|
|
|
208
209
|
{renderComponentOrNode(mainComponentOrNode)}
|
|
209
210
|
</main>
|
|
210
211
|
{hasFooter ? (
|
|
211
|
-
<footer className={footer}>
|
|
212
|
+
<Axis as="footer" className={footer}>
|
|
212
213
|
{renderComponentOrNode(footerComponentOrNode)}
|
|
213
|
-
</
|
|
214
|
+
</Axis>
|
|
214
215
|
) : null}
|
|
215
216
|
</>
|
|
216
217
|
) : null}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {style, styleVariants} from '@vanilla-extract/css';
|
|
2
|
+
|
|
3
|
+
export const axis = style({
|
|
4
|
+
display: 'flex',
|
|
5
|
+
alignItems: 'center',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const axisVariants = styleVariants({
|
|
9
|
+
horizontal: {},
|
|
10
|
+
vertical: {
|
|
11
|
+
flexDirection: 'column',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type {MouseEventHandler, ReactNode} from 'react';
|
|
2
|
+
import {classNames} from '../../common/functions';
|
|
3
|
+
import {axis, axisVariants} from './index.css';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The `Axis` component displays its children along a flex axis, aligning them
|
|
7
|
+
* in the center of the cross-axis.
|
|
8
|
+
*
|
|
9
|
+
* This is useful for compact layouts where icons, avatars, images, and text
|
|
10
|
+
* should share a common visual center line, or for stacked layouts that should
|
|
11
|
+
* remain horizontally centered.
|
|
12
|
+
*
|
|
13
|
+
* @param props The props for the component.
|
|
14
|
+
* @returns The Axis component.
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <Axis>
|
|
18
|
+
* <Image src="/favicon.svg" variant="logo" />
|
|
19
|
+
* TinyWidgets
|
|
20
|
+
* </Axis>
|
|
21
|
+
* ```
|
|
22
|
+
* This example shows a logo image and text aligned along the same
|
|
23
|
+
* horizontal axis.
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <Axis variant="vertical">
|
|
27
|
+
* <Image src="/favicon.svg" variant="logo" />
|
|
28
|
+
* TinyWidgets
|
|
29
|
+
* </Axis>
|
|
30
|
+
* ```
|
|
31
|
+
* This example shows the same content arranged on a vertical axis.
|
|
32
|
+
* @icon Lucide.AlignCenterHorizontal
|
|
33
|
+
*/
|
|
34
|
+
export const Axis = ({
|
|
35
|
+
as: Component = 'div',
|
|
36
|
+
variant = 'horizontal',
|
|
37
|
+
className,
|
|
38
|
+
title,
|
|
39
|
+
onClick,
|
|
40
|
+
children,
|
|
41
|
+
}: {
|
|
42
|
+
/**
|
|
43
|
+
* The HTML element used to wrap the axis, one of:
|
|
44
|
+
* - `div`
|
|
45
|
+
* - `nav`
|
|
46
|
+
* - `span`
|
|
47
|
+
* - `h1`
|
|
48
|
+
* - `header`
|
|
49
|
+
* - `footer`
|
|
50
|
+
*/
|
|
51
|
+
readonly as?: 'div' | 'nav' | 'span' | 'h1' | 'header' | 'footer';
|
|
52
|
+
/**
|
|
53
|
+
* A variant of the axis, one of:
|
|
54
|
+
* - `horizontal`
|
|
55
|
+
* - `vertical`
|
|
56
|
+
*/
|
|
57
|
+
readonly variant?: keyof typeof axisVariants;
|
|
58
|
+
/**
|
|
59
|
+
* An extra CSS class name for the component.
|
|
60
|
+
*/
|
|
61
|
+
readonly className?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Alternative text shown when the user hovers over the component.
|
|
64
|
+
*/
|
|
65
|
+
readonly title?: string;
|
|
66
|
+
/**
|
|
67
|
+
* A handler called when the user clicks on the component.
|
|
68
|
+
*/
|
|
69
|
+
readonly onClick?: MouseEventHandler<HTMLElement>;
|
|
70
|
+
/**
|
|
71
|
+
* The children of the component, arranged along the selected axis.
|
|
72
|
+
*/
|
|
73
|
+
readonly children: ReactNode;
|
|
74
|
+
}) => (
|
|
75
|
+
<Component
|
|
76
|
+
className={classNames(axis, axisVariants[variant], className)}
|
|
77
|
+
title={title}
|
|
78
|
+
onClick={onClick}
|
|
79
|
+
>
|
|
80
|
+
{children}
|
|
81
|
+
</Component>
|
|
82
|
+
);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type {ComponentType, MouseEventHandler, ReactNode} from 'react';
|
|
2
|
+
import {classNames, renderComponentOrNode} from '../../common/functions';
|
|
3
|
+
import {Axis} from '../Axis';
|
|
4
|
+
import {imageLabel} from './index.css';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The `ImageLabel` component displays an image and a text label along a shared
|
|
8
|
+
* horizontal axis.
|
|
9
|
+
*
|
|
10
|
+
* It is useful for common UI patterns such as brand marks, avatars with names,
|
|
11
|
+
* or any compact media-and-label combination.
|
|
12
|
+
*
|
|
13
|
+
* @param props The props for the component.
|
|
14
|
+
* @returns The ImageLabel component.
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <ImageLabel
|
|
18
|
+
* image={<Image src="/favicon.svg" variant="avatar" />}
|
|
19
|
+
* text="TinyWidgets"
|
|
20
|
+
* />
|
|
21
|
+
* ```
|
|
22
|
+
* This example shows a logo image with a text label.
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* <ImageLabel
|
|
26
|
+
* as="h1"
|
|
27
|
+
* image={<Image src="/favicon.svg" variant="logo"/>}
|
|
28
|
+
* text={<b>TinyWidgets</b>}
|
|
29
|
+
* />
|
|
30
|
+
* ```
|
|
31
|
+
* This example shows the component used as a heading with rich text content.
|
|
32
|
+
* @icon Lucide.Captions
|
|
33
|
+
*/
|
|
34
|
+
export const ImageLabel = ({
|
|
35
|
+
as = 'div',
|
|
36
|
+
image,
|
|
37
|
+
text,
|
|
38
|
+
className,
|
|
39
|
+
onClick,
|
|
40
|
+
}: {
|
|
41
|
+
/**
|
|
42
|
+
* The HTML element used to wrap the image label, one of:
|
|
43
|
+
* - `div`
|
|
44
|
+
* - `nav`
|
|
45
|
+
* - `span`
|
|
46
|
+
* - `h1`
|
|
47
|
+
* - `header`
|
|
48
|
+
* - `footer`
|
|
49
|
+
*/
|
|
50
|
+
readonly as?: 'div' | 'nav' | 'span' | 'h1' | 'header' | 'footer';
|
|
51
|
+
/**
|
|
52
|
+
* A component or element which renders the image for the label.
|
|
53
|
+
*/
|
|
54
|
+
readonly image: ComponentType | ReactNode;
|
|
55
|
+
/**
|
|
56
|
+
* A component, element, or string which renders the label text.
|
|
57
|
+
*/
|
|
58
|
+
readonly text: ComponentType | ReactNode;
|
|
59
|
+
/**
|
|
60
|
+
* An extra CSS class name for the component.
|
|
61
|
+
*/
|
|
62
|
+
readonly className?: string;
|
|
63
|
+
/**
|
|
64
|
+
* A handler called when the user clicks on the component.
|
|
65
|
+
*/
|
|
66
|
+
readonly onClick?: MouseEventHandler<HTMLElement>;
|
|
67
|
+
}) => (
|
|
68
|
+
<Axis as={as} className={classNames(imageLabel, className)} onClick={onClick}>
|
|
69
|
+
{renderComponentOrNode(image)}
|
|
70
|
+
{renderComponentOrNode(text)}
|
|
71
|
+
</Axis>
|
|
72
|
+
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {ComponentType, ReactNode} from 'react';
|
|
2
2
|
import {classNames, renderComponentOrNode} from '../../common/functions';
|
|
3
3
|
import {iconSize} from '../../css/dimensions.css';
|
|
4
|
+
import {Axis} from '../Axis';
|
|
4
5
|
import {metric, metricLabel, metricNumber} from './index.css';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -47,10 +48,10 @@ export const Metric = ({
|
|
|
47
48
|
readonly className?: string;
|
|
48
49
|
}) => (
|
|
49
50
|
<div className={classNames(metric, className)}>
|
|
50
|
-
<
|
|
51
|
+
<Axis className={metricLabel}>
|
|
51
52
|
{Icon ? <Icon className={iconSize} /> : null}
|
|
52
53
|
{renderComponentOrNode(titleComponentOrNode)}
|
|
53
|
-
</
|
|
54
|
+
</Axis>
|
|
54
55
|
<div className={metricNumber}>
|
|
55
56
|
{renderComponentOrNode(numberComponentOrNode)}
|
|
56
57
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {ComponentType, ReactNode} from 'react';
|
|
2
2
|
import {classNames, renderComponentOrNode} from '../../common/functions';
|
|
3
|
+
import {Axis} from '../Axis';
|
|
3
4
|
import {tag, tagIcon, tagVariants} from './index.css';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -61,12 +62,12 @@ export const Tag = ({
|
|
|
61
62
|
}) => {
|
|
62
63
|
const icon = Icon ? <Icon className={tagIcon} /> : null;
|
|
63
64
|
return (
|
|
64
|
-
<
|
|
65
|
+
<Axis
|
|
65
66
|
className={classNames(tag, tagVariants[variant], className)}
|
|
66
67
|
title={alt}
|
|
67
68
|
>
|
|
68
69
|
{icon}
|
|
69
70
|
{renderComponentOrNode(title)}
|
|
70
|
-
</
|
|
71
|
+
</Axis>
|
|
71
72
|
);
|
|
72
73
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export {App} from './components/App/index.tsx';
|
|
2
|
+
export {Axis} from './components/Axis/index.tsx';
|
|
2
3
|
export {Button} from './components/Button/index.tsx';
|
|
3
4
|
export {Card} from './components/Card/index.tsx';
|
|
4
5
|
export {Checkbox} from './components/Checkbox/index.tsx';
|
|
@@ -8,6 +9,7 @@ export {Detail} from './components/Detail/index.tsx';
|
|
|
8
9
|
export {Flyout} from './components/Flyout/index.tsx';
|
|
9
10
|
export {Hr} from './components/Hr/index.tsx';
|
|
10
11
|
export {Image} from './components/Image/index.tsx';
|
|
12
|
+
export {ImageLabel} from './components/ImageLabel/index.tsx';
|
|
11
13
|
export {Metric} from './components/Metric/index.tsx';
|
|
12
14
|
export {Row} from './components/Row/index.tsx';
|
|
13
15
|
export {Select} from './components/Select/index.tsx';
|