UNPKG

@reusable-ui/orientationable

Version:

A capability of UI to rotate its layout.

153 lines (152 loc) 8.61 kB
// cssfn: import { // writes css in javascript: rule, startsCapitalized, } from '@cssfn/core'; // writes css in javascript // hooks: // variants: //#region orientationable //#region caches const orientationClassesCache = new Map(); export const createOrientationClass = (orientationName) => { const cached = orientationClassesCache.get(orientationName); if (cached !== undefined) return cached; // null is allowed if (orientationName === undefined) { orientationClassesCache.set(orientationName, null); return null; } // if const orientationClass = `o${startsCapitalized(orientationName)}`; orientationClassesCache.set(orientationName, orientationClass); return orientationClass; }; let defaultInlineOrientationableStuffsCache = undefined; let defaultBlockOrientationableStuffsCache = undefined; // rarely used: // let defaultInlineStartOrientationableWithDirectionStuffsCache : WeakRef<OrientationableWithDirectionStuff>|undefined = undefined; // let defaultInlineEndOrientationableWithDirectionStuffsCache : WeakRef<OrientationableWithDirectionStuff>|undefined = undefined; // let defaultBlockStartOrientationableWithDirectionStuffsCache : WeakRef<OrientationableWithDirectionStuff>|undefined = undefined; // let defaultBlockEndOrientationableWithDirectionStuffsCache : WeakRef<OrientationableWithDirectionStuff>|undefined = undefined; //#endregion caches // utilities: const fallbacks = (value1, value2, value3) => { if (value1 !== undefined) return value1; if (value2 !== undefined) return value2; return value3; }; export const defaultInlineOrientationableOptions = { defaultOrientation: 'inline' }; export const defaultBlockOrientationableOptions = { defaultOrientation: 'block' }; export const usesOrientationable = (options, defaultOptions = defaultBlockOrientationableOptions) => { const isDefaultBlock = ((defaultOptions === defaultBlockOrientationableOptions) && ((options === undefined) || (options === defaultOptions) || ((options.defaultOrientation === defaultOptions.defaultOrientation) && (options.orientationInlineSelector === defaultOptions.orientationInlineSelector) && (options.orientationBlockSelector === defaultOptions.orientationBlockSelector)))); const isDefaultInline = !isDefaultBlock && ((defaultOptions === defaultInlineOrientationableOptions) && ((options === undefined) || (options === defaultOptions) || ((options.defaultOrientation === defaultOptions.defaultOrientation) && (options.orientationInlineSelector === defaultOptions.orientationInlineSelector) && (options.orientationBlockSelector === defaultOptions.orientationBlockSelector)))); if (isDefaultBlock) { const cached = defaultBlockOrientationableStuffsCache?.deref(); if (cached) return cached; } else if (isDefaultInline) { const cached = defaultInlineOrientationableStuffsCache?.deref(); if (cached) return cached; } // if const defaultOrientation = fallbacks(options?.defaultOrientation, defaultOptions.defaultOrientation, 'block'); const orientationInlineSelector = fallbacks(options?.orientationInlineSelector, defaultOptions.orientationInlineSelector, ((defaultOrientation === 'inline') ? ':not(:is(.oBlock, .oBlock-start, .oBlock-end))' : ':is(.oInline, .oInline-start, .oInline-end)')); const orientationBlockSelector = fallbacks(options?.orientationBlockSelector, defaultOptions.orientationBlockSelector, ((defaultOrientation === 'block') ? ':not(:is(.oInline, .oInline-start, .oInline-end))' : ':is(.oBlock, .oBlock-start, .oBlock-end)')); const result = { ...options, defaultOrientation, orientationInlineSelector, orientationBlockSelector, ifOrientationInline: (styles) => rule(orientationInlineSelector, styles), ifOrientationBlock: (styles) => rule(orientationBlockSelector, styles), }; if (isDefaultBlock) { defaultBlockOrientationableStuffsCache = new WeakRef(result); } else if (isDefaultInline) { defaultInlineOrientationableStuffsCache = new WeakRef(result); } // if return result; }; export const useOrientationable = ({ orientation }, defaultOptions = defaultBlockOrientationableOptions) => ({ class: createOrientationClass(orientation) ?? null, get isOrientationBlock() { return ((orientation ?? defaultOptions.defaultOrientation)?.startsWith('block') ?? false); }, get isOrientationVertical() { /* Assumes block === vertical, in most cases the `writing-mode` is `horizontal-tb`. To check for the styling on element is quite expensive (needs to get the ref of the element and then may re-render). For performance reason, we assume the `writing-mode = horizontal-tb`. */ return this.isOrientationBlock; }, get 'aria-orientation'() { return (this.isOrientationVertical ? 'vertical' : 'horizontal'); } }); export const defaultInlineStartOrientationableWithDirectionOptions = { defaultOrientation: 'inline-start' }; export const defaultInlineEndOrientationableWithDirectionOptions = { defaultOrientation: 'inline-end' }; export const defaultBlockStartOrientationableWithDirectionOptions = { defaultOrientation: 'block-start' }; export const defaultBlockEndOrientationableWithDirectionOptions = { defaultOrientation: 'block-end' }; export const usesOrientationableWithDirection = (options, defaultOptions = defaultBlockEndOrientationableWithDirectionOptions) => { const defaultOrientation = fallbacks(options?.defaultOrientation, defaultOptions.defaultOrientation, 'block-end'); const orientationInlineStartSelector = fallbacks(options?.orientationInlineStartSelector, defaultOptions.orientationInlineStartSelector, ((defaultOrientation === 'inline-start') ? ':not(:is(.oBlock-start, .oBlock-end, .oInline-end))' : '.oInline-start')); const orientationInlineEndSelector = fallbacks(options?.orientationInlineEndSelector, defaultOptions.orientationInlineEndSelector, ((defaultOrientation === 'inline-end') ? ':not(:is(.oBlock-start, .oBlock-end, .oInline-start))' : '.oInline-end')); const orientationBlockStartSelector = fallbacks(options?.orientationBlockStartSelector, defaultOptions.orientationBlockStartSelector, ((defaultOrientation === 'block-start') ? ':not(:is(.oInline-start, .oInline-end, .oBlock-end))' : '.oBlock-start')); const orientationBlockEndSelector = fallbacks(options?.orientationBlockEndSelector, defaultOptions.orientationBlockEndSelector, ((defaultOrientation === 'block-end') ? ':not(:is(.oInline-start, .oInline-end, .oBlock-start))' : '.oBlock-end')); return { ...options, defaultOrientation, orientationInlineStartSelector, orientationInlineEndSelector, orientationBlockStartSelector, orientationBlockEndSelector, ifOrientationInlineStart: (styles) => rule(orientationInlineStartSelector, styles), ifOrientationInlineEnd: (styles) => rule(orientationInlineEndSelector, styles), ifOrientationBlockStart: (styles) => rule(orientationBlockStartSelector, styles), ifOrientationBlockEnd: (styles) => rule(orientationBlockEndSelector, styles), ifOrientationInline: (styles) => rule([orientationInlineStartSelector, orientationInlineEndSelector], styles), ifOrientationBlock: (styles) => rule([orientationBlockStartSelector, orientationBlockEndSelector], styles), }; }; export const useOrientationableWithDirection = ({ orientation }, defaultOptions = defaultBlockEndOrientationableWithDirectionOptions) => ({ class: createOrientationClass(orientation) ?? null, get orientation() { return (orientation ?? defaultOptions.defaultOrientation ?? defaultBlockEndOrientationableWithDirectionOptions.defaultOrientation); }, }); //#endregion orientationable