@reusable-ui/nudible
Version:
Nude variant of UI.
209 lines (159 loc) • 7.04 kB
text/typescript
// 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