@allxsmith/bestax-bulma 5.45.1 → 5.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/esm/form/Checkbox.js.map +1 -1
  2. package/dist/esm/form/File.js.map +1 -1
  3. package/dist/esm/form/Numberinput.js.map +1 -1
  4. package/dist/esm/form/PinInput.js.map +1 -1
  5. package/dist/esm/form/Radio.js.map +1 -1
  6. package/dist/esm/form/SelectBase.js.map +1 -1
  7. package/dist/esm/form/Slider.js.map +1 -1
  8. package/dist/esm/form/Switch.js.map +1 -1
  9. package/dist/esm/helpers/Theme.js +191 -49
  10. package/dist/esm/helpers/Theme.js.map +1 -1
  11. package/dist/esm/helpers/useVisibilityClasses.js.map +1 -1
  12. package/dist/index.cjs +190 -49
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/types/form/Checkbox.d.ts +3 -0
  15. package/dist/types/form/File.d.ts +3 -0
  16. package/dist/types/form/Numberinput.d.ts +2 -0
  17. package/dist/types/form/PinInput.d.ts +3 -0
  18. package/dist/types/form/Radio.d.ts +3 -0
  19. package/dist/types/form/SelectBase.d.ts +3 -0
  20. package/dist/types/form/Slider.d.ts +3 -0
  21. package/dist/types/form/Switch.d.ts +2 -0
  22. package/dist/types/helpers/Theme.d.ts +37 -10
  23. package/dist/types/helpers/useVisibilityClasses.d.ts +5 -1
  24. package/dist/types-cjs/form/Checkbox.d.ts +3 -0
  25. package/dist/types-cjs/form/File.d.ts +3 -0
  26. package/dist/types-cjs/form/Numberinput.d.ts +2 -0
  27. package/dist/types-cjs/form/PinInput.d.ts +3 -0
  28. package/dist/types-cjs/form/Radio.d.ts +3 -0
  29. package/dist/types-cjs/form/SelectBase.d.ts +3 -0
  30. package/dist/types-cjs/form/Slider.d.ts +3 -0
  31. package/dist/types-cjs/form/Switch.d.ts +2 -0
  32. package/dist/types-cjs/helpers/Theme.d.ts +37 -10
  33. package/dist/types-cjs/helpers/useVisibilityClasses.d.ts +5 -1
  34. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -20954,6 +20954,21 @@ const bulmaGlobalVars = [
20954
20954
  '--bulma-danger-h',
20955
20955
  '--bulma-danger-s',
20956
20956
  '--bulma-danger-l',
20957
+ // the lightness of each color's label on a solid surface, and of its text
20958
+ // on the page, which Bulma's Sass build picks for contrast with the color
20959
+ // it was built with, so a new trio does not move them
20960
+ '--bulma-primary-invert-l',
20961
+ '--bulma-primary-on-scheme-l',
20962
+ '--bulma-link-invert-l',
20963
+ '--bulma-link-on-scheme-l',
20964
+ '--bulma-info-invert-l',
20965
+ '--bulma-info-on-scheme-l',
20966
+ '--bulma-success-invert-l',
20967
+ '--bulma-success-on-scheme-l',
20968
+ '--bulma-warning-invert-l',
20969
+ '--bulma-warning-on-scheme-l',
20970
+ '--bulma-danger-invert-l',
20971
+ '--bulma-danger-on-scheme-l',
20957
20972
  // shadow (the upstream token .box/.card/.dropdown/.panel derive their own
20958
20973
  // shadow variables from; overriding it from an ancestor is the route that
20959
20974
  // cascades, since those selectors re-declare their derived var on
@@ -21704,6 +21719,104 @@ const setRootThemeRules = (order, rules) => {
21704
21719
  }
21705
21720
  renderRootThemeRules();
21706
21721
  };
