Skip to content

Commit

Permalink
🎨 Stable data attributes for theming (#163)
Browse files Browse the repository at this point in the history
* Added data attributes for stable themeability

* Added new styling docs
  • Loading branch information
Christopher Patty authored Apr 13, 2022
1 parent 3bc5a56 commit dbec4b1
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 23 deletions.
56 changes: 55 additions & 1 deletion docs/docs/theming.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,58 @@
title: Theming
---

Any quality library which provides a user interface for end-users must be theme-able to match the rest of the application. Flume is no exception to this, and work is underway to provide a stable API for creating node editor themes. Because of the diverse methods that developers use to style their applications, we are working to make sure when this API is released it will be robust and universal. If you are interested in contributing to this feature we would love to hear from you on the [Github issue for this topic](https://github.com/chrisjpatty/flume/issues/18).
All required styles are automatically included with the `NodeEditor`, but each Flume component has a data attribute that you can target using normal css.

## Example

For example, you can change the background color of the nodes with the following CSS:

```css
[data-flume-component="node"]{
background: #ffffff;
}
```

## Reference

The following is a comprehensive list of all available data attributes. Each component name is prefixed with `data-flume-component`.

- `stage`
- `node`
- `node-header`
- `connection-svg`
- `connection-path`
- `ports`
- `ports-inputs`
- `ports-outputs-`
- `port-input`
- `port-output`
- `port-label`
- `port-handle`
- `comment`
- `comment-textarea`
- `comment-text`
- `comment-resize-handle`
- `control`
- `control-label`
- `select`
- `select-label`
- `select-desc`
- `text-input`
- `text-input-number`
- `text-input-textarea`
- `checkbox`
- `checkbox-label`
- `color-picker`
- `color-button`
- `toast`
- `toast-title`
- `toast-message`
- `toast-close`
- `ctx-menu`
- `ctx-menu-header`
- `ctx-menu-title`
- `ctx-menu-input`
- `ctx-menu-list`
- `ctx-menu-empty`
- `ctx-menu-option`
3 changes: 2 additions & 1 deletion src/components/Checkbox/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ const Checkbox = ({ label, data, onChange }) => {
return (
<div className={styles.wrapper}>
<input
data-flume-component="checkbox"
className={styles.checkbox}
type="checkbox"
id={id}
value={data}
checked={data}
onChange={e => onChange(e.target.checked)}
/>
<label className={styles.label} htmlFor={id}>
<label data-flume-component="checkbox-label" className={styles.label} htmlFor={id}>
{label}
</label>
</div>
Expand Down
4 changes: 3 additions & 1 deletion src/components/ColorPicker/ColorPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default ({ x, y, onColorPicked, onRequestClose }) => {

const testClickOutside = React.useCallback(
e => {
if (!wrapper.current.contains(e.target)) {
if (wrapper.current && !wrapper.current.contains(e.target)) {
onRequestClose();
document.removeEventListener("click", testClickOutside);
document.removeEventListener("contextmenu", testClickOutside);
Expand Down Expand Up @@ -39,6 +39,7 @@ export default ({ x, y, onColorPicked, onRequestClose }) => {

return (
<div
data-flume-component="color-picker"
ref={wrapper}
className={styles.wrapper}
style={{
Expand All @@ -63,6 +64,7 @@ export default ({ x, y, onColorPicked, onRequestClose }) => {
const ColorButton = ({ color, onSelected }) => (
<div className={styles.colorButtonWrapper}>
<button
data-flume-component="color-button"
className={styles.colorButton}
onClick={onSelected}
data-color={color}
Expand Down
5 changes: 4 additions & 1 deletion src/components/Comment/Comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,11 @@ export default ({
onDoubleClick={startTextEdit}
onWheel={e => e.stopPropagation()}
data-color={color}
data-flume-component="comment"
>
{isEditing ? (
<textarea
data-flume-component="comment-textarea"
className={styles.textarea}
onChange={handleTextChange}
onMouseDown={e => e.stopPropagation()}
Expand All @@ -162,7 +164,7 @@ export default ({
ref={textarea}
/>
) : (
<div data-comment={true} className={styles.text}>
<div data-flume-component="comment-text" data-comment={true} className={styles.text}>
{text}
</div>
)}
Expand All @@ -172,6 +174,7 @@ export default ({
stageRect={stageRect}
onDrag={handleResize}
onDragEnd={handleResizeEnd}
data-flume-component="comment-resize-handle"
/>
{menuOpen ? (
<Portal>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Connection/Connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ const Connection = ({
}) => {
const curve = calculateCurve(from, to)
return (
<svg className={styles.svg}>
<svg className={styles.svg} data-flume-component="connection-svg">
<path
data-connection-id={id}
data-output-node-id={outputNodeId}
data-output-port-name={outputPortName}
data-input-node-id={inputNodeId}
data-input-port-name={inputPortName}
data-flume-component="connection-path"
stroke="rgb(185, 186, 189)"
fill="none"
strokeWidth={3}
Expand Down
10 changes: 7 additions & 3 deletions src/components/ContextMenu/ContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ const ContextMenu = ({

return (
<div
data-flume-component="ctx-menu"
className={styles.menuWrapper}
onMouseDown={e => e.stopPropagation()}
onKeyDown={handleKeyDown}
Expand All @@ -141,10 +142,11 @@ const ContextMenu = ({
aria-activedescendant={`${menuId.current}-${selectedIndex}`}
>
{!hideHeader && (label ? true : !!options.length) ? (
<div className={styles.menuHeader}>
<label className={styles.menuLabel}>{label}</label>
<div className={styles.menuHeader} data-flume-component="ctx-menu-header">
<label className={styles.menuLabel} data-flume-component="ctx-menu-title">{label}</label>
{!hideFilter && options.length ? (
<input
data-flume-component="ctx-menu-input"
type="text"
placeholder="Filter options"
value={filter}
Expand All @@ -157,6 +159,7 @@ const ContextMenu = ({
</div>
) : null}
<div
data-flume-component="ctx-menu-list"
className={styles.optionsWrapper}
role="menu"
ref={menuOptionsWrapper}
Expand All @@ -176,7 +179,7 @@ const ContextMenu = ({
</ContextOption>
))}
{!options.length ? (
<span className={styles.emptyText}>{emptyText}</span>
<span data-flume-component="ctx-menu-empty" className={styles.emptyText}>{emptyText}</span>
) : null}
</div>
</div>
Expand All @@ -193,6 +196,7 @@ const ContextOption = ({
}) => {
return (
<div
data-flume-component="ctx-menu-option"
className={styles.option}
role="menuitem"
onClick={onClick}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Control/Control.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ const Control = ({
};

return (
<div className={styles.wrapper}>
<div className={styles.wrapper} data-flume-component="control">
{calculatedLabel && type !== "checkbox" && type !== "custom" && (
<label className={styles.controlLabel}>{calculatedLabel}</label>
<label data-flume-component="control-label" className={styles.controlLabel}>{calculatedLabel}</label>
)}
{getControlByType(type)}
</div>
Expand Down
13 changes: 8 additions & 5 deletions src/components/IoPorts/IoPorts.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ const IoPorts = ({
const resolvedOutputs = useTransputs(outputs, 'output', nodeId, inputData, connections);

return (
<div className={styles.wrapper}>
<div className={styles.wrapper} data-flume-component="ports">
{resolvedInputs.length ? (
<div className={styles.inputs}>
<div className={styles.inputs} data-flume-component="ports-inputs">
{resolvedInputs.map(input => (
<Input
{...input}
Expand All @@ -75,7 +75,7 @@ const IoPorts = ({
</div>
) : null}
{!!resolvedOutputs.length && (
<div className={styles.outputs}>
<div className={styles.outputs} data-flume-component="ports-outputs">
{resolvedOutputs.map(output => (
<Output
{...output}
Expand Down Expand Up @@ -124,6 +124,7 @@ const Input = ({

return (
<div
data-flume-component="port-input"
className={styles.transput}
data-controlless={isConnected || noControls || !controls.length}
onDragStart={e => {
Expand All @@ -142,7 +143,7 @@ const Input = ({
/>
) : null}
{(!controls.length || noControls || isConnected) && (
<label className={styles.portLabel}>{label || defaultLabel}</label>
<label data-flume-component="port-label" className={styles.portLabel}>{label || defaultLabel}</label>
)}
{!noControls && !isConnected
? (
Expand Down Expand Up @@ -183,14 +184,15 @@ const Output = ({

return (
<div
data-flume-component="port-output"
className={styles.transput}
data-controlless={true}
onDragStart={e => {
e.preventDefault();
e.stopPropagation();
}}
>
<label className={styles.portLabel}>{label || defaultLabel}</label>
<label data-flume-component="port-label" className={styles.portLabel}>{label || defaultLabel}</label>
<Port
type={type}
name={name}
Expand Down Expand Up @@ -392,6 +394,7 @@ const Port = ({
data-port-type={type}
data-port-transput-type={isInput ? "input" : "output"}
data-node-id={nodeId}
data-flume-component="port-handle"
onDragStart={e => {
e.preventDefault();
e.stopPropagation();
Expand Down
3 changes: 2 additions & 1 deletion src/components/Node/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ const Node = ({
onDragEnd={stopDrag}
innerRef={nodeWrapper}
data-node-id={id}
data-flume-component="node"
onContextMenu={handleContextMenu}
stageState={stageState}
stageRect={stageRect}
Expand Down Expand Up @@ -219,7 +220,7 @@ const Node = ({
};

const NodeHeader = ({ children, className = "", ...props }) => (
<h2 {...props} className={styles.label + (className ? ` ${className}` : "")}>
<h2 {...props} className={styles.label + (className ? ` ${className}` : "")} data-flume-component="node-header">
{children}
</h2>
);
Expand Down
6 changes: 3 additions & 3 deletions src/components/Select/Select.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ const SelectedOption = ({
wrapperRef,
onClick
}) => (
<div className={styles.selectedWrapper} onClick={onClick} ref={wrapperRef}>
<label>{label}</label>
{description ? <p>{description}</p> : null}
<div className={styles.selectedWrapper} onClick={onClick} ref={wrapperRef} data-flume-component="select">
<label data-flume-component="select-label">{label}</label>
{description ? <p data-flume-component="select-desc">{description}</p> : null}
</div>
);

Expand Down
1 change: 1 addition & 0 deletions src/components/Stage/Stage.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ const Stage = ({

return (
<Draggable
data-flume-component="stage"
id={`${STAGE_ID}${editorId}`}
className={styles.wrapper}
innerRef={wrapper}
Expand Down
4 changes: 3 additions & 1 deletion src/components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ const TextInput = ({
};

return (
<div className={styles.wrapper}>
<div className={styles.wrapper} data-flume-component="text-input">
{type === "number" ? (
<input
data-flume-component="text-input-number"
onKeyDown={e => {
if(e.keyCode === 69){
e.preventDefault()
Expand Down Expand Up @@ -69,6 +70,7 @@ const TextInput = ({
/>
) : (
<textarea
data-flume-component="text-input-textarea"
onChange={e => onChange(e.target.value)}
onMouseDown={handlePossibleResize}
type="text"
Expand Down
7 changes: 4 additions & 3 deletions src/components/Toaster/Toaster.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ const Toast = ({

return (
<div
data-flume-component="toast"
ref={wrapper}
className={styles.toast}
data-type={type}
Expand All @@ -106,17 +107,17 @@ const Toast = ({
role="alert"
>
{
title ? <span className={styles.title}>{title}</span> : null
title ? <span data-flume-component="toast-title" className={styles.title}>{title}</span> : null
}
<p>{message}</p>
<p data-flume-component="toast-message">{message}</p>
{!paused && (
<div
className={styles.timer}
style={{ animationDuration: `${duration}ms` }}
onAnimationEnd={e => e.stopPropagation()}
/>
)}
<button className={styles.exitButton} onClick={() => {
<button data-flume-component="toast-close" className={styles.exitButton} onClick={() => {
stopTimer()
onExitRequested(id)
}}></button>
Expand Down

0 comments on commit dbec4b1

Please sign in to comment.