This commit is contained in:
seb
2026-08-08 16:58:48 +02:00
commit 253d0f95ee
27 changed files with 4083 additions and 0 deletions

128
src/tiles/Tile.jsx Normal file
View File

@@ -0,0 +1,128 @@
import { Component } from 'react';
import ButtonBase from '@mui/material/ButtonBase';
import Typography from '@mui/material/Typography';
/**
* Square side length (px) used as the basis for the tile grid in consuming
* apps.
*
* @component TILE_SIZE
* @import dashgui/tiles
*
* @behaviour Constant (140) exported for building grids: `repeat(auto-fill, minmax(TILE_SIZEpx, 1fr))`.
*/
export const TILE_SIZE = 140;
/** Matches panel tile rounding (theme.shape.borderRadius × 4 → typically 16px). */
export const TILE_BORDER_RADIUS = 4;
/**
* Shared tile styling, exported for reuse — lets consumers render a tile-like
* box without extending the Tile class (see the blue box in the gallery).
*
* @component tileBaseSx
* @import dashgui/tiles
*
* @behaviour Sets a square aspect ratio, centered flex column layout, rounded corners, background.paper fill, shadow 3, and hover states (background hover + shadow 6).
* @behaviour Not tied to a click handler — add your own onClick when used directly. Spread additional styles after it to override (e.g. bgcolor, color).
*/
export const tileBaseSx = {
aspectRatio: '1 / 1',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: 1,
p: 1.5,
cursor: 'pointer',
userSelect: 'none',
textAlign: 'center',
border: 'none',
borderRadius: TILE_BORDER_RADIUS,
overflow: 'hidden',
bgcolor: 'background.paper',
boxShadow: 3,
transition: 'box-shadow 0.15s, background-color 0.15s',
'&:hover': {
bgcolor: 'action.hover',
boxShadow: 6,
},
};
/**
* Base square tile (ButtonBase) meant to be subclassed — subclasses override
* the render hooks (e.g. `renderIcon`) and pass label/secondary text via props.
* Renders the shared `tileBaseSx` styling.
*
* @component Tile
* @import dashgui/tiles
*
* @prop {ReactNode} [label] - Primary text shown under the icon.
* @prop {ReactNode} [secondary] - Small secondary caption shown under the label.
* @prop {() => void} [onClick] - Called when the tile is clicked (disabled tiles are inert).
* @prop {boolean} [disabled=false] - Fades the tile to 50% opacity and disables pointer events.
* @prop {object} [sx] - Extra styles merged on top of `tileBaseSx` (win over it).
*
* @behaviour Renders inside a `ButtonBase` with `type="button"` and the shared `tileBaseSx` square look (aspect-ratio 1:1, rounded, shadow, hover feedback).
* @behaviour Subclasses override `renderIcon()`, `renderLabel()`, `renderSecondary()`, or `renderExtra()` to compose the tile body.
* @behaviour `TILE_SIZE` (140) is the basis for sizing the containing grid in the consuming app.
*/
export class Tile extends Component {
handleClick = () => {
this.props.onClick?.();
};
renderIcon() {
return null;
}
renderLabel() {
const { label } = this.props;
if (!label) return null;
return (
<Typography variant="body2" fontWeight={600} sx={{ lineHeight: 1.2 }}>
{label}
</Typography>
);
}
renderSecondary() {
const { secondary } = this.props;
if (!secondary) return null;
return (
<Typography variant="caption" color="text.secondary" sx={{ lineHeight: 1.2 }}>
{secondary}
</Typography>
);
}
renderExtra() {
return null;
}
render() {
const { sx, disabled } = this.props;
return (
<ButtonBase
component="button"
type="button"
disabled={disabled}
onClick={this.handleClick}
sx={{
...tileBaseSx,
position: 'relative',
...(disabled
? { opacity: 0.5, pointerEvents: 'none' }
: {}),
...sx,
}}
>
{this.renderIcon()}
{this.renderLabel()}
{this.renderSecondary()}
{this.renderExtra()}
</ButtonBase>
);
}
}

1
src/tiles/index.js Normal file
View File

@@ -0,0 +1 @@
export { TILE_SIZE, Tile, tileBaseSx } from './Tile';