UNPKG

@reusable-ui/nudible

Version:
209 lines (159 loc) 7.04 kB
// cssfn: import { // cssfn general types: Factory, // cssfn css specific types: CssRule, CssStyleCollection, // writes css in javascript: rule, variants, style, vars, // strongly typed of css variables: CssVars, cssVars, // writes complex stylesheets in simpler way: memoizeStyle, memoizeStyleWithVariants, } from '@cssfn/core' // writes css in javascript // reusable-ui features: import { // hooks: usesBorder, } from '@reusable-ui/border' // border (stroke) stuff of UI import { // hooks: usesPadding, } from '@reusable-ui/padding' // padding (inner spacing) stuff of UI // defaults: const _defaultNude : Required<NudibleProps>['nude'] = false // hooks: // variants: //#region nudible export type ToggleNude = boolean|null export interface NudibleVars { /** * the nudible preference. */ nudePr : any /** * the nude switching function. */ nudeSw : any } const [nudibleVars] = cssVars<NudibleVars>({ prefix: 'nu', minify: false }); // shared variables: ensures the server-side & client-side have the same generated css variable names // parent is `.nude` -or- current is `.nude`: export const ifNude = (styles: CssStyleCollection): CssRule => rule( ':is(.nude&, &.nude)' , styles); // specificityWeight = 1 + (parent's specificityWeight) // parent is not `.nude` -and- current is not `.nude`: export const ifNotNude = (styles: CssStyleCollection): CssRule => rule(':where(&):not(:is(.nude&, &.nude))', styles); // specificityWeight = 1 + (parent's specificityWeight) export interface NudibleStuff { nudibleRule: Factory<CssRule>, nudibleVars: CssVars<NudibleVars> } const createNudibleRule = (nudeDefinition : null|((toggle: ToggleNude) => CssStyleCollection) = defineNude): CssRule => { // dependencies: // features: const {borderVars } = usesBorder(); const {paddingVars} = usesPadding(); return style({ // configs: ...vars({ /* supports for `usesColorable()`: only reset `nudibleVars.nudeSw = nudibleVars.nudePr` if `nudeDefinition` provided, so the *modified* `nudibleVars.nudeSw` by `setNude()` still *preserved*, thus the `usesColorable()` can see the <parent>'s actual [nude] status. */ [nudibleVars.nudeSw] : (nudeDefinition || undefined) && nudibleVars.nudePr, }), // toggling props: ...variants([ ifNude({ // backgrounds: // discard background, no valid/invalid animation: backg : [['none'], '!important'], // borders: // discard border stroke: [borderVars.borderWidth ] : '0px', // discard borderRadius: // remove rounded corners on top: [borderVars.borderStartStartRadius] : '0px', [borderVars.borderStartEndRadius ] : '0px', // remove rounded corners on bottom: [borderVars.borderEndStartRadius ] : '0px', [borderVars.borderEndEndRadius ] : '0px', // no shadow & no focus animation: boxShadow : [['none'], '!important'], // spacings: // discard padding: [paddingVars.paddingInline] : '0px', [paddingVars.paddingBlock ] : '0px', }), ]), // toggling conditions: ...variants([ nudeDefinition && ifNude(nudeDefinition(true)), nudeDefinition && ifNotNude(nudeDefinition(false)), ]), }); }; const getDefaultNudibleRule = memoizeStyle(() => createNudibleRule()); /** * Uses a toggleable nudeification (removes background, border & padding). * @param nudeDefinition A callback to create a nudeification rules for each toggle state. * @returns A `NudibleStuff` represents the nudeification rules. */ export const usesNudible = (nudeDefinition : null|((toggle: ToggleNude) => CssStyleCollection) = defineNude): NudibleStuff => { return { nudibleRule: ( (nudeDefinition === defineNude) ? getDefaultNudibleRule : () => createNudibleRule(nudeDefinition) ), nudibleVars, }; }; /** * Defines a nudeification preference rules for the given `toggle` state. * @param toggle `true` to activate the nudeification -or- `false` to deactivate -or- `null` to remove previously declared `defineNude`. * @returns A `CssRule` represents a nudeification rules for the given `toggle` state. */ export const defineNude = memoizeStyleWithVariants((toggle: ToggleNude): CssRule => { return style({ ...vars({ /* *switch on/off* the `**Tg` prop. toggle: true => empty string => do not alter the `**Tg`'s value => activates `**Tg` variable. false => initial (invalid) => destroy the `**Tg`'s value => deactivates `**Tg` variable. null => null => remove the prev declaration */ [nudibleVars.nudePr] : (typeof(toggle) === 'boolean') ? (toggle ? '' : 'initial') : toggle, }), }); }); /** * Sets the current nudeification state by the given `toggle` state. * @param toggle `true` to activate the nudeification -or- `false` to deactivate -or- `null` to remove previously declared `setNude`. * @returns A `CssRule` represents a nudeification rules for the given `toggle` state. */ export const setNude = (toggle: ToggleNude): CssRule => style({ ...vars({ /* *switch on/off* the `**Tg` prop. toggle: true => empty string => do not alter the `**Tg`'s value => activates `**Tg` variable. false => initial (invalid) => destroy the `**Tg`'s value => deactivates `**Tg` variable. null => null => remove the prev declaration */ [nudibleVars.nudeSw] : (typeof(toggle) === 'boolean') ? (toggle ? '' : 'initial') : toggle, }), }); export interface NudibleProps { // variants: nude ?: boolean } export const useNudible = ({nude = _defaultNude}: NudibleProps) => ({ class: (nude || null) && 'nude', }); //#endregion nudible