@reusable-ui/orientationable
Version:
A capability of UI to rotate its layout.
153 lines (152 loc) • 8.61 kB
JavaScript
// 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