UNPKG

@reusable-ui/range

Version:

A UI for the user defines a numeric value in the specified range.

275 lines (274 loc) 15.3 kB
// cssfn: import { // writes css in javascript: rule, children, style, // reads/writes css variables configuration: usesCssProps, usesPrefixedProps, usesSuffixedProps, overwriteProps, // writes complex stylesheets in simpler way: watchChanges, } from '@cssfn/core'; // writes css in javascript // reusable-ui core: import { // reusable common layouts: fillTextLineHeightLayout, fillTextLineWidthLayout, // border (stroke) stuff of UI: usesBorder, // padding (inner spacing) stuff of UI: usesPadding, // groups a list of UIs into a single UI: usesGroupable, usesOrientationable, // size options of UI: usesResizable, } from '@reusable-ui/core'; // a set of reusable-ui packages which are responsible for building any component // reusable-ui components: import { // styles: onEditableControlStylesChange, usesEditableControlLayout, usesEditableControlVariants, usesEditableControlStates, } from '@reusable-ui/editable-control'; // a base component // internals: import { // defaults: defaultOrientationableOptions, } from '../defaults.js'; import { // features: usesRange, } from '../features/range.js'; import { // elements: inputElm, trackElm, trackLowerElm, trackUpperElm, thumbElm, } from './elements.js'; import { // configs: ranges, cssRangeConfig, } from './config.js'; // styles: export const onRangeStylesChange = watchChanges(onEditableControlStylesChange, cssRangeConfig.onChange); export const usesRangeLayout = (options) => { // options: const orientationableStuff = usesOrientationable(options, defaultOrientationableOptions); const { ifOrientationInline, ifOrientationBlock, orientationInlineSelector, orientationBlockSelector } = orientationableStuff; const parentOrientationInlineSelector = `${orientationInlineSelector}&`; const parentOrientationBlockSelector = `${orientationBlockSelector}&`; const ifParentOrientationInline = (styles) => rule(parentOrientationInlineSelector, styles); const ifParentOrientationBlock = (styles) => rule(parentOrientationBlockSelector, styles); // dependencies: // features: const { borderRule: trackBorderRule, borderVars: trackBorderVars, } = usesBorder(usesPrefixedProps(ranges, 'track')); const { borderRule: thumbBorderRule, borderVars: thumbBorderVars, } = usesBorder(usesPrefixedProps(ranges, 'thumb')); const { paddingRule: trackPaddingRule, paddingVars: trackPaddingVars, } = usesPadding(usesPrefixedProps(ranges, 'track')); const { paddingRule: thumbPaddingRule, paddingVars: thumbPaddingVars, } = usesPadding(usesPrefixedProps(ranges, 'thumb')); const { rangeRule, rangeVars } = usesRange(); // capabilities: const { groupableRule } = usesGroupable({ orientationInlineSelector: parentOrientationInlineSelector, orientationBlockSelector: parentOrientationBlockSelector, itemsSelector: [trackLowerElm, trackUpperElm], // only select <trackLower> & <trackUpper>, do not modify the <thumb> }); const { separatorRule } = usesGroupable({ orientationInlineSelector: parentOrientationInlineSelector, orientationBlockSelector: parentOrientationBlockSelector, itemsSelector: [trackLowerElm, trackUpperElm], // only select <trackLower> & <trackUpper>, do not modify the <thumb> }); return style({ // layouts: ...usesEditableControlLayout(), ...style({ // layouts: ...ifOrientationInline({ display: 'flex', // use block flexbox, so it takes the entire parent's width flexDirection: 'row', // items are stacked horizontally }), ...ifOrientationBlock({ display: 'inline-flex', // use inline flexbox, so it takes the width & height as needed flexDirection: 'column', // items are stacked vertically }), justifyContent: 'start', // if range is not growable, the excess space (if any) placed at the end, and if no sufficient space available => the range's first part should be visible first alignItems: 'center', // default center items vertically flexWrap: 'nowrap', // prevents the range to wrap to the next row // positions: verticalAlign: 'baseline', // <Range>'s text should be aligned with sibling text, so the <Range> behave like <span> wrapper // accessibilities: touchAction: 'pinch-zoom', // prevents scrolling by touch, but allows to zoom_in/out WebkitTapHighlightColor: 'transparent', // no tap_&_hold highlight userSelect: 'none', // disable selecting text (double clicking and hold_and_mouse_move not causing selecting the <Range>) // children: ...ifOrientationInline({ ...children('::before', { // layouts: ...fillTextLineHeightLayout(), // adjust the <Range>'s height, the width is block (fills the entire parent's width) }), }), ...ifOrientationBlock({ ...children('::before', { // layouts: ...fillTextLineWidthLayout(), // adjust the <Range>'s width, the height is based on <Range>'s config }), }), ...children(inputElm, { // layouts: display: 'none', // hide the input }), ...children(trackElm, { // capabilities: ...groupableRule(), // make a nicely rounded corners // features: ...trackBorderRule(), ...trackPaddingRule(), // layouts: ...style({ // layouts: display: 'flex', // use block flexbox, so it takes the entire Range's width flexDirection: 'inherit', // customizable orientation // inherit to parent flexbox justifyContent: 'start', // if thumb is not growable, the excess space (if any) placed at the end, and if no sufficient space available => the thumb's first part should be visible first alignItems: 'center', // center thumb vertically flexWrap: 'nowrap', // no wrapping // sizes: boxSizing: 'border-box', // the final size is including borders & paddings flex: [[1, 1, '100%']], // growable, shrinkable, initial 100% parent's width // accessibilities: pointerEvents: 'none', // just an overlay element (ghost), no mouse interaction, clicking on it will focus on the parent // animations: boxShadow: ['none', '!important'], // no shadow & no focus animation // children: ...children([trackLowerElm, trackUpperElm], { // borders: ...separatorRule(), // turns the current border as separator between <trackLower> & <trackUpper> // layouts: ...style({ // layouts: display: 'inline-block', // use inline-block, so it takes the width & height as we set // backgrounds: backg: rangeVars.trackBackg, // borders: // a fix for track(Lower|Upper) background corners: border: trackBorderVars.border, // borderRadius : trackBorderVars.borderRadius, borderStartStartRadius: trackBorderVars.borderStartStartRadius, borderStartEndRadius: trackBorderVars.borderStartEndRadius, borderEndStartRadius: trackBorderVars.borderEndStartRadius, borderEndEndRadius: trackBorderVars.borderEndEndRadius, [trackBorderVars.borderWidth]: '0px', // only setup borderRadius, no borderStroke // sizes: alignSelf: 'stretch', // follows parent's height }), }), ...children(trackLowerElm, { // sizes: // the size grows in proportion to the given ratio, starting from the 1/2 size of <thumb> // the size cannot shrink ...ifParentOrientationInline({ flex: [[rangeVars.valueRatio, 0, `calc(${ranges.thumbInlineSize} / 2)`]], // growable, shrinkable, initial from 0 width; using `valueRatio` for the grow/shrink ratio }), ...ifParentOrientationBlock({ flex: [[rangeVars.valueRatio, 0, `calc(${ranges.thumbBlockSize} / 2)`]], // growable, shrinkable, initial from 0 width; using `valueRatio` for the grow/shrink ratio }), // borders: // removes border at the center of <thumb> when <Range mild={true}>: ...ifParentOrientationInline({ borderInlineEndWidth: '0px', }), ...ifParentOrientationBlock({ borderBlockStartWidth: '0px', }), // customize: ...usesCssProps(usesPrefixedProps(ranges, 'tracklower')), // apply config's cssProps starting with tracklower*** }), ...children(trackUpperElm, { // sizes: // the size grows in proportion to the given ratio, starting from the 1/2 size of <thumb> // the size cannot shrink ...ifParentOrientationInline({ flex: [[`calc(1 - ${rangeVars.valueRatio})`, 0, `calc(${ranges.thumbInlineSize} / 2)`]], // growable, shrinkable, initial from 0 width; using `1 - valueRatio` for the grow/shrink ratio }), ...ifParentOrientationBlock({ flex: [[`calc(1 - ${rangeVars.valueRatio})`, 0, `calc(${ranges.thumbBlockSize} / 2)`]], // growable, shrinkable, initial from 0 width; using `1 - valueRatio` for the grow/shrink ratio }), // borders: // removes border at the center of <thumb> when <Range mild={true}>: ...ifParentOrientationInline({ borderInlineStartWidth: '0px', }), ...ifParentOrientationBlock({ borderBlockEndWidth: '0px', }), // customize: ...usesCssProps(usesPrefixedProps(ranges, 'trackupper')), // apply config's cssProps starting with trackupper*** }), ...children(['&', thumbElm], { // accessibilities: cursor: 'inherit', }), ...children(thumbElm, { // features: ...thumbBorderRule(), ...thumbPaddingRule(), // layouts: ...style({ // layouts: display: 'inline-block', // use inline-block, so it takes the width & height as we set // positions: zIndex: 1, // the <thumb> should at the top of <trackLower> & <trackUpper> // sizes: boxSizing: 'border-box', // the final size is including borders & paddings // customize: ...usesCssProps(usesPrefixedProps(ranges, 'thumb')), // apply config's cssProps starting with thumb*** // borders: border: thumbBorderVars.border, // borderRadius : thumbBorderVars.borderRadius, borderStartStartRadius: thumbBorderVars.borderStartStartRadius, borderStartEndRadius: thumbBorderVars.borderStartEndRadius, borderEndStartRadius: thumbBorderVars.borderEndStartRadius, borderEndEndRadius: thumbBorderVars.borderEndEndRadius, // spacings: // padding : thumbPaddingVars.padding, paddingInline: thumbPaddingVars.paddingInline, paddingBlock: thumbPaddingVars.paddingBlock, // cancel out <thumb>'s size with negative margin, // so the <trackLower> & <trackUpper> can meet on the middle of the <thumb>: marginInline: `calc(0px - (${ranges.thumbInlineSize}) / 2)`, marginBlock: `calc(0px - (${ranges.thumbBlockSize}) / 2)`, }), }), // customize: ...usesCssProps(usesPrefixedProps(ranges, 'track')), // apply config's cssProps starting with track*** // borders: border: trackBorderVars.border, // borderRadius : trackBorderVars.borderRadius, borderStartStartRadius: trackBorderVars.borderStartStartRadius, borderStartEndRadius: trackBorderVars.borderStartEndRadius, borderEndStartRadius: trackBorderVars.borderEndStartRadius, borderEndEndRadius: trackBorderVars.borderEndEndRadius, // spacings: // padding : trackPaddingVars.padding, paddingInline: trackPaddingVars.paddingInline, paddingBlock: trackPaddingVars.paddingBlock, }), }), // customize: ...usesCssProps(ranges), // apply config's cssProps ...ifOrientationInline({ // overwrites propName = propName{Inline}: ...overwriteProps(ranges, usesSuffixedProps(ranges, 'inline')), }), ...ifOrientationBlock({ // overwrites propName = propName{Block}: ...overwriteProps(ranges, usesSuffixedProps(ranges, 'block')), }), }), // features: ...rangeRule(), // must be placed at the last }); }; export const usesRangeVariants = () => { // dependencies: // variants: const { resizableRule } = usesResizable(ranges); return style({ // variants: ...usesEditableControlVariants(), ...resizableRule(), }); }; export const usesRangeStates = usesEditableControlStates; export default () => style({ // layouts: ...usesRangeLayout(), // variants: ...usesRangeVariants(), // states: ...usesRangeStates(), });