21722
+ /** A value given per scheme, rather than as one string. */
21723
+ const isSchemeValues = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
21724
+ /**
21725
+ * The selector of one of Bulma's schemes, as the stylesheet Theme is used
21726
+ * with writes it: `[data-theme=dark], .theme-dark`, or with the class prefix
21727
+ * in both names (`[data-bestax-theme=dark], .bestax-theme-dark`) for a
21728
+ * prefixed build. A prefix that cannot be written into a selector as it
21729
+ * stands gets the plain names, which `colorMode` writes under any prefix.
21730
+ */
21731
+ const schemeSelector = (classPrefix, scheme) => {
21732
+ const prefix = /^[A-Za-z_][\w-]*$/.test(classPrefix) ? classPrefix : '';
21733
+ return `[data-${prefix}theme=${scheme}], .${prefix}theme-${scheme}`;
21734
+ };
21735
+ /**
21736
+ * The `:root` rules for variables given a value per scheme, laid out the
21737
+ * way Bulma lays out its own schemes so each value applies exactly where
21738
+ * Bulma's would: under the visitor's OS preference, unless the page names
21739
+ * the other scheme, and wherever the page names this one, on `<html>` or on
21740
+ * an element inside. A scheme with no value gets no rules, which leaves the
21741
+ * stylesheet's own value there.
21742
+ *
21743
+ * Every selector is as specific as `:root` (the guard is in `:where()`), so
21744
+ * between Themes, and against a plain `:root` rule, the later one still
21745
+ * wins, as it does for every other variable.
21746
+ */
21747
+ const schemeRules = (vars, classPrefix) => {
21748
+ const other = { light: 'dark', dark: 'light' };
21749
+ const body = (scheme) => vars
21750
+ .filter(([, values]) => values[scheme])
21751
+ .map(([key, values]) => `${key}: ${values[scheme]};`)
21752
+ .join(' ');
21753
+ const schemes = ['light', 'dark']
21754
+ .map(scheme => [scheme, body(scheme)])
21755
+ .filter(([, declarations]) => declarations);
21756
+ // Bulma's order: both preferences, then both names, the dark one last.
21757
+ return [
21758
+ ...schemes.map(([scheme, declarations]) => `@media (prefers-color-scheme: ${scheme}) { :root:where(:not(` +
21759
+ `${schemeSelector(classPrefix, other[scheme])})) { ${declarations} } }`),
21760
+ ...schemes.map(([scheme, declarations]) => `${schemeSelector(classPrefix, scheme)} { ${declarations} }`),
21761
+ ];
21762
+ };
21763
+ const schemeAttributes = new Map();
21764
+ /** Set a theme attribute on `<html>`, or remove it for `null`. */
21765
+ const setSchemeAttribute = (name, value) => {
21766
+ const root = document.documentElement;
21767
+ if (value === null) {
21768
+ root.removeAttribute(name);
21769
+ }
21770
+ else {
21771
+ root.setAttribute(name, value);
21772
+ }
21773
+ };
21774
+ /**
21775
+ * Write one Theme's value for a theme attribute, `null` removing it, and
21776
+ * make that Theme the attribute's latest writer. Throws, recording nothing,
21777
+ * when `name` cannot be an attribute name.
21778
+ *
21779
+ * A Theme that writes the value it already holds keeps its place and leaves
21780
+ * the page alone. React runs a layout effect again when a `Suspense`
21781
+ * boundary shows its tree again, with nothing changed, and that must not
21782
+ * take the attribute back from a Theme that wrote after it.
21783
+ */
21784
+ const writeSchemeAttribute = (order, name, value) => {
21785
+ const held = schemeAttributes.get(name);
21786
+ if (held?.writers.some(writer => writer.order === order && writer.value === value)) {
21787
+ return;
21788
+ }
21789
+ const entry = held ?? {
21790
+ original: document.documentElement.getAttribute(name),
21791
+ writers: [],
21792
+ };
21793
+ setSchemeAttribute(name, value);
21794
+ entry.writers = entry.writers.filter(writer => writer.order !== order);
21795
+ entry.writers.push({ order, value });
21796
+ schemeAttributes.set(name, entry);
21797
+ };
21798
+ /**
21799
+ * Take one Theme off every theme attribute it wrote, except those named in
21800
+ * `keep`. An attribute it was the latest writer of goes back to the value of
21801
+ * the writer before it, or to what it held before any of them.
21802
+ */
21803
+ const releaseSchemeAttributes = (order, keep = []) => {
21804
+ for (const [name, entry] of schemeAttributes) {
21805
+ const index = entry.writers.findIndex(writer => writer.order === order);
21806
+ if (index === -1 || keep.includes(name)) {
21807
+ continue;
21808
+ }
21809
+ const latest = index === entry.writers.length - 1;
21810
+ entry.writers.splice(index, 1);
21811
+ if (entry.writers.length === 0) {
21812
+ schemeAttributes.delete(name);
21813
+ setSchemeAttribute(name, entry.original);
21814
+ }
21815
+ else if (latest) {
21816
+ setSchemeAttribute(name, entry.writers[entry.writers.length - 1].value);
21817
+ }
21818
+ }
21819
+ };
21707
21820
  /**
21708
21821
  * Theme component that injects Bulma CSS variables either globally or locally.
21709
21822
  *
@@ -21790,7 +21903,9 @@ const Theme = ({ bulmaVars = {}, children, className, isRoot = false, colorMode,
21790
21903
  });
21791
21904
  // Merge bulmaVars and individual props, with props taking precedence
21792
21905
  const mergedVars = React.useMemo(() => {
21793
- const vars = { ...bulmaVars };
21906
+ const vars = {
21907
+ ...bulmaVars,
21908
+ };
21794
21909
  for (const [propName, cssVar] of Object.entries(bulmaVarPropMap)) {
21795
21910
  if (bulmaVarProps[propName] !== undefined) {
21796
21911
  vars[cssVar] = bulmaVarProps[propName];
@@ -21804,61 +21919,99 @@ const Theme = ({ bulmaVars = {}, children, className, isRoot = false, colorMode,
21804
21919
  }
21805
21920
  return vars;
21806
21921
  }, [bulmaVars, bulmaVarProps, radiusVar, columnGapVar]);
21922
+ // The variables Theme writes, split by form: a value per scheme, or one
21923
+ // value for every scheme. A key Theme does not take, or an empty value, is
21924
+ // left out. A plain value is written as given, so a number untyped code
21925
+ // passes still works.
21926
+ const { plainVars, perSchemeVars } = React.useMemo(() => {
21927
+ const plain = [];
21928
+ const perScheme = [];
21929
+ for (const [key, value] of Object.entries(mergedVars)) {
21930
+ if (!value || !bulmaCssVars.includes(key)) {
21931
+ continue;
21932
+ }
21933
+ if (isSchemeValues(value)) {
21934
+ perScheme.push([key, value]);
21935
+ }
21936
+ else {
21937
+ plain.push([key, value]);
21938
+ }
21939
+ }
21940
+ return { plainVars: plain, perSchemeVars: perScheme };
21941
+ }, [mergedVars]);
21807
21942
  // From an effect rather than the render body, so rendering does none of
21808
21943
  // this work, and a server render none at all. In production it returns
21809
21944
  // at its development check before looking at the variables.
21810
21945
  React.useEffect(() => {
21811
21946
  warnComponentVars(mergedVars);
21812
21947
  }, [mergedVars]);
21813
- // This Theme's place among root Themes; see `rootThemeRules`.
21948
+ // A scoped Theme writes its variables as an inline style, which has no way
21949
+ // to say which scheme a value is for, so it skips a value given per
21950
+ // scheme. Say so, since nothing on the page does.
21951
+ React.useEffect(() => {
21952
+ if (isRoot || perSchemeVars.length === 0 || !isDev()) {
21953
+ return;
21954
+ }
21955
+ const keys = perSchemeVars.map(([key]) => key).sort();
21956
+ warnOnce(`Theme:scoped-scheme-values:${keys.join(',')}`, `[bestax-bulma] <Theme>: a scoped Theme writes its variables as an ` +
21957
+ `inline style, which cannot hold a value per scheme, so ` +
21958
+ `${keys.join(', ')} ${keys.length === 1 ? 'is' : 'are'} not set. ` +
21959
+ 'Give a single string, set the values on an isRoot Theme, or set ' +
21960
+ 'them in your CSS.');
21961
+ }, [isRoot, perSchemeVars]);
21962
+ // This Theme's place among the Themes on the page; see `rootThemeRules`
21963
+ // and `schemeAttributes`.
21814
21964
  const [rootOrder] = React.useState(() => nextRootOrder++);
21815
- // The `:root` rules this Theme contributes, or '' when it contributes none.
21965
+ // Bulma names its scheme attribute after the class prefix a stylesheet was
21966
+ // built with (`data-<prefix>theme`), so the prefixed builds read
21967
+ // `data-bestax-theme` and never `data-theme`. Its scheme classes take the
21968
+ // prefix too (`.bestax-theme-dark`).
21969
+ const classPrefix = useClassPrefix();
21970
+ // The `:root` rules this Theme contributes, or '' when it contributes none:
21971
+ // one block for the plain values, then the rules for each value given per
21972
+ // scheme.
21816
21973
  const rootRules = React.useMemo(() => {
21817
21974
  if (!isRoot) {
21818
21975
  return '';
21819
21976
  }
21820
- const cssRules = Object.entries(mergedVars)
21821
- .filter(([key, value]) => bulmaCssVars.includes(key) && value)
21822
- .map(([key, value]) => `${key}: ${value};`)
21823
- .join(' ');
21824
- return cssRules ? `:root { ${cssRules} }` : '';
21825
- }, [mergedVars, isRoot]);
21977
+ const plain = plainVars.map(([key, value]) => `${key}: ${value};`);
21978
+ return [
21979
+ ...(plain.length > 0 ? [`:root { ${plain.join(' ')} }`] : []),
21980
+ ...schemeRules(perSchemeVars, classPrefix),
21981
+ ].join('\n');
21982
+ }, [plainVars, perSchemeVars, classPrefix, isRoot]);
21826
21983
  // Inject CSS variables globally at :root level, alongside any other root
21827
21984
  // Themes. Clearing `isRoot` or every variable passes '', which withdraws
21828
- // this Theme's rules.
21829
- React.useEffect(() => {
21985
+ // this Theme's rules. A layout effect, so they are in place before the
21986
+ // browser paints what this Theme rendered.
21987
+ useClientLayoutEffect(() => {
21830
21988
  setRootThemeRules(rootOrder, rootRules);
21831
21989
  }, [rootOrder, rootRules]);
21832
21990
  // Withdraw them on unmount. Kept apart from the effect above so a change
21833
21991
  // rewrites the shared element in place rather than removing and recreating
21834
- // it between the cleanup and the next run.
21992
+ // it between the cleanup and the next run, and passive, so a Theme that a
21993
+ // `Suspense` fallback hides keeps its variables meanwhile: React cleans up
21994
+ // a hidden tree's layout effects, but not its passive ones.
21835
21995
  React.useEffect(() => () => setRootThemeRules(rootOrder, ''), [rootOrder]);
21836
- // Bulma names its scheme attribute after the class prefix a stylesheet was
21837
- // built with (`data-<prefix>theme`), so the prefixed builds read
21838
- // `data-bestax-theme` and never `data-theme`.
21839
- const classPrefix = useClassPrefix();
21840
21996
  // Toggle Bulma's light/dark scheme by writing the theme attribute on the
21841
21997
  // document root (<html>). This is always global, even on a scoped Theme.
21842
21998
  // Under a class prefix the prefixed attribute is written as well as
21843
21999
  // `data-theme`, so whichever build is loaded sees it; with no prefix only
21844
22000
  // `data-theme` is. `'system'` removes them so Bulma follows the OS
21845
- // preference.
21846
- React.useEffect(() => {
21847
- if (colorMode === undefined) {
21848
- return;
21849
- }
21850
- const root = document.documentElement;
21851
- const names = classPrefix
21852
- ? ['data-theme', `data-${classPrefix}theme`]
21853
- : ['data-theme'];
21854
- const previous = names.map(name => root.getAttribute(name));
22001
+ // preference. Written before the browser paints, and handed back on
22002
+ // unmount, for the reasons the two effects above give; `schemeAttributes`
22003
+ // keeps the two in step.
22004
+ useClientLayoutEffect(() => {
22005
+ const names = colorMode === undefined
22006
+ ? []
22007
+ : classPrefix
22008
+ ? ['data-theme', `data-${classPrefix}theme`]
22009
+ : ['data-theme'];
22010
+ const value = colorMode === undefined || colorMode === 'system' ? null : colorMode;
22011
+ releaseSchemeAttributes(rootOrder, names);
21855
22012
  for (const name of names) {
21856
- if (colorMode === 'system') {
21857
- root.removeAttribute(name);
21858
- continue;
21859
- }
21860
22013
  try {
21861
- root.setAttribute(name, colorMode);
22014
+ writeSchemeAttribute(rootOrder, name, value);
21862
22015
  }
21863
22016
  catch {
21864
22017
  // A class prefix can hold characters an attribute name cannot.
@@ -21871,32 +22024,20 @@ const Theme = ({ bulmaVars = {}, children, className, isRoot = false, colorMode,
21871
22024
  'with that prefix keeps its scheme.');
21872
22025
  }
21873
22026
  }
21874
- // Restore the previous values when colorMode changes or the component unmounts.
21875
- return () => {
21876
- names.forEach((name, i) => {
21877
- const value = previous[i];
21878
- if (value === null) {
21879
- root.removeAttribute(name);
21880
- }
21881
- else {
21882
- root.setAttribute(name, value);
21883
- }
21884
- });
21885
- };
21886
- }, [colorMode, classPrefix]);
22027
+ }, [colorMode, classPrefix, rootOrder]);
22028
+ // Hand the attributes back on unmount.
22029
+ React.useEffect(() => () => releaseSchemeAttributes(rootOrder), [rootOrder]);
21887
22030
  // For local injection (when isRoot is false), prepare style object for CSS vars
21888
22031
  const style = React.useMemo(() => {
21889
22032
  if (isRoot) {
21890
22033
  return {};
21891
22034
  }
21892
22035
  const styleObj = {};
21893
- for (const [key, value] of Object.entries(mergedVars)) {
21894
- if (bulmaCssVars.includes(key) && value) {
21895
- styleObj[key] = value;
21896
- }
22036
+ for (const [key, value] of plainVars) {
22037
+ styleObj[key] = value;
21897
22038
  }
21898
22039
  return styleObj;
21899
- }, [mergedVars, isRoot]);
22040
+ }, [plainVars, isRoot]);
21900
22041
  // Generate combined class names for the wrapper div
21901
22042
  const combinedClassName = React.useMemo(() => {
21902
22043
  if (isRoot) {