@allxsmith/bestax-bulma 5.45.0 → 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.
- package/dist/esm/components/Modal.js +20 -7
- package/dist/esm/components/Modal.js.map +1 -1
- package/dist/esm/form/Checkbox.js.map +1 -1
- package/dist/esm/form/File.js.map +1 -1
- package/dist/esm/form/Numberinput.js.map +1 -1
- package/dist/esm/form/PinInput.js.map +1 -1
- package/dist/esm/form/Radio.js.map +1 -1
- package/dist/esm/form/SelectBase.js.map +1 -1
- package/dist/esm/form/Slider.js.map +1 -1
- package/dist/esm/form/Switch.js.map +1 -1
- package/dist/esm/helpers/Theme.js +191 -49
- package/dist/esm/helpers/Theme.js.map +1 -1
- package/dist/esm/helpers/useVisibilityClasses.js.map +1 -1
- package/dist/index.cjs +209 -55
- package/dist/index.cjs.map +1 -1
- package/dist/types/components/Modal.d.ts +10 -1
- package/dist/types/form/Checkbox.d.ts +3 -0
- package/dist/types/form/File.d.ts +3 -0
- package/dist/types/form/Numberinput.d.ts +2 -0
- package/dist/types/form/PinInput.d.ts +3 -0
- package/dist/types/form/Radio.d.ts +3 -0
- package/dist/types/form/SelectBase.d.ts +3 -0
- package/dist/types/form/Slider.d.ts +3 -0
- package/dist/types/form/Switch.d.ts +2 -0
- package/dist/types/helpers/Theme.d.ts +37 -10
- package/dist/types/helpers/useVisibilityClasses.d.ts +5 -1
- package/dist/types-cjs/components/Modal.d.ts +10 -1
- package/dist/types-cjs/form/Checkbox.d.ts +3 -0
- package/dist/types-cjs/form/File.d.ts +3 -0
- package/dist/types-cjs/form/Numberinput.d.ts +2 -0
- package/dist/types-cjs/form/PinInput.d.ts +3 -0
- package/dist/types-cjs/form/Radio.d.ts +3 -0
- package/dist/types-cjs/form/SelectBase.d.ts +3 -0
- package/dist/types-cjs/form/Slider.d.ts +3 -0
- package/dist/types-cjs/form/Switch.d.ts +2 -0
- package/dist/types-cjs/helpers/Theme.d.ts +37 -10
- package/dist/types-cjs/helpers/useVisibilityClasses.d.ts +5 -1
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import { useBulmaClasses } from './useBulmaClasses.js';
|
|
|
6
6
|
import { validRadii, validGaps } from './bulmaClassHelpers.js';
|
|
7
7
|
import { warnOnce, isDev } from './devWarnings.js';
|
|
8
8
|
import { useClassPrefix } from './Config.js';
|
|
9
|
+
import { useClientLayoutEffect } from './useClientLayoutEffect.js';
|
|
9
10
|
|
|
10
11
|
// --- Bulma v1 CSS variables Theme sets (originally generated from
|
|
11
12
|
// CSSVAR_KEYS), in two lists by where Bulma declares them ---
|
|
@@ -60,6 +61,21 @@ const bulmaGlobalVars = [
|
|
|
60
61
|
'--bulma-danger-h',
|
|
61
62
|
'--bulma-danger-s',
|
|
62
63
|
'--bulma-danger-l',
|
|
64
|
+
// the lightness of each color's label on a solid surface, and of its text
|
|
65
|
+
// on the page, which Bulma's Sass build picks for contrast with the color
|
|
66
|
+
// it was built with, so a new trio does not move them
|
|
67
|
+
'--bulma-primary-invert-l',
|
|
68
|
+
'--bulma-primary-on-scheme-l',
|
|
69
|
+
'--bulma-link-invert-l',
|
|
70
|
+
'--bulma-link-on-scheme-l',
|
|
71
|
+
'--bulma-info-invert-l',
|
|
72
|
+
'--bulma-info-on-scheme-l',
|
|
73
|
+
'--bulma-success-invert-l',
|
|
74
|
+
'--bulma-success-on-scheme-l',
|
|
75
|
+
'--bulma-warning-invert-l',
|
|
76
|
+
'--bulma-warning-on-scheme-l',
|
|
77
|
+
'--bulma-danger-invert-l',
|
|
78
|
+
'--bulma-danger-on-scheme-l',
|
|
63
79
|
// shadow (the upstream token .box/.card/.dropdown/.panel derive their own
|
|
64
80
|
// shadow variables from; overriding it from an ancestor is the route that
|
|
65
81
|
// cascades, since those selectors re-declare their derived var on
|
|
@@ -810,6 +826,104 @@ const setRootThemeRules = (order, rules) => {
|
|
|
810
826
|
}
|
|
811
827
|
renderRootThemeRules();
|
|
812
828
|
};
|
|
829
|
+
/** A value given per scheme, rather than as one string. */
|
|
830
|
+
const isSchemeValues = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
831
|
+
/**
|
|
832
|
+
* The selector of one of Bulma's schemes, as the stylesheet Theme is used
|
|
833
|
+
* with writes it: `[data-theme=dark], .theme-dark`, or with the class prefix
|
|
834
|
+
* in both names (`[data-bestax-theme=dark], .bestax-theme-dark`) for a
|
|
835
|
+
* prefixed build. A prefix that cannot be written into a selector as it
|
|
836
|
+
* stands gets the plain names, which `colorMode` writes under any prefix.
|
|
837
|
+
*/
|
|
838
|
+
const schemeSelector = (classPrefix, scheme) => {
|
|
839
|
+
const prefix = /^[A-Za-z_][\w-]*$/.test(classPrefix) ? classPrefix : '';
|
|
840
|
+
return `[data-${prefix}theme=${scheme}], .${prefix}theme-${scheme}`;
|
|
841
|
+
};
|
|
842
|
+
/**
|
|
843
|
+
* The `:root` rules for variables given a value per scheme, laid out the
|
|
844
|
+
* way Bulma lays out its own schemes so each value applies exactly where
|
|
845
|
+
* Bulma's would: under the visitor's OS preference, unless the page names
|
|
846
|
+
* the other scheme, and wherever the page names this one, on `<html>` or on
|
|
847
|
+
* an element inside. A scheme with no value gets no rules, which leaves the
|
|
848
|
+
* stylesheet's own value there.
|
|
849
|
+
*
|
|
850
|
+
* Every selector is as specific as `:root` (the guard is in `:where()`), so
|
|
851
|
+
* between Themes, and against a plain `:root` rule, the later one still
|
|
852
|
+
* wins, as it does for every other variable.
|
|
853
|
+
*/
|
|
854
|
+
const schemeRules = (vars, classPrefix) => {
|
|
855
|
+
const other = { light: 'dark', dark: 'light' };
|
|
856
|
+
const body = (scheme) => vars
|
|
857
|
+
.filter(([, values]) => values[scheme])
|
|
858
|
+
.map(([key, values]) => `${key}: ${values[scheme]};`)
|
|
859
|
+
.join(' ');
|
|
860
|
+
const schemes = ['light', 'dark']
|
|
861
|
+
.map(scheme => [scheme, body(scheme)])
|
|
862
|
+
.filter(([, declarations]) => declarations);
|
|
863
|
+
// Bulma's order: both preferences, then both names, the dark one last.
|
|
864
|
+
return [
|
|
865
|
+
...schemes.map(([scheme, declarations]) => `@media (prefers-color-scheme: ${scheme}) { :root:where(:not(` +
|
|
866
|
+
`${schemeSelector(classPrefix, other[scheme])})) { ${declarations} } }`),
|
|
867
|
+
...schemes.map(([scheme, declarations]) => `${schemeSelector(classPrefix, scheme)} { ${declarations} }`),
|
|
868
|
+
];
|
|
869
|
+
};
|
|
870
|
+
const schemeAttributes = new Map();
|
|
871
|
+
/** Set a theme attribute on `<html>`, or remove it for `null`. */
|
|
872
|
+
const setSchemeAttribute = (name, value) => {
|
|
873
|
+
const root = document.documentElement;
|
|
874
|
+
if (value === null) {
|
|
875
|
+
root.removeAttribute(name);
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
root.setAttribute(name, value);
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
/**
|
|
882
|
+
* Write one Theme's value for a theme attribute, `null` removing it, and
|
|
883
|
+
* make that Theme the attribute's latest writer. Throws, recording nothing,
|
|
884
|
+
* when `name` cannot be an attribute name.
|
|
885
|
+
*
|
|
886
|
+
* A Theme that writes the value it already holds keeps its place and leaves
|
|
887
|
+
* the page alone. React runs a layout effect again when a `Suspense`
|
|
888
|
+
* boundary shows its tree again, with nothing changed, and that must not
|
|
889
|
+
* take the attribute back from a Theme that wrote after it.
|
|
890
|
+
*/
|
|
891
|
+
const writeSchemeAttribute = (order, name, value) => {
|
|
892
|
+
const held = schemeAttributes.get(name);
|
|
893
|
+
if (held?.writers.some(writer => writer.order === order && writer.value === value)) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
const entry = held ?? {
|
|
897
|
+
original: document.documentElement.getAttribute(name),
|
|
898
|
+
writers: [],
|
|
899
|
+
};
|
|
900
|
+
setSchemeAttribute(name, value);
|
|
901
|
+
entry.writers = entry.writers.filter(writer => writer.order !== order);
|
|
902
|
+
entry.writers.push({ order, value });
|
|
903
|
+
schemeAttributes.set(name, entry);
|
|
904
|
+
};
|
|
905
|
+
/**
|
|
906
|
+
* Take one Theme off every theme attribute it wrote, except those named in
|
|
907
|
+
* `keep`. An attribute it was the latest writer of goes back to the value of
|
|
908
|
+
* the writer before it, or to what it held before any of them.
|
|
909
|
+
*/
|
|
910
|
+
const releaseSchemeAttributes = (order, keep = []) => {
|
|
911
|
+
for (const [name, entry] of schemeAttributes) {
|
|
912
|
+
const index = entry.writers.findIndex(writer => writer.order === order);
|
|
913
|
+
if (index === -1 || keep.includes(name)) {
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
const latest = index === entry.writers.length - 1;
|
|
917
|
+
entry.writers.splice(index, 1);
|
|
918
|
+
if (entry.writers.length === 0) {
|
|
919
|
+
schemeAttributes.delete(name);
|
|
920
|
+
setSchemeAttribute(name, entry.original);
|
|
921
|
+
}
|
|
922
|
+
else if (latest) {
|
|
923
|
+
setSchemeAttribute(name, entry.writers[entry.writers.length - 1].value);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
};
|
|
813
927
|
/**
|
|
814
928
|
* Theme component that injects Bulma CSS variables either globally or locally.
|
|
815
929
|
*
|
|
@@ -896,7 +1010,9 @@ const Theme = ({ bulmaVars = {}, children, className, isRoot = false, colorMode,
|
|
|
896
1010
|
});
|
|
897
1011
|
// Merge bulmaVars and individual props, with props taking precedence
|
|
898
1012
|
const mergedVars = useMemo(() => {
|
|
899
|
-
const vars = {
|
|
1013
|
+
const vars = {
|
|
1014
|
+
...bulmaVars,
|
|
1015
|
+
};
|
|
900
1016
|
for (const [propName, cssVar] of Object.entries(bulmaVarPropMap)) {
|
|
901
1017
|
if (bulmaVarProps[propName] !== undefined) {
|
|
902
1018
|
vars[cssVar] = bulmaVarProps[propName];
|
|
@@ -910,61 +1026,99 @@ const Theme = ({ bulmaVars = {}, children, className, isRoot = false, colorMode,
|
|
|
910
1026
|
}
|
|
911
1027
|
return vars;
|
|
912
1028
|
}, [bulmaVars, bulmaVarProps, radiusVar, columnGapVar]);
|
|
1029
|
+
// The variables Theme writes, split by form: a value per scheme, or one
|
|
1030
|
+
// value for every scheme. A key Theme does not take, or an empty value, is
|
|
1031
|
+
// left out. A plain value is written as given, so a number untyped code
|
|
1032
|
+
// passes still works.
|
|
1033
|
+
const { plainVars, perSchemeVars } = useMemo(() => {
|
|
1034
|
+
const plain = [];
|
|
1035
|
+
const perScheme = [];
|
|
1036
|
+
for (const [key, value] of Object.entries(mergedVars)) {
|
|
1037
|
+
if (!value || !bulmaCssVars.includes(key)) {
|
|
1038
|
+
continue;
|
|
1039
|
+
}
|
|
1040
|
+
if (isSchemeValues(value)) {
|
|
1041
|
+
perScheme.push([key, value]);
|
|
1042
|
+
}
|
|
1043
|
+
else {
|
|
1044
|
+
plain.push([key, value]);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
return { plainVars: plain, perSchemeVars: perScheme };
|
|
1048
|
+
}, [mergedVars]);
|
|
913
1049
|
// From an effect rather than the render body, so rendering does none of
|
|
914
1050
|
// this work, and a server render none at all. In production it returns
|
|
915
1051
|
// at its development check before looking at the variables.
|
|
916
1052
|
useEffect(() => {
|
|
917
1053
|
warnComponentVars(mergedVars);
|
|
918
1054
|
}, [mergedVars]);
|
|
919
|
-
//
|
|
1055
|
+
// A scoped Theme writes its variables as an inline style, which has no way
|
|
1056
|
+
// to say which scheme a value is for, so it skips a value given per
|
|
1057
|
+
// scheme. Say so, since nothing on the page does.
|
|
1058
|
+
useEffect(() => {
|
|
1059
|
+
if (isRoot || perSchemeVars.length === 0 || !isDev()) {
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
const keys = perSchemeVars.map(([key]) => key).sort();
|
|
1063
|
+
warnOnce(`Theme:scoped-scheme-values:${keys.join(',')}`, `[bestax-bulma] <Theme>: a scoped Theme writes its variables as an ` +
|
|
1064
|
+
`inline style, which cannot hold a value per scheme, so ` +
|
|
1065
|
+
`${keys.join(', ')} ${keys.length === 1 ? 'is' : 'are'} not set. ` +
|
|
1066
|
+
'Give a single string, set the values on an isRoot Theme, or set ' +
|
|
1067
|
+
'them in your CSS.');
|
|
1068
|
+
}, [isRoot, perSchemeVars]);
|
|
1069
|
+
// This Theme's place among the Themes on the page; see `rootThemeRules`
|
|
1070
|
+
// and `schemeAttributes`.
|
|
920
1071
|
const [rootOrder] = useState(() => nextRootOrder++);
|
|
921
|
-
//
|
|
1072
|
+
// Bulma names its scheme attribute after the class prefix a stylesheet was
|
|
1073
|
+
// built with (`data-<prefix>theme`), so the prefixed builds read
|
|
1074
|
+
// `data-bestax-theme` and never `data-theme`. Its scheme classes take the
|
|
1075
|
+
// prefix too (`.bestax-theme-dark`).
|
|
1076
|
+
const classPrefix = useClassPrefix();
|
|
1077
|
+
// The `:root` rules this Theme contributes, or '' when it contributes none:
|
|
1078
|
+
// one block for the plain values, then the rules for each value given per
|
|
1079
|
+
// scheme.
|
|
922
1080
|
const rootRules = useMemo(() => {
|
|
923
1081
|
if (!isRoot) {
|
|
924
1082
|
return '';
|
|
925
1083
|
}
|
|
926
|
-
const
|
|
927
|
-
|
|
928
|
-
.
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
}, [
|
|
1084
|
+
const plain = plainVars.map(([key, value]) => `${key}: ${value};`);
|
|
1085
|
+
return [
|
|
1086
|
+
...(plain.length > 0 ? [`:root { ${plain.join(' ')} }`] : []),
|
|
1087
|
+
...schemeRules(perSchemeVars, classPrefix),
|
|
1088
|
+
].join('\n');
|
|
1089
|
+
}, [plainVars, perSchemeVars, classPrefix, isRoot]);
|
|
932
1090
|
// Inject CSS variables globally at :root level, alongside any other root
|
|
933
1091
|
// Themes. Clearing `isRoot` or every variable passes '', which withdraws
|
|
934
|
-
// this Theme's rules.
|
|
935
|
-
|
|
1092
|
+
// this Theme's rules. A layout effect, so they are in place before the
|
|
1093
|
+
// browser paints what this Theme rendered.
|
|
1094
|
+
useClientLayoutEffect(() => {
|
|
936
1095
|
setRootThemeRules(rootOrder, rootRules);
|
|
937
1096
|
}, [rootOrder, rootRules]);
|
|
938
1097
|
// Withdraw them on unmount. Kept apart from the effect above so a change
|
|
939
1098
|
// rewrites the shared element in place rather than removing and recreating
|
|
940
|
-
// it between the cleanup and the next run
|
|
1099
|
+
// it between the cleanup and the next run, and passive, so a Theme that a
|
|
1100
|
+
// `Suspense` fallback hides keeps its variables meanwhile: React cleans up
|
|
1101
|
+
// a hidden tree's layout effects, but not its passive ones.
|
|
941
1102
|
useEffect(() => () => setRootThemeRules(rootOrder, ''), [rootOrder]);
|
|
942
|
-
// Bulma names its scheme attribute after the class prefix a stylesheet was
|
|
943
|
-
// built with (`data-<prefix>theme`), so the prefixed builds read
|
|
944
|
-
// `data-bestax-theme` and never `data-theme`.
|
|
945
|
-
const classPrefix = useClassPrefix();
|
|
946
1103
|
// Toggle Bulma's light/dark scheme by writing the theme attribute on the
|
|
947
1104
|
// document root (<html>). This is always global, even on a scoped Theme.
|
|
948
1105
|
// Under a class prefix the prefixed attribute is written as well as
|
|
949
1106
|
// `data-theme`, so whichever build is loaded sees it; with no prefix only
|
|
950
1107
|
// `data-theme` is. `'system'` removes them so Bulma follows the OS
|
|
951
|
-
// preference.
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
const
|
|
1108
|
+
// preference. Written before the browser paints, and handed back on
|
|
1109
|
+
// unmount, for the reasons the two effects above give; `schemeAttributes`
|
|
1110
|
+
// keeps the two in step.
|
|
1111
|
+
useClientLayoutEffect(() => {
|
|
1112
|
+
const names = colorMode === undefined
|
|
1113
|
+
? []
|
|
1114
|
+
: classPrefix
|
|
1115
|
+
? ['data-theme', `data-${classPrefix}theme`]
|
|
1116
|
+
: ['data-theme'];
|
|
1117
|
+
const value = colorMode === undefined || colorMode === 'system' ? null : colorMode;
|
|
1118
|
+
releaseSchemeAttributes(rootOrder, names);
|
|
961
1119
|
for (const name of names) {
|
|
962
|
-
if (colorMode === 'system') {
|
|
963
|
-
root.removeAttribute(name);
|
|
964
|
-
continue;
|
|
965
|
-
}
|
|
966
1120
|
try {
|
|
967
|
-
|
|
1121
|
+
writeSchemeAttribute(rootOrder, name, value);
|
|
968
1122
|
}
|
|
969
1123
|
catch {
|
|
970
1124
|
// A class prefix can hold characters an attribute name cannot.
|
|
@@ -977,32 +1131,20 @@ const Theme = ({ bulmaVars = {}, children, className, isRoot = false, colorMode,
|
|
|
977
1131
|
'with that prefix keeps its scheme.');
|
|
978
1132
|
}
|
|
979
1133
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
const value = previous[i];
|
|
984
|
-
if (value === null) {
|
|
985
|
-
root.removeAttribute(name);
|
|
986
|
-
}
|
|
987
|
-
else {
|
|
988
|
-
root.setAttribute(name, value);
|
|
989
|
-
}
|
|
990
|
-
});
|
|
991
|
-
};
|
|
992
|
-
}, [colorMode, classPrefix]);
|
|
1134
|
+
}, [colorMode, classPrefix, rootOrder]);
|
|
1135
|
+
// Hand the attributes back on unmount.
|
|
1136
|
+
useEffect(() => () => releaseSchemeAttributes(rootOrder), [rootOrder]);
|
|
993
1137
|
// For local injection (when isRoot is false), prepare style object for CSS vars
|
|
994
1138
|
const style = useMemo(() => {
|
|
995
1139
|
if (isRoot) {
|
|
996
1140
|
return {};
|
|
997
1141
|
}
|
|
998
1142
|
const styleObj = {};
|
|
999
|
-
for (const [key, value] of
|
|
1000
|
-
|
|
1001
|
-
styleObj[key] = value;
|
|
1002
|
-
}
|
|
1143
|
+
for (const [key, value] of plainVars) {
|
|
1144
|
+
styleObj[key] = value;
|
|
1003
1145
|
}
|
|
1004
1146
|
return styleObj;
|
|
1005
|
-
}, [
|
|
1147
|
+
}, [plainVars, isRoot]);
|
|
1006
1148
|
// Generate combined class names for the wrapper div
|
|
1007
1149
|
const combinedClassName = useMemo(() => {
|
|
1008
1150
|
if (isRoot) {
|