master
Sven Slootweg 2 years ago
parent 6f4cd8bff5
commit e43c5312fc

@ -3,6 +3,10 @@
- Figure out a way to make this automatically load the correct PostCSS plugins, when used with icssify (since we probably don't want to pre-compile the CSS?)
- Sort out naming consistency for a) control names and b) classification of related elements (as their own controls? like for MenuItem, which is currently a part of Menu)
- Move radius etc. to the theme
- Shared 'stretchable' class that can be composed by all elements which can be height:100%'d in something like a ribbon
- Make this a fixed-name export rather than an auto-generated class name, to make it work across different versions of the library in the future? Maybe also do this for other 'marker classes'?
- Generic "focus" state abstraction, reusable between menus, dropdowns, etc. so that all 'ephemeral controls' behave like one would expect from a native application
- Add React to peerDependencies
# Name ideas

@ -21,14 +21,16 @@
"@react-hook/debounce": "^3.0.0",
"as-expression": "^1.0.0",
"assure-array": "^1.0.0",
"clamp": "^1.0.1",
"classnames": "^2.2.6",
"default-value": "^1.0.0",
"flatten": "^1.0.3",
"match-value": "^1.1.0",
"nanoid": "^3.1.12",
"react": "link:../site-builder/node_modules/react",
"react-resizable": "^2.0.0",
"react-use-measure": "^2.0.1",
"syncpipe": "^1.0.0"
"syncpipe": "^1.0.0",
"timm": "^1.7.1"
},
"browserify": {
"transform": [

@ -42,7 +42,7 @@ module.exports = function ButtonSet({ x, y, horizontal, vertical, choice, onSele
});
return (
<div className={withTheme("buttonSet", `direction-${direction}`)} style={generateGridItemStyle({ x, y })}>
<div className={withTheme("uilibComponent", "buttonSet", `direction-${direction}`)} style={generateGridItemStyle({ x, y })}>
{wrappedChildren}
</div>
);

@ -11,6 +11,8 @@ const generateGridItemStyle = require("../../util/generate-grid-item-style");
const Icon = require("../icon");
// FIXME: Mark div as button with aria attributes
module.exports = function Button({ x, y, id, type, onClick, children, icon, default: default_ }) {
// TODO: Validate type?
// TODO: How does icon + custom HTML children end up looking? Should we use a grid to ensure that the icon is always displayed on the left in its own column?
@ -38,8 +40,8 @@ module.exports = function Button({ x, y, id, type, onClick, children, icon, defa
: undefined;
return (
<button
className={withTheme("button", typeClass, { selected: isSelected })}
<div
className={withTheme("uilibComponent", "button", typeClass, { selected: isSelected })}
onClick={() => onClickAll(ownID)}
style={generateGridItemStyle({ x, y })}
>
@ -53,6 +55,6 @@ module.exports = function Button({ x, y, id, type, onClick, children, icon, defa
{children}
</contexts.button_selectedID.Provider>
</span>
</button>
</div>
);
};

@ -2,19 +2,21 @@
.button {
composes: centerContent from "../shared.css";
cursor: default;
box-sizing: border-box;
padding: 3px;
padding: 4px;
border-radius: 2px;
.icon {
display: block;
margin: 0px auto 7px auto;
margin: 3px auto 7px auto;
width: 28px;
height: 28px;
}
}
.buttonContents {
display: block;
padding: 0px 6px;
}

@ -1,6 +1,6 @@
/* NOTE: The 'glo-bal' filename of this file is a workaround to deal with `insert-module-globals` incorrectly detecting this file to contain a reference to a `g lobal` variable (due to the icssify-generated class names including the filename) */
.uilibComponent {
.uilibThemedElement {
font-family: sans-serif;
font-size: 12px;
}

@ -6,20 +6,7 @@ const defaultValue = require("default-value");
const useTheme = require("../../util/themeable");
const defaultStyle = require("./style.css");
const generateGridItemStyle = require("../../util/generate-grid-item-style");
function normalizeGridCellSize(value) {
if (typeof value === "number") {
return `${value}fr`;
} else {
return value;
}
}
function generateGridTemplateString(values) {
return values
.map((value) => normalizeGridCellSize(value))
.join(" ");
}
const generateGridTemplateString = require("../../util/generate-grid-template-string");
module.exports = function Grid({ x, y, rows, columns, rowGap, columnGap, children }) {
let { withTheme } = useTheme({ control: "grid", defaultStyle });
@ -38,7 +25,7 @@ module.exports = function Grid({ x, y, rows, columns, rowGap, columnGap, childre
// FIXME: Context for x/y
return (
<div className={withTheme("grid")} style={style}>
<div className={withTheme("uilibComponent", "grid")} style={style}>
{children}
</div>
);

@ -1,5 +1,7 @@
.grid {
display: grid;
width: 100%;
height: 100%;
row-gap: 2px;
column-gap: 2px;
}

@ -11,6 +11,6 @@ module.exports = function Icon({ icon }) {
// FIXME: Don't hardcode icon base path, get it from theme
return (
<img className={withTheme("icon")} src={`/images/icons/${icon}.svg`} />
<img className={withTheme("uilibComponent", "icon")} src={`/images/icons/${icon}.svg`} />
);
};

@ -66,7 +66,7 @@ module.exports = function ListItem({ label, id, children }) {
return (
<React.Fragment>
{/* FIXME: item_selected capitalization */}
<div className={withTheme("item", { item_selected: isSelected })} style={style} onMouseDown={handleMouseDown} onClick={handleClick}>
<div className={withTheme("uilibComponent", "item", { item_selected: isSelected })} style={style} onMouseDown={handleMouseDown} onClick={handleClick}>
{(children != null)
? <Expander expanded={expanded} onClick={toggleExpanded} />
: <ExpanderPlaceholder />}

@ -19,11 +19,12 @@
.expander, .expanderPlaceholder {
width: 12px;
height: 12px;
margin-right: 4px;
padding-right: 4px;
padding-left: 1px;
vertical-align: 2px;
}
.expander {
text-align: center;
font-size: 10px;
font-size: .7em;
}

@ -8,6 +8,8 @@ const ListItemContext = require("../../contexts/list-item");
const generateGridItemStyle = require("../../util/generate-grid-item-style");
// FIXME: Track full collapsing state throughout the tree
// FIXME: Remove left-margin if there are zero collapsible items (as we don't need to render the collapse icons)
// FIXME: Only trigger select when the selected item has actually changed!
module.exports = function List({ x, y, children, onPick, onSelect }) {
let [ selectedItem, setSelectedItem ] = React.useState([]);
@ -32,7 +34,7 @@ module.exports = function List({ x, y, children, onPick, onSelect }) {
return (
<ListItemContext.Provider value={itemContext}>
<div className={withTheme("list")} style={generateGridItemStyle({ x, y })}>
<div className={withTheme("uilibComponent", "list")} style={generateGridItemStyle({ x, y })}>
{children}
</div>
</ListItemContext.Provider>

@ -45,7 +45,7 @@ module.exports = function MenuBar({ style, children }) {
return (
<MenuItemContext.Provider value={itemContext}>
<div className={withTheme("menuBar")} style={style} ref={element}>
<div className={withTheme("uilibComponent", "menuBar")} style={style} ref={element}>
{children}
</div>
</MenuItemContext.Provider>

@ -8,5 +8,5 @@ const defaultStyle = require("./style.css");
module.exports = function MenuDivider() {
let { withTheme } = useTheme({ control: "menu", defaultStyle });
return <hr className={withTheme("divider")} />;
return <hr className={withTheme("uilibComponent", "divider")} />;
};

@ -65,6 +65,7 @@ module.exports = function MenuItem({ label, title, menu, onClick }) {
};
let buttonClasses = withTheme(
"uilibComponent",
"item",
{ item_selected: isSelected },
{ item_directPress: (menu == null) }

@ -43,7 +43,7 @@ module.exports = function Menu({ children }) {
if (parentItemContext.isActive) {
return (
<MenuItemContext.Provider value={itemContext}>
<div className={withTheme("menu")} style={style}>
<div className={withTheme("uilibComponent", "menu")} style={style}>
{children}
</div>
</MenuItemContext.Provider>

@ -0,0 +1,138 @@
"use strict";
const React = require("react");
const useMeasure = require("react-use-measure");
const syncpipe = require("syncpipe");
const defaultValue = require("default-value");
const clamp = require("clamp");
const useTheme = require("../../util/themeable");
const defaultStyle = require("./style.css");
const childrenWithProps = require("../../util/children-with-props");
const generateGridTemplateString = require("../../util/generate-grid-template-string");
const useIndexedState = require("../../util/use-indexed-state");
const sum = require("../../util/sum");
const MINIMUM_PANE_SIZE = 16;
// FIXME: Logic to constrain resizing when increasing a pane's size further would make the paneset exceed its size boundaries
// FIXME: Disable selection globally during dragging
function selectSmallestSize(sizes) {
console.log("sizes", sizes);
if (sizes.some((size) => size != null && isNaN(size))) {
throw new Error(`NaN encountered!`);
}
return syncpipe(sizes, [
(_) => _.filter((size) => size != null),
(_) => (_.length > 0)
? Math.min(... _)
: undefined
]);
}
module.exports = function PaneSet({ vertical, horizontal, children }) {
let { withTheme } = useTheme({ control: "paneSet", defaultStyle });
let [ boundsRef, bounds ] = useMeasure({ debounce: 10 });
let [ newSizes, setNewSize, resetNewSizes ] = useIndexedState();
// FIXME: How to deal with new children? Reset all newSizes?
let direction = (vertical === true)
? "vertical"
: "horizontal";
let ownSize = (direction === "vertical")
? bounds.height
: bounds.width;
let childrenArray = React.Children.toArray(children);
let minimumSizes = React.useMemo(() => {
return childrenArray.map((child) => {
return selectSmallestSize([ child.props.minimumSize, MINIMUM_PANE_SIZE ]);
});
});
// FIXME: Detect when this fails
let autoPaneMinimumSize = syncpipe(childrenArray, [
(_) => _.findIndex((child) => child.resizable !== true),
(_) => minimumSizes[_]
]);
let maximumSizes = React.useMemo(() => {
let paneSizes = childrenArray.map((child, i) => {
// FIXME: Require initialSize when resizable
return defaultValue(newSizes[i], child.props.initialSize);
});
return childrenArray.map((_, ownIndex) => {
let otherPaneSizes = syncpipe(paneSizes, [
(_) => _.filter((size, i) => size != null && i !== ownIndex),
(_) => sum(_)
]);
return clamp(ownSize - otherPaneSizes - autoPaneMinimumSize, 0, Infinity);
});
}, [ childrenArray, newSizes, ownSize ]);
let autoSizeSeen = 0;
// NOTE: Not using React.Children.map because it will ignore null return values
let handleSides = childrenArray.map((child) => {
if (child.props.resizable === true) {
if (autoSizeSeen > 0) {
return (direction === "vertical")
? "n" // north, top
: "w"; // west, left
} else {
return (direction === "vertical")
? "s" // south, bottom
: "e"; // east, right
}
} else {
autoSizeSeen += 1;
return null;
}
});
let gridTemplateProperty = (direction === "vertical")
? "gridTemplateRows"
: "gridTemplateColumns";
let gridTemplateString = syncpipe(handleSides, [
(_) => _.map((handle) => handle != null ? "auto" : 1),
(_) => generateGridTemplateString(_)
]);
let style = {
[gridTemplateProperty]: gridTemplateString
};
if (autoSizeSeen !== 1) {
// We don't allow *multiple* auto-sized panes, to avoid weird behaviour where the auto-layouted panes may not end up exactly where the resize handle is
throw new Error(`Exactly one (center) pane must be non-resizable!`);
}
return (
<div ref={boundsRef} className={withTheme("uilibComponent", "paneSet", `direction-${direction}`)} style={style}>
{childrenWithProps(children, (child, i) => {
let handleSide = handleSides[i];
return {
onResized: (newSize) => {
setNewSize(i, newSize);
},
maximumSize: selectSmallestSize([ child.props.maximumSize, maximumSizes[i] ]),
// Note that we shouldn't override an explicitly user-configured handleSide
... (handleSide == null)
? {}
: {
handleSide: defaultValue(child.props.handleSide, handleSide),
direction: direction
}
};
})}
</div>
);
};

@ -0,0 +1,24 @@
:import("../pane/style.css") { pane: pane; }
.paneSet {
display: grid;
width: 100%;
height: 100%;
}
.direction-horizontal {
.pane {
height: 100%;
/* FIXME: This should really be & > ..., but that isn't allowed by the parser */
:global .react-resizable {
height: 100%;
}
}
}
.direction-vertical {
.pane {
width: 100%;
}
}

@ -0,0 +1,84 @@
"use strict";
const React = require("react");
const { ResizableBox } = require("react-resizable");
const defaultValue = require("default-value");
const useDisableSelection = require("../../util/use-disable-selection");
const useTheme = require("../../util/themeable");
const defaultStyle = require("./style.css");
function Handle({ ... props }) {
let { withTheme } = useTheme({ control: "pane", defaultStyle });
// NOTE: We need to forward all props to make the mouse events work, which react-resizable internally adds to the component
return (
<div className={withTheme("handleHitbox")} {... props}>
<div className={withTheme("handle")} />
</div>
);
}
// TODO: Only exact pixel values currently supported for initialSize, add some sort of percentage support in the future?
// FIXME: Increase handle hitbox size
// FIXME: `Resizable` wrapper element
// NOTE: onResized and direction are internal, handleSide too but it can be overwritten
module.exports = function Pane({ onResized, resizable, initialSize, minimumSize, maximumSize, direction, handleSide, children }) {
let { withTheme } = useTheme({ control: "pane", defaultStyle });
let { disableGlobalSelection, enableGlobalSelection } = useDisableSelection();
// FIXME: Validate inputs
let sizeProps = (direction === "vertical")
? {
height: initialSize,
minConstraints: [ 0, defaultValue(minimumSize, 0) ],
maxConstraints: [ Infinity, defaultValue(maximumSize, Infinity) ],
}
: {
width: initialSize,
minConstraints: [ defaultValue(minimumSize, 0), 0 ],
maxConstraints: [ defaultValue(maximumSize, Infinity), Infinity ],
};
let axisProp = (direction === "vertical")
? "y"
: "x";
function handleOnResizeStop(node, event) {
let newSize = (direction === "vertical")
? event.size.height
: event.size.width;
onResized(newSize);
enableGlobalSelection();
}
function handleOnResizeStart() {
disableGlobalSelection();
}
// NOTE: Needed to prevent react-resizable from somehow breaking hooks inside of Handle
function handleFunction() {
return <Handle />;
}
// FIXME: Do we still need the contentWrapper, without the bounds-measuring logic?
let content = (
<div className={withTheme("contentWrapper")}>
{children}
</div>
);
return (
<div className={withTheme("uilibComponent", "pane", `handleSide-${handleSide}`)}>
{(resizable === true)
? <ResizableBox handle={handleFunction} resizeHandles={[ handleSide ]} axis={axisProp} onResizeStop={handleOnResizeStop} onResizeStart={handleOnResizeStart} {... sizeProps}>
{content}
</ResizableBox>
: content
}
</div>
);
};

@ -0,0 +1,88 @@
/* FIXME: Move these variables to theme variables eventually */
$handleSize: 2px;
$hitboxPadding: 5px;
.pane {
position: relative;
overflow: hidden;
}
.contentWrapper {
width: 100%;
height: 100%;
}
.handle {
height: 100%;
}
.handleHitbox {
position: absolute;
/* background-color: red; */
}
.handleSide-n, .handleSide-s {
.handleHitbox {
cursor: ns-resize;
padding: $hitboxPadding 0;
}
.handle {
height: $handleSize;
}
}
.handleSide-e, .handleSide-w {
.handleHitbox {
cursor: ew-resize;
padding: 0 $hitboxPadding;
}
.handle {
width: $handleSize;
}
}
.handleSide-n {
/* north / top */
padding-top: $handleSize;
.handleHitbox {
top: calc(0px - $hitboxPadding);
left: 0;
right: 0;
}
}
.handleSide-s {
/* south / bottom */
padding-bottom: $handleSize;
.handleHitbox {
bottom: calc(0px - $hitboxPadding);
left: 0;
right: 0;
}
}
.handleSide-e {
/* east - right */
padding-right: $handleSize;
.handleHitbox {
top: 0;
bottom: 0;
right: calc(0px - $hitboxPadding);
}
}
.handleSide-w {
/* west - left */
padding-left: $handleSize;
.handleHitbox {
top: 0;
bottom: 0;
left: calc(0px - $hitboxPadding);
}
}

@ -11,7 +11,7 @@ module.exports = function ProgressBar({ x, y, progress, color }) {
let { withTheme } = useTheme({ control: "progressBar", defaultStyle });
return (
<div className={withTheme("bar")} style={generateGridItemStyle({ x, y })}>
<div className={withTheme("uilibComponent", "bar")} style={generateGridItemStyle({ x, y })}>
<div className={withTheme("fill")} style={{width: `${progress * 100}%`, backgroundColor: color}} />
</div>
);

@ -9,7 +9,7 @@ const Button = require("../button");
const ProgressBar = require("../progress-bar");
module.exports = function ProgressButton({ x, y, horizontal, vertical, type, icon, progress, progressColor, children }) {
// FIXME: progressColor in theme, same for custom button style -- maybe make `type` a thing that takes an array, for multiple type classes? to deal with composite controls
// FIXME: progressColor in theme, same for custom button style / proportions -- maybe make `type` a thing that takes an array, for multiple type classes? to deal with composite controls
let { withTheme } = useTheme({ control: "progressButton", defaultStyle });
@ -18,7 +18,7 @@ module.exports = function ProgressButton({ x, y, horizontal, vertical, type, ico
: "horizontal";
return (
<div className={withTheme("progressButton", `direction-${direction}`)} style={generateGridItemStyle({ x, y })}>
<div className={withTheme("uilibComponent", "progressButton", `direction-${direction}`)} style={generateGridItemStyle({ x, y })}>
<Button type={type} icon={icon}>
{children}
</Button>

@ -11,7 +11,7 @@ module.exports = function RibbonBox({ label, children, width, height }) {
// FIXME: How to handle width/height here? Is the current approach correct? Or should we let the user override this through a class?
return (
<div className={withTheme("box")} style={{width: width, height: height}}>
<div className={withTheme("uilibComponent", "box")} style={{width: width, height: height}}>
<div className={withTheme("contents")}>
{children}
</div>

@ -3,6 +3,7 @@
grid-template-rows: 1fr auto;
margin: 4px 0px;
padding: 0px 8px;
background-color: transparent;
&:first-child {
border-left: none;

@ -10,7 +10,7 @@ module.exports = function Ribbon({ x, y, children }) {
let { withTheme } = useTheme({ control: "ribbon", defaultStyle });
return (
<div className={withTheme("ribbon")} style={generateGridItemStyle({ x, y })}>
<div className={withTheme("uilibComponent", "ribbon")} style={generateGridItemStyle({ x, y })}>
{children}
</div>
);

@ -4,6 +4,7 @@
:import("../icon/style.css") { icon: icon; }
:import("../grid/style.css") { grid_class: grid; } /* NOTE: Weird alias to avoid conflicting with `display: grid;` */
:import("../progress-bar/style.css") { progressBar: bar; }
:import("../status-indicator/style.css") { statusIndicator: statusIndicator; }
:import("../shared.css") { combinedButton: combinedButton; }
.ribbon {
@ -44,4 +45,8 @@
.progressButton {
display: grid;
}
.statusIndicator {
height: 100%;
}
}

@ -34,3 +34,71 @@
.combinedVertical {
/* flex-direction: column; */
}
:global {
.react-resizable {
position: relative;
}
.react-resizable-handle {
position: absolute;
width: 20px;
height: 20px;
background-repeat: no-repeat;
background-origin: content-box;
box-sizing: border-box;
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+');
background-position: bottom right;
padding: 0 3px 3px 0;
}
.react-resizable-handle-sw {
bottom: 0;
left: 0;
cursor: sw-resize;
transform: rotate(90deg);
}
.react-resizable-handle-se {
bottom: 0;
right: 0;
cursor: se-resize;
}
.react-resizable-handle-nw {
top: 0;
left: 0;
cursor: nw-resize;
transform: rotate(180deg);
}
.react-resizable-handle-ne {
top: 0;
right: 0;
cursor: ne-resize;
transform: rotate(270deg);
}
.react-resizable-handle-w,
.react-resizable-handle-e {
top: 50%;
margin-top: -10px;
cursor: ew-resize;
}
.react-resizable-handle-w {
left: 0;
transform: rotate(135deg);
}
.react-resizable-handle-e {
right: 0;
transform: rotate(315deg);
}
.react-resizable-handle-n,
.react-resizable-handle-s {
left: 50%;
margin-left: -10px;
cursor: ns-resize;
}
.react-resizable-handle-n {
top: 0;
transform: rotate(225deg);
}
.react-resizable-handle-s {
bottom: 0;
transform: rotate(45deg);
}
}

@ -0,0 +1,19 @@
"use strict";
const React = require("react");
const useTheme = require("../../util/themeable");
const defaultStyle = require("./style.css");
const generateGridItemStyle = require("../../util/generate-grid-item-style");
module.exports = function StatusIndicator({ x, y, status, children }) {
let { withTheme } = useTheme({ control: "statusIndicator", defaultStyle });
return (
<div className={withTheme("uilibComponent", "statusIndicator", `status-${status}`)} style={generateGridItemStyle({ x, y })}>
<div className={withTheme("content")}>
{children}
</div>
</div>
);
};

@ -0,0 +1,20 @@
.statusIndicator {
composes: centerContent from "../shared.css";
border-radius: 2px;
}
.content {
padding: 4px; /* FIXME: Why do we have this? */
}
.status-positive {
background-color: rgb(0, 93, 9);
}
.status-negative {
background-color: rgb(106, 0, 0);
}
.status-neutral {
background-color: #5a5a5a;
}

@ -14,7 +14,7 @@ module.exports = function Text({ x, y, align, children }) {
: undefined;
return (
<div className={withTheme("text", alignClass)} style={generateGridItemStyle({ x, y })}>
<div className={withTheme("uilibComponent", "text", alignClass)} style={generateGridItemStyle({ x, y })}>
{children}
</div>
);

@ -1,5 +1,7 @@
.text {
/* FIXME: Should be moved to ribbon-only, but this is probably not possible with ICSS */
composes: centerContent from "../shared.css";
background-color: transparent;
}
.align-left {

@ -9,7 +9,7 @@ module.exports = {
ButtonSet: require("./controls/button-set/index.jsx"),
ProgressBar: require("./controls/progress-bar/index.jsx"),
ProgressButton: require("./controls/progress-button/index.jsx"),
// Text: require("./controls/text.jsx"),
StatusIndicator: require("./controls/status-indicator/index.jsx"),
// /* Pane layout */
// PaneLayout: require("./controls/pane-layout/layout.jsx"),
@ -40,11 +40,18 @@ module.exports = {
// RibbonStatusIndicator: require("./controls/ribbon/status-indicator.jsx"),
// RibbonText: require("./controls/ribbon/text.jsx")
Pane: require("./controls/pane"),
PaneSet: require("./controls/pane-set"),
SetTheme: require("./util/themeable/set-theme"),
themes: {
dark: {
getIcon: (name) => `/icons/${name}.svg`,
css: require("./themes/dark.css")
},
light: {
getIcon: (name) => `/icons/${name}.svg`,
css: require("./themes/light.css")
}
}
};

@ -1,4 +1,5 @@
$darkGray: rgb(34, 34, 34);
$lessDarkGray: rgb(44, 44, 44);
$hoverColor: rgba(113, 113, 113, 0.26);
$activeColor: $darkGray;
@ -25,13 +26,23 @@ body {
inset -1px -1px rgba(135, 131, 131, 0.3);
}
.bar {
composes: edgeRaise;
background-color: rgb(60, 62, 66);
.uilibThemedElement {
color: white;
}
/* Default to a black background for any newly-defined components that don't have their own styles yet, for minimum readability */
.uilibComponent {
color: white;
background-color: black;
}
/* ... but don't apply this to known container-type components which don't have their own styling */
.icon_icon, .grid_grid, .ribbonBox_box {
background-color: transparent;
}
.bar {
composes: edgeRaise;
background-color: rgb(60, 62, 66);
}
.list_list {
@ -89,7 +100,7 @@ body {
.button_button {
background-color: #38383c;
// background-color: magenta;
/* background-color: magenta; */
border: none;
&:hover {
@ -126,6 +137,12 @@ body {
background-color: rgb(54, 135, 18);
}
.pane_handle {
background-color: $darkGray;
composes: edgeRaise;
}
.ribbon_ribbon {
/* ... */
}

@ -0,0 +1,141 @@
$darkGray: rgb(34, 34, 34);
$lessDarkGray: rgb(44, 44, 44);
$hoverColor: #e5e5e5;
$activeColor: #c5dae5;
$white: white;
$barColor: #edf5f5;
$lightGray: silver;
/* :export {
darkGray: $darkGray;
hoverColor: $hoverColor;
activeColor: $activeColor;
} */
/* FIXME: Casemap control names */
body {
/* FIXME: Think about whether this belongs in the theme, or whether it should be scoped to some sort of ApplicationFrame instead. */
background-color: white;
color: black;
}
.edgeRaise {
box-shadow: inset -1px -1px rgba(187, 183, 183, 0.7),
inset 1px 1px rgba(206, 206, 206, 0.3);
}
.edgeLower {
box-shadow: inset 1px 1px rgba(187, 183, 183, 0.7),
inset -1px -1px rgba(206, 206, 206, 0.3);
}
.bar {
composes: edgeRaise;
background-color: $barColor;
}
.uilibComponent {
color: black;
}
.list_list {
/* color: red; */
}
.list_item {
&:nth-child(odd) {
/* background-color: rgb(45, 45, 45); */
/* background-color: rgb(51, 51, 51); */
background-color: rgb(57, 57, 60);
}
&:nth-child(even) {
background-color: rgb(27, 27, 27);
}
&.list_item_selected {
/* FIXME: Find a better color for this. */
/* background-color: rgb(38, 38, 42); */
background-color: blue; /* FIXME: Remove testing color */
}
}
.menu_menuBar, .ribbon_ribbon {
composes: bar;
}
.menu_menuBar > .menu_item {
&:hover {
background-color: $hoverColor;
}
&.menu_item_selected, &menu_item_directPress {
background-color: $activeColor;
}
}
.menu_menu {
background-color: $barColor;
box-shadow: 1px 1px 2px rgb(54, 54, 54);
& > .menu_item:hover {
background-color: $hoverColor;
}
hr {
border-bottom: 1px solid rgb(78, 78, 78);
}
}
.menu_divider {
border-bottom: 1px solid rgb(78, 78, 78);
}
.button_button {
background-color: #d0d0d0;
/* background-color: magenta; */
border: none;
&:hover {
background-color: $hoverColor;
}
&:active {
@include edge-lower;
background-color: $activeColor;
}
&.button_selected {
background-color: $darkGray;
}
}
.ribbonBox_box {
border-left: 1px solid rgba(30, 27, 27, 0.7);
border-right: 1px solid rgba(135, 131, 131, 0.3);
}
.ribbonBox_label {
background-color: #f3f1fa;
}
.progressBar_bar {
border: 1px solid rgb(32, 32, 32);
/* background-color: rgb(64, 64, 70); */
background-color: rgb(43, 43, 47);
}
.progressBar_fill {
background-color: rgb(54, 135, 18);
}
.pane_handle {
background-color: rgb(245, 245, 245);
composes: edgeRaise;
}
.ribbon_ribbon {
/* ... */
}

@ -0,0 +1,20 @@
"use strict";
// NOTE: Only use this where absolutely needed! Normally, the context API should be preferred where possible.
const React = require("react");
module.exports = function childrenWithProps(children, mapper) {
return React.Children.map(children, (child, i) => {
let extraProps = (typeof mapper === "function")
? mapper(child, i)
: mapper;
if (extraProps != null) {
// FIXME: Do we need to check React.isValidElement here, to deal with eg. text nodes? Or is that handled internally by React.cloneElement?
return React.cloneElement(child, extraProps);
} else {
return child;
}
});
};

@ -0,0 +1,15 @@
"use strict";
function normalizeGridCellSize(value) {
if (typeof value === "number") {
return `${value}fr`;
} else {
return value;
}
}
module.exports = function generateGridTemplateString(values) {
return values
.map((value) => normalizeGridCellSize(value))
.join(" ");
};

@ -0,0 +1,5 @@
"use strict";
module.exports = function sum(numbers) {
return numbers.reduce((total, number) => total + number, 0);
};

@ -20,6 +20,8 @@ function mapDefaultName(defaultStyle, className) {
module.exports = function useTheme({ control, defaultStyle }) {
let theme = React.useContext(ThemeContext);
console.log({ css: theme.css });
let rulePrefix = (control != null)
? `${control}_`
: "";
@ -37,9 +39,16 @@ module.exports = function useTheme({ control, defaultStyle }) {
(theme != null)
? mapThemeName(theme.css, className, rulePrefix)
: null,
mapDefaultName(globalStyle, "uilibComponent"),
mapDefaultName(globalStyle, "uilibThemedElement"),
(theme != null)
? mapThemeName(theme.css, "uilibComponent", "")
? mapThemeName(theme.css, "uilibThemedElement", "")
: null,
// Special case: unprefixed control-independent class names -- FIXME: This check does not work for conditional class names!
(className === "uilibComponent")
? mapDefaultName(globalStyle, className)
: null,
(theme != null && className === "uilibComponent")
? mapThemeName(theme.css, className, "")
: null,
];
}),

@ -0,0 +1,22 @@
"use strict";
const React = require("react");
module.exports = function useDisableSelection() {
let body = document.querySelector("body");
let lastSelectSetting = React.useRef();
return {
disableGlobalSelection: function () {
if (body.style.userSelect !== "none") {
lastSelectSetting.current = body.style.userSelect;
body.style.userSelect = "none";
}
},
enableGlobalSelection: function () {
if (body.style.userSelect === "none" && lastSelectSetting.current != null) {
body.style.userSelect = lastSelectSetting.current;
}
}
};
};

@ -0,0 +1,30 @@
"use strict";
const React = require("react");
const timm = require("timm");
module.exports = function useIndexedState(initialState = []) {
let [ state, setState ] = React.useState(initialState);
function setIndexedState(index, value) {
setState((oldArray) => {
let evaluatedValue = (typeof value === "function")
? value(oldArray[index])
: value;
return timm.replaceAt(oldArray, index, evaluatedValue);
});
}
function resetState() {
setState((oldArray) => {
return oldArray.map(() => undefined);
});
}
return [
state,
setIndexedState,
resetState
];
};

@ -1113,7 +1113,12 @@ chardet@^0.7.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
classnames@^2.2.6:
clamp@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/clamp/-/clamp-1.0.1.tgz#66a0e64011816e37196828fdc8c8c147312c8634"
integrity sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ=
classnames@^2.2.5, classnames@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
@ -1841,7 +1846,7 @@ lodash@^4.17.19:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
loose-envify@^1.0.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@ -2115,7 +2120,7 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@15.x, prop-types@^15.6.0, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@ -2129,11 +2134,27 @@ punycode@^2.1.0:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
react-draggable@^4.0.3:
version "4.4.3"
resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.3.tgz#0727f2cae5813e36b0e4962bf11b2f9ef2b406f3"
integrity sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==
dependencies:
classnames "^2.2.5"
prop-types "^15.6.0"
react-is@^16.8.1:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
react-resizable@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/react-resizable/-/react-resizable-2.0.0.tgz#e254908acd949e52015ecf56e9788dc73b8b510d"
integrity sha512-oNVMKk+IQNW+nxcCB1W1uShZLJhIh3TDSW8NAbfck6N2jsiUTQ/V5ozBVFEHlRsxgxkf2A22rZJnXLRCBF14OA==
dependencies:
prop-types "15.x"
react-draggable "^4.0.3"
react-use-measure@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.0.1.tgz#4f23f94c832cd4512da55acb300d1915dcbf3ae8"
@ -2141,10 +2162,6 @@ react-use-measure@^2.0.1:
dependencies:
debounce "^1.2.0"
"react@link:../site-builder/node_modules/react":
version "0.0.0"
uid ""
readable-stream@^3.4.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
@ -2471,6 +2488,11 @@ through@^2.3.6:
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
timm@^1.7.1:
version "1.7.1"
resolved "https://registry.yarnpkg.com/timm/-/timm-1.7.1.tgz#96bab60c7d45b5a10a8a4d0f0117c6b7e5aff76f"
integrity sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"

Loading…
Cancel
Save