@allxsmith/bestax-bulma 5.57.5 → 5.59.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/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/components/Avatars.js +2 -2
- package/dist/esm/components/Avatars.js.map +1 -1
- package/dist/esm/components/Badge.js +2 -2
- package/dist/esm/components/Badge.js.map +1 -1
- package/dist/esm/components/Pagination.js +2 -2
- package/dist/esm/components/Pagination.js.map +1 -1
- package/dist/esm/components/Steps.js +2 -2
- package/dist/esm/components/Steps.js.map +1 -1
- package/dist/esm/components/TreeView.js +7 -5
- package/dist/esm/components/TreeView.js.map +1 -1
- package/dist/esm/components/useDataTable.js +2 -2
- package/dist/esm/components/useDataTable.js.map +1 -1
- package/dist/esm/elements/Buttons.js +2 -0
- package/dist/esm/elements/Buttons.js.map +1 -1
- package/dist/esm/elements/IconButton.js +114 -0
- package/dist/esm/elements/IconButton.js.map +1 -0
- package/dist/esm/elements/NumberFormatter.js +42 -0
- package/dist/esm/elements/NumberFormatter.js.map +1 -0
- package/dist/esm/elements/RingProgress.js +2 -2
- package/dist/esm/elements/RingProgress.js.map +1 -1
- package/dist/esm/form/Dropzone.js +2 -2
- package/dist/esm/form/Dropzone.js.map +1 -1
- package/dist/esm/form/File.js +2 -2
- package/dist/esm/form/File.js.map +1 -1
- package/dist/esm/form/Rate.js +2 -2
- package/dist/esm/form/Rate.js.map +1 -1
- package/dist/esm/form/Slider.js +2 -2
- package/dist/esm/form/Slider.js.map +1 -1
- package/dist/esm/form/Taginput.js +2 -2
- package/dist/esm/form/Taginput.js.map +1 -1
- package/dist/esm/form/_colorInternals/pickerState.js +2 -2
- package/dist/esm/form/_colorInternals/pickerState.js.map +1 -1
- package/dist/esm/helpers/LocaleProvider.js +13 -6
- package/dist/esm/helpers/LocaleProvider.js.map +1 -1
- package/dist/esm/helpers/containerBreakpoints.js +14 -0
- package/dist/esm/helpers/containerBreakpoints.js.map +1 -0
- package/dist/esm/helpers/localeFormat.js +196 -36
- package/dist/esm/helpers/localeFormat.js.map +1 -1
- package/dist/esm/helpers/polymorphic.js.map +1 -1
- package/dist/esm/helpers/stackLayout.js +30 -0
- package/dist/esm/helpers/stackLayout.js.map +1 -0
- package/dist/esm/helpers/useBulmaClasses.js +8 -2
- package/dist/esm/helpers/useBulmaClasses.js.map +1 -1
- package/dist/esm/helpers/useFlexboxClasses.js +17 -4
- package/dist/esm/helpers/useFlexboxClasses.js.map +1 -1
- package/dist/esm/helpers/useTreeView.js +54 -20
- package/dist/esm/helpers/useTreeView.js.map +1 -1
- package/dist/esm/layout/Center.js +33 -0
- package/dist/esm/layout/Center.js.map +1 -0
- package/dist/esm/layout/Container.js +2 -6
- package/dist/esm/layout/Container.js.map +1 -1
- package/dist/esm/layout/Flex.js +34 -0
- package/dist/esm/layout/Flex.js.map +1 -0
- package/dist/esm/layout/Group.js +43 -0
- package/dist/esm/layout/Group.js.map +1 -0
- package/dist/esm/layout/Stack.js +34 -0
- package/dist/esm/layout/Stack.js.map +1 -0
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +712 -212
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +7 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/types/components/TreeView.d.ts +20 -11
- package/dist/types/elements/Buttons.d.ts +1 -0
- package/dist/types/elements/IconButton.d.ts +110 -0
- package/dist/types/elements/NumberFormatter.d.ts +62 -0
- package/dist/types/helpers/LocaleProvider.d.ts +7 -0
- package/dist/types/helpers/containerBreakpoints.d.ts +2 -0
- package/dist/types/helpers/localeFormat.d.ts +30 -8
- package/dist/types/helpers/polymorphic.d.ts +19 -0
- package/dist/types/helpers/stackLayout.d.ts +62 -0
- package/dist/types/helpers/useBulmaClasses.d.ts +8 -2
- package/dist/types/helpers/useFlexboxClasses.d.ts +26 -3
- package/dist/types/helpers/useTreeView.d.ts +12 -6
- package/dist/types/index.d.ts +10 -1
- package/dist/types/layout/Center.d.ts +54 -0
- package/dist/types/layout/Flex.d.ts +92 -0
- package/dist/types/layout/Group.d.ts +86 -0
- package/dist/types/layout/Stack.d.ts +63 -0
- package/dist/types-cjs/components/TreeView.d.ts +20 -11
- package/dist/types-cjs/elements/Buttons.d.ts +1 -0
- package/dist/types-cjs/elements/IconButton.d.ts +110 -0
- package/dist/types-cjs/elements/NumberFormatter.d.ts +62 -0
- package/dist/types-cjs/helpers/LocaleProvider.d.ts +7 -0
- package/dist/types-cjs/helpers/containerBreakpoints.d.ts +2 -0
- package/dist/types-cjs/helpers/localeFormat.d.ts +30 -8
- package/dist/types-cjs/helpers/polymorphic.d.ts +19 -0
- package/dist/types-cjs/helpers/stackLayout.d.ts +62 -0
- package/dist/types-cjs/helpers/useBulmaClasses.d.ts +8 -2
- package/dist/types-cjs/helpers/useFlexboxClasses.d.ts +26 -3
- package/dist/types-cjs/helpers/useTreeView.d.ts +12 -6
- package/dist/types-cjs/index.d.ts +10 -1
- package/dist/types-cjs/layout/Center.d.ts +54 -0
- package/dist/types-cjs/layout/Flex.d.ts +92 -0
- package/dist/types-cjs/layout/Group.d.ts +86 -0
- package/dist/types-cjs/layout/Stack.d.ts +63 -0
- package/dist/versions/bestax-no-dark-mode.css +1 -1
- package/dist/versions/bestax-no-dark-mode.css.map +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
- package/dist/versions/bestax-no-helpers.css +1 -1
- package/dist/versions/bestax-no-helpers.css.map +1 -1
- package/dist/versions/bestax-prefixed.css +1 -1
- package/dist/versions/bestax-prefixed.css.map +1 -1
- package/package.json +1 -1
- package/src/scss/elements/_iconbutton.scss +36 -0
- package/src/scss/elements/_index.scss +1 -0
- package/src/scss/layout/_index.scss +1 -0
- package/src/scss/layout/_stack.scss +145 -0
|
@@ -1,50 +1,210 @@
|
|
|
1
1
|
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
2
|
'use client';
|
|
3
3
|
import { useMemo } from 'react';
|
|
4
|
-
import { useLocaleScope } from './LocaleProvider.js';
|
|
4
|
+
import { useLocaleScope, validLocale } from './LocaleProvider.js';
|
|
5
|
+
import { warnOnce } from './devWarnings.js';
|
|
5
6
|
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
7
|
+
// Number formatting and text matching in a locale.
|
|
8
|
+
//
|
|
9
|
+
// `useNumberFormat` and its `NumberFormatFunction` type are public, exported
|
|
10
|
+
// from src/index.ts. The rest is INTERNAL, deliberately not exported.
|
|
11
|
+
//
|
|
12
|
+
// Which a component calls depends on its job. A component whose job is to
|
|
13
|
+
// write a number, such as `NumberFormatter`, uses `useNumberFormat`, which
|
|
14
|
+
// writes in the runtime's locale outside a `LocaleProvider`, as the pickers
|
|
15
|
+
// do. Any other component that shows a number in passing, such as a badge
|
|
16
|
+
// count, a page number or a step's number, uses `useScopedNumberFormat`,
|
|
17
|
+
// and filters a list through `useLocaleMatcher`. Those two return `null`
|
|
18
|
+
// outside a provider, and inside one that gives no locale, as a provider
|
|
19
|
+
// with only a `dir` doesn't, so the component keeps the formatting and the
|
|
20
|
+
// matching it has always had there, as `LocaleProvider` promises.
|
|
10
21
|
/** A combining mark: an accent, a vowel sign or a point written on a letter. */
|
|
11
22
|
const MARK = /\p{M}/u;
|
|
12
23
|
/**
|
|
13
|
-
* The
|
|
14
|
-
*
|
|
15
|
-
* provider's; outside a provider, none.
|
|
24
|
+
* The warning about a locale `Intl` refuses, given to `useNumberFormat`,
|
|
25
|
+
* and so to `NumberFormatter`, the only callers that pass one.
|
|
16
26
|
*/
|
|
17
|
-
|
|
27
|
+
const warnOwnLocale = (tag) => warnOnce(`number-format-invalid-locale:${tag}`, `[bestax-bulma] NumberFormatter or useNumberFormat was given the locale ` +
|
|
28
|
+
`"${tag}", which is not a language tag Intl accepts, so it is ` +
|
|
29
|
+
"ignored and numbers are written in the LocaleProvider's locale, " +
|
|
30
|
+
'else the runtime\'s. Use a BCP 47 tag such as "en-US", with a ' +
|
|
31
|
+
'hyphen rather than an underscore.');
|
|
32
|
+
/**
|
|
33
|
+
* The locale a component formats and matches in: the locale given, else the
|
|
34
|
+
* `LocaleProvider`'s, else none. A tag `Intl` refuses counts as none, with a
|
|
35
|
+
* development warning, as `LocaleProvider` treats one.
|
|
36
|
+
*/
|
|
37
|
+
function useResolvedLocale(own) {
|
|
18
38
|
const scope = useLocaleScope();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return scope.locale;
|
|
39
|
+
const valid = useMemo(() => validLocale(own, warnOwnLocale), [own]);
|
|
40
|
+
return valid || scope?.locale;
|
|
22
41
|
}
|
|
23
42
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
* The options `Intl.NumberFormat` reads, as ECMA-402 lists them. A key reads
|
|
44
|
+
* each by name, so an inherited option counts as `Intl` counts it.
|
|
45
|
+
*/
|
|
46
|
+
const NUMBER_FORMAT_OPTIONS = [
|
|
47
|
+
'localeMatcher',
|
|
48
|
+
'numberingSystem',
|
|
49
|
+
'style',
|
|
50
|
+
'currency',
|
|
51
|
+
'currencyDisplay',
|
|
52
|
+
'currencySign',
|
|
53
|
+
'unit',
|
|
54
|
+
'unitDisplay',
|
|
55
|
+
'notation',
|
|
56
|
+
'minimumIntegerDigits',
|
|
57
|
+
'minimumFractionDigits',
|
|
58
|
+
'maximumFractionDigits',
|
|
59
|
+
'minimumSignificantDigits',
|
|
60
|
+
'maximumSignificantDigits',
|
|
61
|
+
'roundingIncrement',
|
|
62
|
+
'roundingMode',
|
|
63
|
+
'roundingPriority',
|
|
64
|
+
'trailingZeroDisplay',
|
|
65
|
+
'compactDisplay',
|
|
66
|
+
'useGrouping',
|
|
67
|
+
'signDisplay',
|
|
68
|
+
];
|
|
69
|
+
const KNOWN_OPTIONS = new Set(NUMBER_FORMAT_OPTIONS);
|
|
70
|
+
/** One option's value with its type, or `null` for one a key can't hold. */
|
|
71
|
+
function writeOption(value) {
|
|
72
|
+
switch (typeof value) {
|
|
73
|
+
case 'string':
|
|
74
|
+
return JSON.stringify(value);
|
|
75
|
+
case 'number':
|
|
76
|
+
// `NaN` and `Infinity` as themselves, which `JSON.stringify` makes
|
|
77
|
+
// `null`, and `-0` with its sign.
|
|
78
|
+
return Object.is(value, -0) ? '-0' : String(value);
|
|
79
|
+
case 'boolean':
|
|
80
|
+
return String(value);
|
|
81
|
+
case 'bigint':
|
|
82
|
+
return `${value}n`;
|
|
83
|
+
default:
|
|
84
|
+
return value === null ? 'null' : null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The options as `Intl` will read them, and a key that tells apart any two
|
|
89
|
+
* sets it would read differently; `null` when an option `Intl` reads holds
|
|
90
|
+
* an object, a function or a symbol, which `Intl` converts in ways a key
|
|
91
|
+
* can't follow. It reads each option `Intl` reads by name, inherited ones
|
|
92
|
+
* included, then every other enumerable key, own or inherited, so an option
|
|
93
|
+
* `Intl` gains later still keeps two sets apart. Since `Intl`'s options are
|
|
94
|
+
* all primitives, one of those other keys holding anything else is left
|
|
95
|
+
* out. An option set to `undefined` is unset, as it is for `Intl`. The
|
|
96
|
+
* formatter is built from the copy, so `Intl` sees what the key says, even
|
|
97
|
+
* of a getter.
|
|
98
|
+
*/
|
|
99
|
+
function readOptions(options) {
|
|
100
|
+
const others = [];
|
|
101
|
+
for (const name in options) {
|
|
102
|
+
if (!KNOWN_OPTIONS.has(name))
|
|
103
|
+
others.push(name);
|
|
104
|
+
}
|
|
105
|
+
const names = NUMBER_FORMAT_OPTIONS.concat(others.sort());
|
|
106
|
+
const read = {};
|
|
107
|
+
const parts = [];
|
|
108
|
+
for (const name of names) {
|
|
109
|
+
const value = options[name];
|
|
110
|
+
if (value === undefined)
|
|
111
|
+
continue;
|
|
112
|
+
const written = writeOption(value);
|
|
113
|
+
if (written === null) {
|
|
114
|
+
// An option `Intl` reads converts an object; a key it doesn't read,
|
|
115
|
+
// such as a config's own `meta`, means nothing to it.
|
|
116
|
+
if (KNOWN_OPTIONS.has(name))
|
|
117
|
+
return null;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
read[name] = value;
|
|
121
|
+
parts.push(`${JSON.stringify(name)}:${written}`);
|
|
122
|
+
}
|
|
123
|
+
return { key: parts.join(','), read };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Every formatter's `Intl.NumberFormat`s, by locale and options, so a page of
|
|
127
|
+
* numbers in one locale and format builds one. Building one is the expensive
|
|
128
|
+
* part of formatting. Past `MOST_NUMBER_FORMATS`, the one used longest ago
|
|
129
|
+
* goes, so a server that writes numbers in many locales doesn't keep them
|
|
130
|
+
* all, and the ones in use stay. A `Map` keeps its keys in the order they
|
|
131
|
+
* were set, so a formatter is set again each time it's used.
|
|
132
|
+
*/
|
|
133
|
+
const numberFormats = new Map();
|
|
134
|
+
const MOST_NUMBER_FORMATS = 200;
|
|
135
|
+
/** The `Intl.NumberFormat` for `locale` and `options`, built once. */
|
|
136
|
+
function numberFormatFor(locale, options) {
|
|
137
|
+
let read;
|
|
138
|
+
let key = '';
|
|
139
|
+
if (options !== undefined) {
|
|
140
|
+
const copy = typeof options === 'object' && options !== null
|
|
141
|
+
? readOptions(options)
|
|
142
|
+
: null;
|
|
143
|
+
// Options a key can't hold get a formatter of their own, which also
|
|
144
|
+
// throws what `Intl` throws for them.
|
|
145
|
+
if (!copy)
|
|
146
|
+
return new Intl.NumberFormat(locale, options);
|
|
147
|
+
read = copy.read;
|
|
148
|
+
key = copy.key;
|
|
149
|
+
}
|
|
150
|
+
// A locale tag has no spaces, so the key can't be read two ways.
|
|
151
|
+
const cacheKey = `${locale ?? ''} ${key}`;
|
|
152
|
+
let format = numberFormats.get(cacheKey);
|
|
153
|
+
if (format) {
|
|
154
|
+
numberFormats.delete(cacheKey);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// Built before anything is stored, so options `Intl` refuses throw its
|
|
158
|
+
// error and leave nothing behind.
|
|
159
|
+
format = new Intl.NumberFormat(locale, read);
|
|
160
|
+
if (numberFormats.size >= MOST_NUMBER_FORMATS) {
|
|
161
|
+
numberFormats.delete(numberFormats.keys().next().value);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
numberFormats.set(cacheKey, format);
|
|
165
|
+
return format;
|
|
166
|
+
}
|
|
167
|
+
/** A function that writes numbers in `locale`, the same one for each locale. */
|
|
168
|
+
function useFormatIn(locale) {
|
|
169
|
+
return useMemo(() => (value, options) => numberFormatFor(locale, options).format(value), [locale]);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* A function that writes numbers for display in a locale: `locale` when given, else the `LocaleProvider`'s around the component, else the runtime's.
|
|
173
|
+
*
|
|
174
|
+
* Formatters are kept per locale and set of options, since building one is
|
|
175
|
+
* the expensive part, and shared, so many numbers written the same way build
|
|
176
|
+
* one. The function is the same from one render to the next for the same
|
|
177
|
+
* locale, so it's safe in a dependency array. Use it for text outside JSX,
|
|
178
|
+
* such as an `aria-label`, a `placeholder` or a chart's axis; in JSX,
|
|
179
|
+
* `NumberFormatter` also marks the number up. On a server render the
|
|
180
|
+
* runtime's locale is the server's, so give it a locale, here or through a
|
|
181
|
+
* `LocaleProvider`, where the server's and the browser's can differ. A tag
|
|
182
|
+
* `Intl` refuses is ignored, with a warning in development, as
|
|
183
|
+
* `LocaleProvider` ignores one. Options `Intl` refuses throw its error when
|
|
184
|
+
* the function is called.
|
|
185
|
+
*
|
|
186
|
+
* @function
|
|
187
|
+
* @param locale - A BCP 47 locale tag, which wins over the provider's.
|
|
188
|
+
* @returns {NumberFormatFunction} Writes `value` with `Intl.NumberFormat` options.
|
|
28
189
|
*
|
|
29
|
-
* @
|
|
30
|
-
*
|
|
190
|
+
* @example
|
|
191
|
+
* const format = useNumberFormat();
|
|
192
|
+
* const placeholder = format(1500, { style: 'currency', currency: 'EUR' });
|
|
31
193
|
*/
|
|
32
|
-
function useNumberFormat(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
};
|
|
47
|
-
}, [locale]);
|
|
194
|
+
function useNumberFormat(locale) {
|
|
195
|
+
return useFormatIn(useResolvedLocale(locale));
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* INTERNAL, deliberately not exported from `src/index.ts`. Number formatting
|
|
199
|
+
* for a component that shows a number in passing, as `useNumberFormat`
|
|
200
|
+
* writes it in the `LocaleProvider`'s locale, or `null` outside a provider
|
|
201
|
+
* and inside one that gives no locale, as a provider with only a `dir`
|
|
202
|
+
* doesn't, where the component keeps the formatting it has always had.
|
|
203
|
+
*/
|
|
204
|
+
function useScopedNumberFormat() {
|
|
205
|
+
const scoped = useResolvedLocale(undefined);
|
|
206
|
+
const format = useFormatIn(scoped);
|
|
207
|
+
return scoped ? format : null;
|
|
48
208
|
}
|
|
49
209
|
/**
|
|
50
210
|
* A matcher for filtering a list by what was typed, in the locale of the
|
|
@@ -58,7 +218,7 @@ function useNumberFormat(own) {
|
|
|
58
218
|
* matches everything.
|
|
59
219
|
*/
|
|
60
220
|
function useLocaleMatcher() {
|
|
61
|
-
const locale =
|
|
221
|
+
const locale = useResolvedLocale(undefined);
|
|
62
222
|
return useMemo(() => {
|
|
63
223
|
if (!locale)
|
|
64
224
|
return null;
|
|
@@ -105,5 +265,5 @@ function useLocaleMatcher() {
|
|
|
105
265
|
}, [locale]);
|
|
106
266
|
}
|
|
107
267
|
|
|
108
|
-
export { useLocaleMatcher, useNumberFormat };
|
|
268
|
+
export { useLocaleMatcher, useNumberFormat, useScopedNumberFormat };
|
|
109
269
|
//# sourceMappingURL=localeFormat.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localeFormat.js","sources":["../../../src/helpers/localeFormat.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts. Number formatting\n// and text matching in a LocaleProvider's locale. Both return `null` outside\n// a provider that gives a locale, so each caller keeps the formatting and the\n// matching it has always had there.\n\nimport { useMemo } from 'react';\nimport { useLocaleScope } from './LocaleProvider';\n\n/** A combining mark: an accent, a vowel sign or a point written on a letter. */\nconst MARK = /\\p{M}/u;\n\n/** Formats a number for display, with `Intl.NumberFormat` options. */\nexport type NumberFormatter = (\n value: number,\n options?: Intl.NumberFormatOptions\n) => string;\n\n/** Whether `text` contains `query`, as the locale compares letters. */\nexport type TextMatcher = (text: string, query: string) => boolean;\n\n/**\n * The locale a component formats and matches in: inside a `LocaleProvider`,\n * the component's own locale when it has one (a picker's `locale`), else the\n * provider's; outside a provider, none.\n */\nfunction useScopedLocale(own: string | undefined): string | undefined {\n const scope = useLocaleScope();\n if (!scope) return undefined;\n return own || scope.locale;\n}\n\n/**\n * A formatter for numbers shown on screen, in the locale of the\n * `LocaleProvider` around the component, or `null` outside one that gives a\n * locale. Formatters are kept per set of options, since building one is the\n * expensive part.\n *\n * @param own - The component's own locale, such as a picker's `locale`,\n * which wins inside a provider.\n */\nexport function useNumberFormat(own?: string): NumberFormatter | null {\n const locale = useScopedLocale(own);\n return useMemo(() => {\n if (!locale) return null;\n const formats = new Map<string, Intl.NumberFormat>();\n return (value, options) => {\n const key = options ? JSON.stringify(options) : '';\n let format = formats.get(key);\n if (!format) {\n format = new Intl.NumberFormat(locale, options);\n formats.set(key, format);\n }\n return format.format(value);\n };\n }, [locale]);\n}\n\n/**\n * A matcher for filtering a list by what was typed, in the locale of the\n * `LocaleProvider` around the component, or `null` outside one that gives a\n * locale. It compares with `Intl.Collator` at `usage: 'search'` and\n * `sensitivity: 'base'`, so it ignores case and accents where the language\n * treats the letters as the same: \"cafe\" finds \"Café\", while in Swedish \"a\"\n * doesn't find \"å\", a letter of its own there. Marks the language ignores\n * count for nothing on either side, so in Arabic محمد finds مُحَمَّد and the\n * other way round. An empty query, or one of nothing but such marks,\n * matches everything.\n */\nexport function useLocaleMatcher(): TextMatcher | null {\n const locale = useScopedLocale(undefined);\n return useMemo(() => {\n if (!locale) return null;\n const collator = new Intl.Collator(locale, {\n usage: 'search',\n sensitivity: 'base',\n });\n return (text, query) => {\n const needle = query.normalize('NFC');\n if (collator.compare(needle, '') === 0) return true;\n // Compared by code point, so a slice never splits a surrogate pair.\n const chars = Array.from(text.normalize('NFC'));\n const marks = chars.map(char => MARK.test(char));\n // A slice equal to the needle can be longer or shorter than it: the\n // marks the collator ignores, Arabic harakat or Hebrew niqqud, take\n // code points on one side and not the other, and the expansions the\n // collator makes, \"ß\" against \"ss\" or \"æ\" against \"ae\", turn one letter\n // into two. So each slice runs from a letter to just before the next\n // letter, never cutting a letter from its marks, and the slices from\n // one start grow until they hold twice the needle's letters, which\n // those expansions can't get past. That keeps the work proportional to\n // the text's length, however long a label is, rather than to its\n // square.\n const most =\n 2 * Array.from(needle).filter(char => !MARK.test(char)).length;\n for (let start = 0; start < chars.length; start++) {\n if (marks[start]) continue;\n let letters = 0;\n for (let end = start + 1; end <= chars.length; end++) {\n if (!marks[end - 1]) letters++;\n if (letters > most) break;\n if (marks[end]) continue;\n if (\n collator.compare(chars.slice(start, end).join(''), needle) === 0\n ) {\n return true;\n }\n }\n }\n return false;\n };\n }, [locale]);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"localeFormat.js","sources":["../../../src/helpers/localeFormat.ts"],"sourcesContent":["// Number formatting and text matching in a locale.\n//\n// `useNumberFormat` and its `NumberFormatFunction` type are public, exported\n// from src/index.ts. The rest is INTERNAL, deliberately not exported.\n//\n// Which a component calls depends on its job. A component whose job is to\n// write a number, such as `NumberFormatter`, uses `useNumberFormat`, which\n// writes in the runtime's locale outside a `LocaleProvider`, as the pickers\n// do. Any other component that shows a number in passing, such as a badge\n// count, a page number or a step's number, uses `useScopedNumberFormat`,\n// and filters a list through `useLocaleMatcher`. Those two return `null`\n// outside a provider, and inside one that gives no locale, as a provider\n// with only a `dir` doesn't, so the component keeps the formatting and the\n// matching it has always had there, as `LocaleProvider` promises.\n\nimport { useMemo } from 'react';\nimport { useLocaleScope, validLocale } from './LocaleProvider';\nimport { warnOnce } from './devWarnings';\n\n/** A combining mark: an accent, a vowel sign or a point written on a letter. */\nconst MARK = /\\p{M}/u;\n\n/** Formats a number for display, with `Intl.NumberFormat` options. */\nexport type NumberFormatFunction = (\n value: number,\n options?: Intl.NumberFormatOptions\n) => string;\n\n/** Whether `text` contains `query`, as the locale compares letters. */\nexport type TextMatcher = (text: string, query: string) => boolean;\n\n/**\n * The warning about a locale `Intl` refuses, given to `useNumberFormat`,\n * and so to `NumberFormatter`, the only callers that pass one.\n */\nconst warnOwnLocale = (tag: string) =>\n warnOnce(\n `number-format-invalid-locale:${tag}`,\n `[bestax-bulma] NumberFormatter or useNumberFormat was given the locale ` +\n `\"${tag}\", which is not a language tag Intl accepts, so it is ` +\n \"ignored and numbers are written in the LocaleProvider's locale, \" +\n 'else the runtime\\'s. Use a BCP 47 tag such as \"en-US\", with a ' +\n 'hyphen rather than an underscore.'\n );\n\n/**\n * The locale a component formats and matches in: the locale given, else the\n * `LocaleProvider`'s, else none. A tag `Intl` refuses counts as none, with a\n * development warning, as `LocaleProvider` treats one.\n */\nfunction useResolvedLocale(own: string | undefined): string | undefined {\n const scope = useLocaleScope();\n const valid = useMemo(() => validLocale(own, warnOwnLocale), [own]);\n return valid || scope?.locale;\n}\n\n/**\n * The options `Intl.NumberFormat` reads, as ECMA-402 lists them. A key reads\n * each by name, so an inherited option counts as `Intl` counts it.\n */\nconst NUMBER_FORMAT_OPTIONS = [\n 'localeMatcher',\n 'numberingSystem',\n 'style',\n 'currency',\n 'currencyDisplay',\n 'currencySign',\n 'unit',\n 'unitDisplay',\n 'notation',\n 'minimumIntegerDigits',\n 'minimumFractionDigits',\n 'maximumFractionDigits',\n 'minimumSignificantDigits',\n 'maximumSignificantDigits',\n 'roundingIncrement',\n 'roundingMode',\n 'roundingPriority',\n 'trailingZeroDisplay',\n 'compactDisplay',\n 'useGrouping',\n 'signDisplay',\n];\nconst KNOWN_OPTIONS = new Set(NUMBER_FORMAT_OPTIONS);\n\n/** One option's value with its type, or `null` for one a key can't hold. */\nfunction writeOption(value: unknown): string | null {\n switch (typeof value) {\n case 'string':\n return JSON.stringify(value);\n case 'number':\n // `NaN` and `Infinity` as themselves, which `JSON.stringify` makes\n // `null`, and `-0` with its sign.\n return Object.is(value, -0) ? '-0' : String(value);\n case 'boolean':\n return String(value);\n case 'bigint':\n return `${value}n`;\n default:\n return value === null ? 'null' : null;\n }\n}\n\n/**\n * The options as `Intl` will read them, and a key that tells apart any two\n * sets it would read differently; `null` when an option `Intl` reads holds\n * an object, a function or a symbol, which `Intl` converts in ways a key\n * can't follow. It reads each option `Intl` reads by name, inherited ones\n * included, then every other enumerable key, own or inherited, so an option\n * `Intl` gains later still keeps two sets apart. Since `Intl`'s options are\n * all primitives, one of those other keys holding anything else is left\n * out. An option set to `undefined` is unset, as it is for `Intl`. The\n * formatter is built from the copy, so `Intl` sees what the key says, even\n * of a getter.\n */\nfunction readOptions(\n options: object\n): { key: string; read: Record<string, unknown> } | null {\n const others: string[] = [];\n for (const name in options) {\n if (!KNOWN_OPTIONS.has(name)) others.push(name);\n }\n const names = NUMBER_FORMAT_OPTIONS.concat(others.sort());\n const read: Record<string, unknown> = {};\n const parts: string[] = [];\n for (const name of names) {\n const value = (options as Record<string, unknown>)[name];\n if (value === undefined) continue;\n const written = writeOption(value);\n if (written === null) {\n // An option `Intl` reads converts an object; a key it doesn't read,\n // such as a config's own `meta`, means nothing to it.\n if (KNOWN_OPTIONS.has(name)) return null;\n continue;\n }\n read[name] = value;\n parts.push(`${JSON.stringify(name)}:${written}`);\n }\n return { key: parts.join(','), read };\n}\n\n/**\n * Every formatter's `Intl.NumberFormat`s, by locale and options, so a page of\n * numbers in one locale and format builds one. Building one is the expensive\n * part of formatting. Past `MOST_NUMBER_FORMATS`, the one used longest ago\n * goes, so a server that writes numbers in many locales doesn't keep them\n * all, and the ones in use stay. A `Map` keeps its keys in the order they\n * were set, so a formatter is set again each time it's used.\n */\nconst numberFormats = new Map<string, Intl.NumberFormat>();\nconst MOST_NUMBER_FORMATS = 200;\n\n/** The `Intl.NumberFormat` for `locale` and `options`, built once. */\nfunction numberFormatFor(\n locale: string | undefined,\n options: Intl.NumberFormatOptions | undefined\n): Intl.NumberFormat {\n let read: Intl.NumberFormatOptions | undefined;\n let key = '';\n if (options !== undefined) {\n const copy =\n typeof options === 'object' && options !== null\n ? readOptions(options)\n : null;\n // Options a key can't hold get a formatter of their own, which also\n // throws what `Intl` throws for them.\n if (!copy) return new Intl.NumberFormat(locale, options);\n read = copy.read;\n key = copy.key;\n }\n // A locale tag has no spaces, so the key can't be read two ways.\n const cacheKey = `${locale ?? ''} ${key}`;\n let format = numberFormats.get(cacheKey);\n if (format) {\n numberFormats.delete(cacheKey);\n } else {\n // Built before anything is stored, so options `Intl` refuses throw its\n // error and leave nothing behind.\n format = new Intl.NumberFormat(locale, read);\n if (numberFormats.size >= MOST_NUMBER_FORMATS) {\n numberFormats.delete(numberFormats.keys().next().value as string);\n }\n }\n numberFormats.set(cacheKey, format);\n return format;\n}\n\n/** A function that writes numbers in `locale`, the same one for each locale. */\nfunction useFormatIn(locale: string | undefined): NumberFormatFunction {\n return useMemo<NumberFormatFunction>(\n () => (value, options) => numberFormatFor(locale, options).format(value),\n [locale]\n );\n}\n\n/**\n * A function that writes numbers for display in a locale: `locale` when given, else the `LocaleProvider`'s around the component, else the runtime's.\n *\n * Formatters are kept per locale and set of options, since building one is\n * the expensive part, and shared, so many numbers written the same way build\n * one. The function is the same from one render to the next for the same\n * locale, so it's safe in a dependency array. Use it for text outside JSX,\n * such as an `aria-label`, a `placeholder` or a chart's axis; in JSX,\n * `NumberFormatter` also marks the number up. On a server render the\n * runtime's locale is the server's, so give it a locale, here or through a\n * `LocaleProvider`, where the server's and the browser's can differ. A tag\n * `Intl` refuses is ignored, with a warning in development, as\n * `LocaleProvider` ignores one. Options `Intl` refuses throw its error when\n * the function is called.\n *\n * @function\n * @param locale - A BCP 47 locale tag, which wins over the provider's.\n * @returns {NumberFormatFunction} Writes `value` with `Intl.NumberFormat` options.\n *\n * @example\n * const format = useNumberFormat();\n * const placeholder = format(1500, { style: 'currency', currency: 'EUR' });\n */\nexport function useNumberFormat(locale?: string): NumberFormatFunction {\n return useFormatIn(useResolvedLocale(locale));\n}\n\n/**\n * INTERNAL, deliberately not exported from `src/index.ts`. Number formatting\n * for a component that shows a number in passing, as `useNumberFormat`\n * writes it in the `LocaleProvider`'s locale, or `null` outside a provider\n * and inside one that gives no locale, as a provider with only a `dir`\n * doesn't, where the component keeps the formatting it has always had.\n */\nexport function useScopedNumberFormat(): NumberFormatFunction | null {\n const scoped = useResolvedLocale(undefined);\n const format = useFormatIn(scoped);\n return scoped ? format : null;\n}\n\n/**\n * A matcher for filtering a list by what was typed, in the locale of the\n * `LocaleProvider` around the component, or `null` outside one that gives a\n * locale. It compares with `Intl.Collator` at `usage: 'search'` and\n * `sensitivity: 'base'`, so it ignores case and accents where the language\n * treats the letters as the same: \"cafe\" finds \"Café\", while in Swedish \"a\"\n * doesn't find \"å\", a letter of its own there. Marks the language ignores\n * count for nothing on either side, so in Arabic محمد finds مُحَمَّد and the\n * other way round. An empty query, or one of nothing but such marks,\n * matches everything.\n */\nexport function useLocaleMatcher(): TextMatcher | null {\n const locale = useResolvedLocale(undefined);\n return useMemo(() => {\n if (!locale) return null;\n const collator = new Intl.Collator(locale, {\n usage: 'search',\n sensitivity: 'base',\n });\n return (text, query) => {\n const needle = query.normalize('NFC');\n if (collator.compare(needle, '') === 0) return true;\n // Compared by code point, so a slice never splits a surrogate pair.\n const chars = Array.from(text.normalize('NFC'));\n const marks = chars.map(char => MARK.test(char));\n // A slice equal to the needle can be longer or shorter than it: the\n // marks the collator ignores, Arabic harakat or Hebrew niqqud, take\n // code points on one side and not the other, and the expansions the\n // collator makes, \"ß\" against \"ss\" or \"æ\" against \"ae\", turn one letter\n // into two. So each slice runs from a letter to just before the next\n // letter, never cutting a letter from its marks, and the slices from\n // one start grow until they hold twice the needle's letters, which\n // those expansions can't get past. That keeps the work proportional to\n // the text's length, however long a label is, rather than to its\n // square.\n const most =\n 2 * Array.from(needle).filter(char => !MARK.test(char)).length;\n for (let start = 0; start < chars.length; start++) {\n if (marks[start]) continue;\n let letters = 0;\n for (let end = start + 1; end <= chars.length; end++) {\n if (!marks[end - 1]) letters++;\n if (letters > most) break;\n if (marks[end]) continue;\n if (\n collator.compare(chars.slice(start, end).join(''), needle) === 0\n ) {\n return true;\n }\n }\n }\n return false;\n };\n }, [locale]);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"polymorphic.js","sources":["../../../src/helpers/polymorphic.ts"],"sourcesContent":["import type React from 'react';\n\n/**\n * Whether an `as` target is a custom element rather than a built-in tag.\n *\n * HTML requires a custom element's name to contain a hyphen, and no built-in\n * element name has one, so this is exact rather than a heuristic. It matters\n * because a custom element is an intrinsic STRING — `typeof as === 'string'` is\n * true — while its props are whatever a consumer declared through\n * `React.JSX.IntrinsicElements`, the way this package declares `<ion-icon>`.\n * A runtime backstop that filters built-in attributes must leave those alone,\n * or it strips props the derived type just promised to forward.\n */\nexport function isCustomElement(as: unknown): as is string {\n return typeof as === 'string' && as.includes('-');\n}\n\n/**\n * The ref type of whatever element `as` renders.\n *\n * Derived from `ComponentPropsWithRef` rather than `React.ComponentRef` so it\n * resolves on every `@types/react` a consumer may be on: this package supports\n * React 18 and 19, and the CI matrix pins only `react`/`react-dom`, never the\n * types, so a types-only regression here would not be caught.\n *\n * `never` when the target takes no ref. A plain function component has no `ref`\n * key at all, and React's own types reject `<PlainFC ref={…} />`; without the\n * guard the indexed access degraded to `unknown` and we accepted a ref that\n * silently does nothing — `ref.current` stays null and a later `.focus()`\n * throws. Matching React's strictness is the point.\n */\nexport type PolymorphicRef<T extends React.ElementType> =\n 'ref' extends keyof React.ComponentPropsWithRef<T>\n ? React.ComponentPropsWithRef<T>['ref']\n : never;\n\n/**\n * A component's own props, plus the attributes of the element `as` names.\n *\n * `Own` wins a collision by default — a component that declares\n * `color` as a Bulma variant keeps it, rather than inheriting the DOM attribute\n * of the same name. `color` used to be dropped here as well, to stop the\n * deprecated presentational HTML attribute reaching the components whose own\n * props do not declare one — but doing it in the shared type also stripped\n * `color` from a CUSTOM target that legitimately has one. Those components\n * declare `color?: never` themselves instead, which lands in `keyof Own` and\n * reaches the same result without a special case here.\n *\n * `Forwarded` names the own props the component hands STRAIGHT to the target\n * instead of consuming, and the subtraction runs the other way for them: the\n * TARGET's declaration wins, and the own one is what covers a target that has\n * no such prop. Subtracting them threw the target's declaration away, so an\n * OPTIONAL own prop hid a REQUIRED one — `<Avatar as={NextLink} name=\"Ada\" />`\n * compiled because Avatar's `href?: string` survived while `next/link`'s\n * required `href` was omitted, and the target threw on what it never received\n * (#665). The target wins rather than both declarations applying at once,\n * because for a prop the component only passes along its own type has no\n * standing: `next/link` accepts a URL object as well as a string, and\n * intersecting Avatar's `string` with that would refuse the object.\n *\n * `Forwarded` defaults to `never`, so a prop the component CONSUMES stays\n * subtracted — the common case, and the one `color` needs. What that costs is\n * that a target REQUIRING a consumed prop is still satisfiable with the prop\n * omitted, and the value goes wherever the component sends it: `Menu.Item`\n * spends several of its own props on its wrapping `<li>`, `title` and `style`\n * among them, so a target requiring one of those never sees it. Erroring on\n * that needs a category this pair cannot express, since `className` is consumed\n * too and yet every component composes one and passes it on. It is pinned as a\n * limitation in `__typetests__/polymorphic.tsx` rather than guessed at here.\n *\n * `Own` is returned untouched when nothing is forwarded, rather than run through\n * an `Omit` that names no keys. `Omit` is not the identity it looks like: it\n * keys off `keyof Own`, which for a UNION `Own` is only what the members share,\n * so `PolymorphicProps<'div', A | B>` lost every prop of both. It is public API\n * and a consumer's wrapper can pass a union; the untouched branch also keeps\n * each own prop's TSDoc reaching an IDE, which a mapped type drops. The\n * forwarded branch distributes over `Own` for the same reason.\n *\n * Distributive over `T` on purpose. `Omit<A | B, K>` keys off `keyof (A | B)`,\n * which is only what A and B share — so a union-typed `as` (a ternary, or a\n * variable typed `'a' | 'button'`) silently lost every prop that belongs to\n * just one member, `href` among them. Distributing produces a union of prop\n * shapes instead, and each member keeps its own.\n *\n * Each component writes this intersection out in its own `*Props` alias rather\n * than referring to this type: the API-docs extractor\n * (`scripts/lib/props-extract.mjs`) walks heritage syntactically, and a generic\n * alias leaves it resolving a type parameter it cannot see through.\n * This type is here for the component's cast target and for consumers writing\n * wrappers.\n */\nexport type PolymorphicProps<\n T extends React.ElementType,\n Own,\n Forwarded extends keyof Own = never,\n> = T extends unknown\n ? ([Forwarded] extends [never]\n ? Own\n : Own extends unknown\n ? Omit<Own, Extract<Forwarded, keyof React.ComponentPropsWithoutRef<T>>>\n : never) &\n Omit<\n React.ComponentPropsWithoutRef<T>,\n Exclude<keyof Own, Forwarded> | 'as'\n > & {\n /** The element or component to render. */\n as?: T;\n }\n : never;\n\n/**\n * A component whose props and forwarded ref both follow `as`.\n *\n * `forwardRef` cannot express a generic component, so the implementation is\n * cast to this. `displayName` is part of the type because the library sets it\n * on every `forwardRef` component, and `withSubComponents` constrains its base\n * to `{ displayName?: string }`.\n *\n * The second, non-generic signature exists for type DERIVATION rather than for\n * calls. `React.ComponentProps<typeof Button>` infers from the last overload;\n * with only the generic one it instantiated at the constraint and collapsed to\n * `any`, so a consumer building their own prop type on ours lost every check.\n * Calls still resolve against the generic signature first, so ordering here is\n * load-bearing — swapping the two puts the collapse back.\n *\n * **A wrapping HOC erases the genericity.** `React.memo(Button)`,\n * `React.lazy`, a `styled()` wrapper — anything that infers its props through\n * `ComponentProps<T>` — instantiates the type parameter once and hands back a\n * component with a single prop type. It takes the LAST call signature, which is\n * the derivation overload, so the wrapper is pinned to the DEFAULT element:\n * `React.memo(Button)` rejects `as=\"div\"` outright, and `React.memo(Dropdown.Item)`\n * renders neither a `<div>` nor a `<button>`, though both components accept those\n * directly. Stricter, not looser — the checks do not stop, they collapse onto one\n * element, and a `Dropdown.Item` memoized for a long menu list is the case that\n * meets it. This is inherent to polymorphic components in TypeScript, not\n * something this library can fix. Re-assert the type to get the rest back:\n *\n * ```tsx\n * const MemoButton = React.memo(Button) as typeof Button;\n * ```\n */\nexport interface PolymorphicComponent<\n Own,\n Default extends React.ElementType,\n Forwarded extends keyof Own = never,\n> {\n <T extends React.ElementType = Default>(\n props: PolymorphicProps<T, Own, Forwarded> & { ref?: PolymorphicRef<T> }\n ): React.ReactElement | null;\n (\n props: PolymorphicProps<Default, Own, Forwarded> & {\n ref?: PolymorphicRef<Default>;\n }\n ): React.ReactElement | null;\n displayName?: string;\n}\n\n/**\n * A component whose props follow `as` but which forwards no ref.\n *\n * For components that own the node they observe — `Reveal` keeps its own ref on\n * the element it watches for scroll intersection, which is not always the\n * element `as` names.\n *\n * It carries `Forwarded` even with no component passing one today: the default\n * is the behaviour without it, and a component that grows a forwarded own prop\n * would otherwise reintroduce #665 here with nothing to say so.\n */\nexport interface PolymorphicComponentWithoutRef<\n Own,\n Default extends React.ElementType,\n Forwarded extends keyof Own = never,\n> {\n <T extends React.ElementType = Default>(\n props: PolymorphicProps<T, Own, Forwarded>\n ): React.ReactElement | null;\n // The same derivation overload `PolymorphicComponent` carries, for the same\n // reason: without it `React.ComponentProps<typeof Reveal>` instantiates at the\n // constraint and collapses to `any`.\n (props: PolymorphicProps<Default, Own, Forwarded>): React.ReactElement | null;\n displayName?: string;\n}\n\n/**\n * A component whose props follow `as`, where `as` is a closed set of tags.\n *\n * `PolymorphicComponentWithoutRef` lets `as` name anything; this narrows it to\n * `Allowed`. Bulma pins some elements by its own markup contract — a dropdown\n * item is an `<a>`, a `<div>` or a `<button>` and nothing else — and the\n * constraint keeps that promise while still deriving props from whichever of\n * the three a caller picks.\n *\n * Genericity is what makes the derivation exact for a LITERAL `as`, and it is\n * worth the extra type parameter. Typing such a component as `React.FC<Union>`\n * instead — a union of the three prop shapes — reads as equivalent but is not:\n * against a union target, an object literal's excess-property check passes if\n * the property exists in ANY member, so `<Item as=\"div\" href=\"/x\" />` slips\n * through. Inferring `T` from the literal checks against that one member.\n *\n * A UNION-typed `as` — a ternary, or a variable typed as the whole set — infers\n * `T` as the union and lands back in that same permissive check, so\n * `<Item as={tag} href=\"/x\" />` compiles whatever `tag` turns out to be. That is\n * the open `as` behaviour too, and deliberate there: `PolymorphicProps`\n * distributes precisely so a union-typed `as` KEEPS each member's props rather\n * than losing every prop the members do not share. Narrowing it is not a\n * constrained-component question; it would have to change for `Button` first.\n */\nexport interface ConstrainedPolymorphicComponentWithoutRef<\n Own,\n Allowed extends React.ElementType,\n Default extends Allowed,\n Forwarded extends keyof Own = never,\n> {\n <T extends Allowed = Default>(\n props: PolymorphicProps<T, Own, Forwarded>\n ): React.ReactElement | null;\n // The derivation overload `PolymorphicComponent` documents, for the same\n // reason: without it `React.ComponentProps<typeof DropdownItem>` instantiates\n // at the constraint and collapses to `any`.\n (props: PolymorphicProps<Default, Own, Forwarded>): React.ReactElement | null;\n displayName?: string;\n}\n"],"names":[],"mappings":";AAEA;;;;;;;;;;AAUG;AACG,SAAU,eAAe,CAAC,EAAW,EAAA;IACzC,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;AACnD;;"}
|
|
1
|
+
{"version":3,"file":"polymorphic.js","sources":["../../../src/helpers/polymorphic.ts"],"sourcesContent":["import type React from 'react';\n\n/**\n * Whether an `as` target is a custom element rather than a built-in tag.\n *\n * HTML requires a custom element's name to contain a hyphen, and no built-in\n * element name has one, so this is exact rather than a heuristic. It matters\n * because a custom element is an intrinsic STRING — `typeof as === 'string'` is\n * true — while its props are whatever a consumer declared through\n * `React.JSX.IntrinsicElements`, the way this package declares `<ion-icon>`.\n * A runtime backstop that filters built-in attributes must leave those alone,\n * or it strips props the derived type just promised to forward.\n */\nexport function isCustomElement(as: unknown): as is string {\n return typeof as === 'string' && as.includes('-');\n}\n\n/**\n * The ref type of whatever element `as` renders.\n *\n * Derived from `ComponentPropsWithRef` rather than `React.ComponentRef` so it\n * resolves on every `@types/react` a consumer may be on: this package supports\n * React 18 and 19, and the CI matrix pins only `react`/`react-dom`, never the\n * types, so a types-only regression here would not be caught.\n *\n * `never` when the target takes no ref. A plain function component has no `ref`\n * key at all, and React's own types reject `<PlainFC ref={…} />`; without the\n * guard the indexed access degraded to `unknown` and we accepted a ref that\n * silently does nothing — `ref.current` stays null and a later `.focus()`\n * throws. Matching React's strictness is the point.\n */\nexport type PolymorphicRef<T extends React.ElementType> =\n 'ref' extends keyof React.ComponentPropsWithRef<T>\n ? React.ComponentPropsWithRef<T>['ref']\n : never;\n\n/**\n * A component's own props, plus the attributes of the element `as` names.\n *\n * `Own` wins a collision by default — a component that declares\n * `color` as a Bulma variant keeps it, rather than inheriting the DOM attribute\n * of the same name. `color` used to be dropped here as well, to stop the\n * deprecated presentational HTML attribute reaching the components whose own\n * props do not declare one — but doing it in the shared type also stripped\n * `color` from a CUSTOM target that legitimately has one. Those components\n * declare `color?: never` themselves instead, which lands in `keyof Own` and\n * reaches the same result without a special case here.\n *\n * `Forwarded` names the own props the component hands STRAIGHT to the target\n * instead of consuming, and the subtraction runs the other way for them: the\n * TARGET's declaration wins, and the own one is what covers a target that has\n * no such prop. Subtracting them threw the target's declaration away, so an\n * OPTIONAL own prop hid a REQUIRED one — `<Avatar as={NextLink} name=\"Ada\" />`\n * compiled because Avatar's `href?: string` survived while `next/link`'s\n * required `href` was omitted, and the target threw on what it never received\n * (#665). The target wins rather than both declarations applying at once,\n * because for a prop the component only passes along its own type has no\n * standing: `next/link` accepts a URL object as well as a string, and\n * intersecting Avatar's `string` with that would refuse the object.\n *\n * `Forwarded` defaults to `never`, so a prop the component CONSUMES stays\n * subtracted — the common case, and the one `color` needs. What that costs is\n * that a target REQUIRING a consumed prop is still satisfiable with the prop\n * omitted, and the value goes wherever the component sends it: `Menu.Item`\n * spends several of its own props on its wrapping `<li>`, `title` and `style`\n * among them, so a target requiring one of those never sees it. Erroring on\n * that needs a category this pair cannot express, since `className` is consumed\n * too and yet every component composes one and passes it on. It is pinned as a\n * limitation in `__typetests__/polymorphic.tsx` rather than guessed at here.\n *\n * `Own` is returned untouched when nothing is forwarded, rather than run through\n * an `Omit` that names no keys. `Omit` is not the identity it looks like: it\n * keys off `keyof Own`, which for a UNION `Own` is only what the members share,\n * so `PolymorphicProps<'div', A | B>` lost every prop of both. It is public API\n * and a consumer's wrapper can pass a union; the untouched branch also keeps\n * each own prop's TSDoc reaching an IDE, which a mapped type drops. The\n * forwarded branch distributes over `Own` for the same reason.\n *\n * Distributive over `T` on purpose. `Omit<A | B, K>` keys off `keyof (A | B)`,\n * which is only what A and B share — so a union-typed `as` (a ternary, or a\n * variable typed `'a' | 'button'`) silently lost every prop that belongs to\n * just one member, `href` among them. Distributing produces a union of prop\n * shapes instead, and each member keeps its own.\n *\n * Each component writes this intersection out in its own `*Props` alias rather\n * than referring to this type: the API-docs extractor\n * (`scripts/lib/props-extract.mjs`) walks heritage syntactically, and a generic\n * alias leaves it resolving a type parameter it cannot see through.\n * This type is here for the component's cast target and for consumers writing\n * wrappers.\n */\nexport type PolymorphicProps<\n T extends React.ElementType,\n Own,\n Forwarded extends keyof Own = never,\n> = T extends unknown\n ? ([Forwarded] extends [never]\n ? Own\n : Own extends unknown\n ? Omit<Own, Extract<Forwarded, keyof React.ComponentPropsWithoutRef<T>>>\n : never) &\n Omit<\n React.ComponentPropsWithoutRef<T>,\n Exclude<keyof Own, Forwarded> | 'as'\n > & {\n /** The element or component to render. */\n as?: T;\n }\n : never;\n\n/**\n * A component whose props and forwarded ref both follow `as`.\n *\n * `forwardRef` cannot express a generic component, so the implementation is\n * cast to this. `displayName` is part of the type because the library sets it\n * on every `forwardRef` component, and `withSubComponents` constrains its base\n * to `{ displayName?: string }`.\n *\n * The second, non-generic signature exists for type DERIVATION rather than for\n * calls. `React.ComponentProps<typeof Button>` infers from the last overload;\n * with only the generic one it instantiated at the constraint and collapsed to\n * `any`, so a consumer building their own prop type on ours lost every check.\n * Calls still resolve against the generic signature first, so ordering here is\n * load-bearing — swapping the two puts the collapse back.\n *\n * **A wrapping HOC erases the genericity.** `React.memo(Button)`,\n * `React.lazy`, a `styled()` wrapper — anything that infers its props through\n * `ComponentProps<T>` — instantiates the type parameter once and hands back a\n * component with a single prop type. It takes the LAST call signature, which is\n * the derivation overload, so the wrapper is pinned to the DEFAULT element:\n * `React.memo(Button)` rejects `as=\"div\"` outright, and `React.memo(Dropdown.Item)`\n * renders neither a `<div>` nor a `<button>`, though both components accept those\n * directly. Stricter, not looser — the checks do not stop, they collapse onto one\n * element, and a `Dropdown.Item` memoized for a long menu list is the case that\n * meets it. This is inherent to polymorphic components in TypeScript, not\n * something this library can fix. Re-assert the type to get the rest back:\n *\n * ```tsx\n * const MemoButton = React.memo(Button) as typeof Button;\n * ```\n */\nexport interface PolymorphicComponent<\n Own,\n Default extends React.ElementType,\n Forwarded extends keyof Own = never,\n> {\n <T extends React.ElementType = Default>(\n props: PolymorphicProps<T, Own, Forwarded> & { ref?: PolymorphicRef<T> }\n ): React.ReactElement | null;\n (\n props: PolymorphicProps<Default, Own, Forwarded> & {\n ref?: PolymorphicRef<Default>;\n }\n ): React.ReactElement | null;\n displayName?: string;\n}\n\n/**\n * A component whose props follow `as` but which forwards no ref.\n *\n * For components that own the node they observe — `Reveal` keeps its own ref on\n * the element it watches for scroll intersection, which is not always the\n * element `as` names.\n *\n * It carries `Forwarded` even with no component passing one today: the default\n * is the behaviour without it, and a component that grows a forwarded own prop\n * would otherwise reintroduce #665 here with nothing to say so.\n */\nexport interface PolymorphicComponentWithoutRef<\n Own,\n Default extends React.ElementType,\n Forwarded extends keyof Own = never,\n> {\n <T extends React.ElementType = Default>(\n props: PolymorphicProps<T, Own, Forwarded>\n ): React.ReactElement | null;\n // The same derivation overload `PolymorphicComponent` carries, for the same\n // reason: without it `React.ComponentProps<typeof Reveal>` instantiates at the\n // constraint and collapses to `any`.\n (props: PolymorphicProps<Default, Own, Forwarded>): React.ReactElement | null;\n displayName?: string;\n}\n\n/**\n * A component whose props follow `as`, where `as` is a closed set of tags.\n *\n * `PolymorphicComponentWithoutRef` lets `as` name anything; this narrows it to\n * `Allowed`. Bulma pins some elements by its own markup contract — a dropdown\n * item is an `<a>`, a `<div>` or a `<button>` and nothing else — and the\n * constraint keeps that promise while still deriving props from whichever of\n * the three a caller picks.\n *\n * Genericity is what makes the derivation exact for a LITERAL `as`, and it is\n * worth the extra type parameter. Typing such a component as `React.FC<Union>`\n * instead — a union of the three prop shapes — reads as equivalent but is not:\n * against a union target, an object literal's excess-property check passes if\n * the property exists in ANY member, so `<Item as=\"div\" href=\"/x\" />` slips\n * through. Inferring `T` from the literal checks against that one member.\n *\n * A UNION-typed `as` — a ternary, or a variable typed as the whole set — infers\n * `T` as the union and lands back in that same permissive check, so\n * `<Item as={tag} href=\"/x\" />` compiles whatever `tag` turns out to be. That is\n * the open `as` behaviour too, and deliberate there: `PolymorphicProps`\n * distributes precisely so a union-typed `as` KEEPS each member's props rather\n * than losing every prop the members do not share. Narrowing it is not a\n * constrained-component question; it would have to change for `Button` first.\n */\nexport interface ConstrainedPolymorphicComponentWithoutRef<\n Own,\n Allowed extends React.ElementType,\n Default extends Allowed,\n Forwarded extends keyof Own = never,\n> {\n <T extends Allowed = Default>(\n props: PolymorphicProps<T, Own, Forwarded>\n ): React.ReactElement | null;\n // The derivation overload `PolymorphicComponent` documents, for the same\n // reason: without it `React.ComponentProps<typeof DropdownItem>` instantiates\n // at the constraint and collapses to `any`.\n (props: PolymorphicProps<Default, Own, Forwarded>): React.ReactElement | null;\n displayName?: string;\n}\n\n/**\n * `ConstrainedPolymorphicComponentWithoutRef` with a forwarded ref: the props\n * and the ref both follow `as`, and `as` is one of `Allowed`.\n *\n * For a component whose `as` is a closed set of tags that don't share one\n * prop surface. `Stack` renders a `<div>`, a `<form>`, an `<ol>` or a\n * `<fieldset>` among others, which take different attributes (`action`,\n * `start`, `disabled`) and give the ref a different element, so pinning them\n * all to `HTMLAttributes<HTMLElement>` would refuse attributes that work.\n */\nexport interface ConstrainedPolymorphicComponent<\n Own,\n Allowed extends React.ElementType,\n Default extends Allowed,\n Forwarded extends keyof Own = never,\n> {\n <T extends Allowed = Default>(\n props: PolymorphicProps<T, Own, Forwarded> & { ref?: PolymorphicRef<T> }\n ): React.ReactElement | null;\n // The derivation overload `PolymorphicComponent` documents.\n (\n props: PolymorphicProps<Default, Own, Forwarded> & {\n ref?: PolymorphicRef<Default>;\n }\n ): React.ReactElement | null;\n displayName?: string;\n}\n"],"names":[],"mappings":";AAEA;;;;;;;;;;AAUG;AACG,SAAU,eAAe,CAAC,EAAW,EAAA;IACzC,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;AACnD;;"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
'use client';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
import { forwardRef } from 'react';
|
|
5
|
+
import { usePrefixedClassNames, classNames } from './classNames.js';
|
|
6
|
+
import { useBulmaClasses } from './useBulmaClasses.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The `.stack` element behind Stack, Group, Center and Flex.
|
|
10
|
+
*
|
|
11
|
+
* With `as="ul"` or `as="ol"` it renders `role="list"`, which Safari needs:
|
|
12
|
+
* WebKit stops exposing a list as one once its markers are hidden, and
|
|
13
|
+
* Bulma's reset hides a `<ul>`'s. Menu.List restates it for the same reason.
|
|
14
|
+
*/
|
|
15
|
+
const StackLayout = forwardRef(function StackLayout({ as = 'div', className, textColor, bgColor,
|
|
16
|
+
// Not accepted, and taken out before the spread below, where a spread's
|
|
17
|
+
// `color: undefined`, or a JavaScript caller's `color`, would replace
|
|
18
|
+
// `textColor`, as `backgroundColor` would `bgColor`.
|
|
19
|
+
color: _color, backgroundColor: _backgroundColor, modifiers, children, ...props }, ref) {
|
|
20
|
+
// The partial makes the element a flex container, so the container
|
|
21
|
+
// helpers (alignItems and the rest) apply without a `display` prop.
|
|
22
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({ color: textColor, backgroundColor: bgColor, ...props }, { flexContainer: true });
|
|
23
|
+
const stackClasses = usePrefixedClassNames('stack', modifiers);
|
|
24
|
+
const Component = as;
|
|
25
|
+
const isList = as === 'ul' || as === 'ol';
|
|
26
|
+
return (jsx(Component, { ref: ref, className: classNames(stackClasses, bulmaHelperClasses, className), ...rest, ...(isList ? { role: rest.role || 'list' } : {}), children: children }));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export { StackLayout };
|
|
30
|
+
//# sourceMappingURL=stackLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stackLayout.js","sources":["../../../src/helpers/stackLayout.tsx"],"sourcesContent":["// INTERNAL: only `LayoutElement` is exported from src/index.ts.\n//\n// The element Stack, Group, Center and Flex share. Each is a `.stack`, which\n// `scss/layout/_stack.scss` makes a flex container, with a modifier class\n// for its own layout, so the helper props reach it as they reach any flex\n// container, with no `display` prop.\n\nimport React, { forwardRef } from 'react';\nimport { classNames, usePrefixedClassNames } from './classNames';\nimport {\n useBulmaClasses,\n type BulmaClassesProps,\n type validColors,\n type validFlexDirections,\n} from './useBulmaClasses';\n\n/**\n * The elements Stack, Group, Center and Flex can render as: Container's\n * landmarks and sections, a list, a form or a fieldset.\n */\nexport type LayoutElement =\n | 'div'\n | 'main'\n | 'section'\n | 'article'\n | 'aside'\n | 'header'\n | 'footer'\n | 'nav'\n | 'ul'\n | 'ol'\n | 'form'\n | 'fieldset';\n\n/**\n * What Stack, Group, Center and Flex take in common. The helper props, the\n * other attributes and the `ref` land on the root.\n *\n * @flexContainer The root is a flex container by its own stylesheet, so the\n * flex container helper props apply with no `display` prop. The API docs\n * extractor reads this tag off a component's props type or its bases, and\n * the ESLint plugin's `no-inert-flex-props` takes its flex containers from it.\n */\nexport interface LayoutOwnProps extends Omit<\n BulmaClassesProps,\n 'color' | 'backgroundColor' | 'flexDirection'\n> {\n /**\n * Not accepted. `color` here would be the deprecated presentational HTML\n * attribute. Use `textColor`.\n * @internal\n */\n color?: never;\n /**\n * Not accepted under this name. Use `bgColor`.\n * @internal\n */\n backgroundColor?: never;\n /** Additional CSS classes. */\n className?: string;\n /**\n * What it lays out. The top and bottom margins Bulma gives them are\n * zeroed, so `gap` is the only space between them, and a spacing helper\n * such as `mt=\"2\"` still adds to it. A `Columns` child keeps its own top and side\n * margins, and takes at the bottom the one Bulma gives the last Columns in\n * a container, the column gap pulled back in (none when gapless), so its\n * columns line up. A `Pagination` child keeps its negative margins too,\n * so its links line up.\n */\n children?: React.ReactNode;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/** The props `StackLayout` reads, from whichever of the four renders it. */\nexport interface StackLayoutProps\n extends LayoutOwnProps, Omit<React.HTMLAttributes<HTMLElement>, 'color'> {\n /** The element to render. */\n as?: LayoutElement;\n /** Flex's direction. The other three pass `undefined`, so they never reorder. */\n flexDirection?: (typeof validFlexDirections)[number];\n /** The modifier classes beside `stack`, as `usePrefixedClassNames` takes them. */\n modifiers?: Record<string, boolean | undefined>;\n}\n\n/**\n * The `.stack` element behind Stack, Group, Center and Flex.\n *\n * With `as=\"ul\"` or `as=\"ol\"` it renders `role=\"list\"`, which Safari needs:\n * WebKit stops exposing a list as one once its markers are hidden, and\n * Bulma's reset hides a `<ul>`'s. Menu.List restates it for the same reason.\n */\nexport const StackLayout = forwardRef<HTMLElement, StackLayoutProps>(\n function StackLayout(\n {\n as = 'div',\n className,\n textColor,\n bgColor,\n // Not accepted, and taken out before the spread below, where a spread's\n // `color: undefined`, or a JavaScript caller's `color`, would replace\n // `textColor`, as `backgroundColor` would `bgColor`.\n color: _color,\n backgroundColor: _backgroundColor,\n modifiers,\n children,\n ...props\n },\n ref\n ) {\n // The partial makes the element a flex container, so the container\n // helpers (alignItems and the rest) apply without a `display` prop.\n const { bulmaHelperClasses, rest } = useBulmaClasses(\n { color: textColor, backgroundColor: bgColor, ...props },\n { flexContainer: true }\n );\n const stackClasses = usePrefixedClassNames('stack', modifiers);\n const Component = as as React.ElementType;\n const isList = as === 'ul' || as === 'ol';\n\n return (\n <Component\n ref={ref}\n className={classNames(stackClasses, bulmaHelperClasses, className)}\n {...rest}\n // After the spread, reading through it, so a spread carrying\n // `role: undefined` can't erase the list's role. A role you pass wins.\n {...(isList ? { role: rest.role || 'list' } : {})}\n >\n {children}\n </Component>\n );\n }\n);\n"],"names":["_jsx"],"mappings":";;;;;;;AAuFA;;;;;;AAMG;AACI,MAAM,WAAW,GAAG,UAAU,CACnC,SAAS,WAAW,CAClB,EACE,EAAE,GAAG,KAAK,EACV,SAAS,EACT,SAAS,EACT,OAAO;AACP;AACA;AACA;AACA,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,gBAAgB,EACjC,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EACD,GAAG,EAAA;;;IAIH,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAClD,EAAE,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EACxD,EAAE,aAAa,EAAE,IAAI,EAAE,CACxB;IACD,MAAM,YAAY,GAAG,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC;IAC9D,MAAM,SAAS,GAAG,EAAuB;IACzC,MAAM,MAAM,GAAG,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI;IAEzC,QACEA,IAAC,SAAS,EAAA,EACR,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,UAAU,CAAC,YAAY,EAAE,kBAAkB,EAAE,SAAS,CAAC,EAAA,GAC9D,IAAI,EAAA,IAGH,MAAM,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,MAAM,EAAE,GAAG,EAAE,CAAC,YAEhD,QAAQ,EAAA,CACC;AAEhB,CAAC;;"}
|
|
@@ -22,8 +22,14 @@ export { validAlignContents, validAlignItems, validAlignSelfs, validAlignments,
|
|
|
22
22
|
* `background-color: var(--bulma-scheme-*)` style (`undefined` for all other
|
|
23
23
|
* inputs). Merge it with a user `style` prop via `mergeBulmaStyles`.
|
|
24
24
|
*
|
|
25
|
+
* The flex container props (`flexDirection`, `flexWrap`, `justifyContent`,
|
|
26
|
+
* `alignContent`, `alignItems`) emit a class only on a flex container: when a
|
|
27
|
+
* `display` prop is `flex` or `inline-flex`, or when `options.flexContainer`
|
|
28
|
+
* says the element's own stylesheet makes it one.
|
|
29
|
+
*
|
|
25
30
|
* @function useBulmaClasses
|
|
26
31
|
* @param props - Combination of BulmaClassesPropsWithScheme and additional props.
|
|
32
|
+
* @param options - Passed to `useFlexboxClasses`: whether the element is a flex container without a display prop.
|
|
27
33
|
* @returns An object containing the Bulma helper classes, optional helper
|
|
28
34
|
* styles, and unhandled props.
|
|
29
35
|
* @example
|
|
@@ -36,7 +42,7 @@ export { validAlignContents, validAlignItems, validAlignSelfs, validAlignments,
|
|
|
36
42
|
* // bulmaHelperStyles: { backgroundColor: 'var(--bulma-scheme-main-bis)' }
|
|
37
43
|
* // rest: { className: 'custom-class' }
|
|
38
44
|
*/
|
|
39
|
-
const useBulmaClasses = (props) => {
|
|
45
|
+
const useBulmaClasses = (props, options) => {
|
|
40
46
|
const { color, backgroundColor, colorShade, backgroundColorShade, m, mt, mr, mb, ml, mx, my, p, pt, pr, pb, pl, px, py, gap, columnGap, rowGap, gapless, textSize, textAlign, textTransform, textWeight, fontFamily, display, visibility, flexDirection, flexWrap, justifyContent, alignContent, alignItems, alignSelf, flexGrow, flexShrink, float, overflow, overflowX, overflowY, overlay, interaction, cursor, radius, shadow, responsive, viewport, displayMobile, displayTablet, displayTabletOnly, displayTouch, displayDesktop, displayDesktopOnly, displayWidescreen, displayWidescreenOnly, displayFullhd, textSizeMobile, textSizeTablet, textSizeDesktop, textSizeWidescreen, textSizeFullhd, textAlignMobile, textAlignTablet, textAlignDesktop, textAlignWidescreen, textAlignFullhd, visibilityMobile, visibilityTablet, visibilityTabletOnly, visibilityTouch, visibilityDesktop, visibilityDesktopOnly, visibilityWidescreen, visibilityWidescreenOnly, visibilityFullhd, skeleton, clearfix, pos, relative, fullHeight, aspectRatio, ...rest } = props;
|
|
41
47
|
const colorClasses = useColorClasses({
|
|
42
48
|
color,
|
|
@@ -125,7 +131,7 @@ const useBulmaClasses = (props) => {
|
|
|
125
131
|
displayWidescreen,
|
|
126
132
|
displayWidescreenOnly,
|
|
127
133
|
displayFullhd,
|
|
128
|
-
});
|
|
134
|
+
}, options);
|
|
129
135
|
const otherClasses = useOtherClasses({
|
|
130
136
|
float,
|
|
131
137
|
overflow,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useBulmaClasses.js","sources":["../../../src/helpers/useBulmaClasses.tsx"],"sourcesContent":["import { useMemo, type CSSProperties } from 'react';\nimport { classNames } from '../helpers/classNames';\nimport {\n BulmaDisplayProps,\n BulmaViewportProps,\n type BulmaGapStep,\n} from './bulmaClassHelpers';\nimport {\n useColorClasses,\n useColorStyles,\n BulmaColorProps,\n BulmaColorPropsWithScheme,\n} from './useColorClasses';\nimport { useSpacingClasses, BulmaSpacingProps } from './useSpacingClasses';\nimport {\n useTypographyClasses,\n BulmaTypographyProps,\n} from './useTypographyClasses';\nimport {\n useVisibilityClasses,\n BulmaVisibilityProps,\n} from './useVisibilityClasses';\nimport { useFlexboxClasses, BulmaFlexboxProps } from './useFlexboxClasses';\nimport { useOtherClasses, BulmaOtherProps } from './useOtherClasses';\n\n/**\n * Props for applying Bulma helper classes to components.\n */\nexport interface BulmaClassesProps\n extends\n BulmaColorProps,\n BulmaSpacingProps,\n BulmaTypographyProps,\n BulmaVisibilityProps,\n BulmaFlexboxProps,\n BulmaOtherProps {}\n\n/**\n * {@link BulmaClassesProps} with `backgroundColor` widened to also accept the\n * scheme color names from `validSchemeColors` (see\n * {@link BulmaColorPropsWithScheme}). This is the input type of\n * `useBulmaClasses`; scheme values surface as the `bulmaHelperStyles` return\n * member instead of a class.\n */\nexport type BulmaClassesPropsWithScheme = Omit<\n BulmaClassesProps,\n keyof BulmaColorProps\n> &\n BulmaColorPropsWithScheme;\n\n/**\n * A hook that generates Bulma helper classes from props and separates unhandled props.\n *\n * Composed from the per-concern mini hooks (useColorClasses, useSpacingClasses,\n * useTypographyClasses, useVisibilityClasses, useFlexboxClasses, and\n * useOtherClasses), which can also be used individually.\n *\n * Scheme background values (`backgroundColor: 'scheme-main-bis'`, …) emit no\n * class; they are returned as `bulmaHelperStyles` — a dark-mode-safe inline\n * `background-color: var(--bulma-scheme-*)` style (`undefined` for all other\n * inputs). Merge it with a user `style` prop via `mergeBulmaStyles`.\n *\n * @function useBulmaClasses\n * @param props - Combination of BulmaClassesPropsWithScheme and additional props.\n * @returns An object containing the Bulma helper classes, optional helper\n * styles, and unhandled props.\n * @example\n * const { bulmaHelperClasses, bulmaHelperStyles, rest } = useBulmaClasses({\n * color: 'primary',\n * backgroundColor: 'scheme-main-bis',\n * className: 'custom-class'\n * });\n * // bulmaHelperClasses: 'has-text-primary'\n * // bulmaHelperStyles: { backgroundColor: 'var(--bulma-scheme-main-bis)' }\n * // rest: { className: 'custom-class' }\n */\nexport const useBulmaClasses = <T extends object>(\n props: BulmaClassesPropsWithScheme & T\n): {\n bulmaHelperClasses: string;\n bulmaHelperStyles?: CSSProperties;\n rest: Omit<T, keyof BulmaClassesProps>;\n} => {\n const {\n color,\n backgroundColor,\n colorShade,\n backgroundColorShade,\n m,\n mt,\n mr,\n mb,\n ml,\n mx,\n my,\n p,\n pt,\n pr,\n pb,\n pl,\n px,\n py,\n gap,\n columnGap,\n rowGap,\n gapless,\n textSize,\n textAlign,\n textTransform,\n textWeight,\n fontFamily,\n display,\n visibility,\n flexDirection,\n flexWrap,\n justifyContent,\n alignContent,\n alignItems,\n alignSelf,\n flexGrow,\n flexShrink,\n float,\n overflow,\n overflowX,\n overflowY,\n overlay,\n interaction,\n cursor,\n radius,\n shadow,\n responsive,\n viewport,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n textSizeMobile,\n textSizeTablet,\n textSizeDesktop,\n textSizeWidescreen,\n textSizeFullhd,\n textAlignMobile,\n textAlignTablet,\n textAlignDesktop,\n textAlignWidescreen,\n textAlignFullhd,\n visibilityMobile,\n visibilityTablet,\n visibilityTabletOnly,\n visibilityTouch,\n visibilityDesktop,\n visibilityDesktopOnly,\n visibilityWidescreen,\n visibilityWidescreenOnly,\n visibilityFullhd,\n skeleton,\n clearfix,\n pos,\n relative,\n fullHeight,\n aspectRatio,\n ...rest\n } = props;\n\n const colorClasses = useColorClasses({\n color,\n colorShade,\n backgroundColor,\n backgroundColorShade,\n });\n\n const bulmaHelperStyles = useColorStyles({ backgroundColor });\n\n const spacingClasses = useSpacingClasses({\n m,\n mt,\n mr,\n mb,\n ml,\n mx,\n my,\n p,\n pt,\n pr,\n pb,\n pl,\n px,\n py,\n gap,\n columnGap,\n rowGap,\n gapless,\n });\n\n const typographyClasses = useTypographyClasses({\n textSize,\n textAlign,\n textTransform,\n textWeight,\n fontFamily,\n viewport,\n textSizeMobile,\n textSizeTablet,\n textSizeDesktop,\n textSizeWidescreen,\n textSizeFullhd,\n textAlignMobile,\n textAlignTablet,\n textAlignDesktop,\n textAlignWidescreen,\n textAlignFullhd,\n });\n\n const visibilityClasses = useVisibilityClasses({\n visibility,\n visibilityMobile,\n visibilityTablet,\n visibilityTabletOnly,\n visibilityTouch,\n visibilityDesktop,\n visibilityDesktopOnly,\n visibilityWidescreen,\n visibilityWidescreenOnly,\n visibilityFullhd,\n display,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n viewport,\n });\n\n const flexboxClasses = useFlexboxClasses({\n flexDirection,\n flexWrap,\n justifyContent,\n alignContent,\n alignItems,\n alignSelf,\n flexGrow,\n flexShrink,\n display,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n });\n\n const otherClasses = useOtherClasses({\n float,\n overflow,\n overflowX,\n overflowY,\n overlay,\n interaction,\n cursor,\n radius,\n shadow,\n responsive,\n skeleton,\n clearfix,\n pos,\n relative,\n fullHeight,\n aspectRatio,\n });\n\n const bulmaHelperClasses = useMemo(\n () =>\n classNames(\n colorClasses,\n spacingClasses,\n typographyClasses,\n visibilityClasses,\n flexboxClasses,\n otherClasses\n ),\n [\n colorClasses,\n spacingClasses,\n typographyClasses,\n visibilityClasses,\n flexboxClasses,\n otherClasses,\n ]\n );\n\n return { bulmaHelperClasses, bulmaHelperStyles, rest };\n};\n\nexport {\n validColors,\n validColorShades,\n validSchemeColors,\n validSizes,\n validGaps,\n validTextSizes,\n validAlignments,\n validTextTransforms,\n validTextWeights,\n validFontFamilies,\n validDisplays,\n validVisibilities,\n validFlexDirections,\n validFlexWraps,\n validJustifyContents,\n validAlignContents,\n validAlignItems,\n validAlignSelfs,\n validFlexGrowShrink,\n validViewports,\n validFloats,\n validOverflows,\n validAxisOverflows,\n validInteractions,\n validCursors,\n validRadii,\n validShadows,\n validResponsives,\n validPositions,\n validAspectRatios,\n} from './bulmaClassHelpers';\nexport type { BulmaViewportProps, BulmaDisplayProps, BulmaGapStep };\nexport * from './useColorClasses';\nexport * from './useSpacingClasses';\nexport * from './useTypographyClasses';\nexport * from './useVisibilityClasses';\nexport * from './useFlexboxClasses';\nexport * from './useOtherClasses';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useBulmaClasses.js","sources":["../../../src/helpers/useBulmaClasses.tsx"],"sourcesContent":["import { useMemo, type CSSProperties } from 'react';\nimport { classNames } from '../helpers/classNames';\nimport {\n BulmaDisplayProps,\n BulmaViewportProps,\n type BulmaGapStep,\n} from './bulmaClassHelpers';\nimport {\n useColorClasses,\n useColorStyles,\n BulmaColorProps,\n BulmaColorPropsWithScheme,\n} from './useColorClasses';\nimport { useSpacingClasses, BulmaSpacingProps } from './useSpacingClasses';\nimport {\n useTypographyClasses,\n BulmaTypographyProps,\n} from './useTypographyClasses';\nimport {\n useVisibilityClasses,\n BulmaVisibilityProps,\n} from './useVisibilityClasses';\nimport {\n useFlexboxClasses,\n BulmaFlexboxProps,\n type UseFlexboxClassesOptions,\n} from './useFlexboxClasses';\nimport { useOtherClasses, BulmaOtherProps } from './useOtherClasses';\n\n/**\n * Props for applying Bulma helper classes to components.\n */\nexport interface BulmaClassesProps\n extends\n BulmaColorProps,\n BulmaSpacingProps,\n BulmaTypographyProps,\n BulmaVisibilityProps,\n BulmaFlexboxProps,\n BulmaOtherProps {}\n\n/**\n * {@link BulmaClassesProps} with `backgroundColor` widened to also accept the\n * scheme color names from `validSchemeColors` (see\n * {@link BulmaColorPropsWithScheme}). This is the input type of\n * `useBulmaClasses`; scheme values surface as the `bulmaHelperStyles` return\n * member instead of a class.\n */\nexport type BulmaClassesPropsWithScheme = Omit<\n BulmaClassesProps,\n keyof BulmaColorProps\n> &\n BulmaColorPropsWithScheme;\n\n/**\n * A hook that generates Bulma helper classes from props and separates unhandled props.\n *\n * Composed from the per-concern mini hooks (useColorClasses, useSpacingClasses,\n * useTypographyClasses, useVisibilityClasses, useFlexboxClasses, and\n * useOtherClasses), which can also be used individually.\n *\n * Scheme background values (`backgroundColor: 'scheme-main-bis'`, …) emit no\n * class; they are returned as `bulmaHelperStyles` — a dark-mode-safe inline\n * `background-color: var(--bulma-scheme-*)` style (`undefined` for all other\n * inputs). Merge it with a user `style` prop via `mergeBulmaStyles`.\n *\n * The flex container props (`flexDirection`, `flexWrap`, `justifyContent`,\n * `alignContent`, `alignItems`) emit a class only on a flex container: when a\n * `display` prop is `flex` or `inline-flex`, or when `options.flexContainer`\n * says the element's own stylesheet makes it one.\n *\n * @function useBulmaClasses\n * @param props - Combination of BulmaClassesPropsWithScheme and additional props.\n * @param options - Passed to `useFlexboxClasses`: whether the element is a flex container without a display prop.\n * @returns An object containing the Bulma helper classes, optional helper\n * styles, and unhandled props.\n * @example\n * const { bulmaHelperClasses, bulmaHelperStyles, rest } = useBulmaClasses({\n * color: 'primary',\n * backgroundColor: 'scheme-main-bis',\n * className: 'custom-class'\n * });\n * // bulmaHelperClasses: 'has-text-primary'\n * // bulmaHelperStyles: { backgroundColor: 'var(--bulma-scheme-main-bis)' }\n * // rest: { className: 'custom-class' }\n */\nexport const useBulmaClasses = <T extends object>(\n props: BulmaClassesPropsWithScheme & T,\n options?: UseFlexboxClassesOptions\n): {\n bulmaHelperClasses: string;\n bulmaHelperStyles?: CSSProperties;\n rest: Omit<T, keyof BulmaClassesProps>;\n} => {\n const {\n color,\n backgroundColor,\n colorShade,\n backgroundColorShade,\n m,\n mt,\n mr,\n mb,\n ml,\n mx,\n my,\n p,\n pt,\n pr,\n pb,\n pl,\n px,\n py,\n gap,\n columnGap,\n rowGap,\n gapless,\n textSize,\n textAlign,\n textTransform,\n textWeight,\n fontFamily,\n display,\n visibility,\n flexDirection,\n flexWrap,\n justifyContent,\n alignContent,\n alignItems,\n alignSelf,\n flexGrow,\n flexShrink,\n float,\n overflow,\n overflowX,\n overflowY,\n overlay,\n interaction,\n cursor,\n radius,\n shadow,\n responsive,\n viewport,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n textSizeMobile,\n textSizeTablet,\n textSizeDesktop,\n textSizeWidescreen,\n textSizeFullhd,\n textAlignMobile,\n textAlignTablet,\n textAlignDesktop,\n textAlignWidescreen,\n textAlignFullhd,\n visibilityMobile,\n visibilityTablet,\n visibilityTabletOnly,\n visibilityTouch,\n visibilityDesktop,\n visibilityDesktopOnly,\n visibilityWidescreen,\n visibilityWidescreenOnly,\n visibilityFullhd,\n skeleton,\n clearfix,\n pos,\n relative,\n fullHeight,\n aspectRatio,\n ...rest\n } = props;\n\n const colorClasses = useColorClasses({\n color,\n colorShade,\n backgroundColor,\n backgroundColorShade,\n });\n\n const bulmaHelperStyles = useColorStyles({ backgroundColor });\n\n const spacingClasses = useSpacingClasses({\n m,\n mt,\n mr,\n mb,\n ml,\n mx,\n my,\n p,\n pt,\n pr,\n pb,\n pl,\n px,\n py,\n gap,\n columnGap,\n rowGap,\n gapless,\n });\n\n const typographyClasses = useTypographyClasses({\n textSize,\n textAlign,\n textTransform,\n textWeight,\n fontFamily,\n viewport,\n textSizeMobile,\n textSizeTablet,\n textSizeDesktop,\n textSizeWidescreen,\n textSizeFullhd,\n textAlignMobile,\n textAlignTablet,\n textAlignDesktop,\n textAlignWidescreen,\n textAlignFullhd,\n });\n\n const visibilityClasses = useVisibilityClasses({\n visibility,\n visibilityMobile,\n visibilityTablet,\n visibilityTabletOnly,\n visibilityTouch,\n visibilityDesktop,\n visibilityDesktopOnly,\n visibilityWidescreen,\n visibilityWidescreenOnly,\n visibilityFullhd,\n display,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n viewport,\n });\n\n const flexboxClasses = useFlexboxClasses(\n {\n flexDirection,\n flexWrap,\n justifyContent,\n alignContent,\n alignItems,\n alignSelf,\n flexGrow,\n flexShrink,\n display,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n },\n options\n );\n\n const otherClasses = useOtherClasses({\n float,\n overflow,\n overflowX,\n overflowY,\n overlay,\n interaction,\n cursor,\n radius,\n shadow,\n responsive,\n skeleton,\n clearfix,\n pos,\n relative,\n fullHeight,\n aspectRatio,\n });\n\n const bulmaHelperClasses = useMemo(\n () =>\n classNames(\n colorClasses,\n spacingClasses,\n typographyClasses,\n visibilityClasses,\n flexboxClasses,\n otherClasses\n ),\n [\n colorClasses,\n spacingClasses,\n typographyClasses,\n visibilityClasses,\n flexboxClasses,\n otherClasses,\n ]\n );\n\n return { bulmaHelperClasses, bulmaHelperStyles, rest };\n};\n\nexport {\n validColors,\n validColorShades,\n validSchemeColors,\n validSizes,\n validGaps,\n validTextSizes,\n validAlignments,\n validTextTransforms,\n validTextWeights,\n validFontFamilies,\n validDisplays,\n validVisibilities,\n validFlexDirections,\n validFlexWraps,\n validJustifyContents,\n validAlignContents,\n validAlignItems,\n validAlignSelfs,\n validFlexGrowShrink,\n validViewports,\n validFloats,\n validOverflows,\n validAxisOverflows,\n validInteractions,\n validCursors,\n validRadii,\n validShadows,\n validResponsives,\n validPositions,\n validAspectRatios,\n} from './bulmaClassHelpers';\nexport type { BulmaViewportProps, BulmaDisplayProps, BulmaGapStep };\nexport * from './useColorClasses';\nexport * from './useSpacingClasses';\nexport * from './useTypographyClasses';\nexport * from './useVisibilityClasses';\nexport * from './useFlexboxClasses';\nexport * from './useOtherClasses';\n"],"names":[],"mappings":"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"}
|
|
@@ -8,8 +8,9 @@ import { createBulmaClassHelpers, validFlexDirections, validFlexWraps, validJust
|
|
|
8
8
|
/**
|
|
9
9
|
* A hook that generates Bulma flexbox helper classes. Flex container helpers
|
|
10
10
|
* (direction, wrap, justify/align content, align items) are only emitted when
|
|
11
|
-
* one of the display props indicates a flex display
|
|
12
|
-
*
|
|
11
|
+
* one of the display props indicates a flex display, or when `options` says
|
|
12
|
+
* the element is a flex container by its own CSS; flex item helpers (align
|
|
13
|
+
* self, grow, shrink) are always emitted.
|
|
13
14
|
*
|
|
14
15
|
* Note: this hook reads the display props purely as a gate and never emits
|
|
15
16
|
* display classes — `is-flex` and friends come from useVisibilityClasses (or
|
|
@@ -17,6 +18,7 @@ import { createBulmaClassHelpers, validFlexDirections, validFlexWraps, validJust
|
|
|
17
18
|
*
|
|
18
19
|
* @function useFlexboxClasses
|
|
19
20
|
* @param props - Flexbox-related Bulma helper props.
|
|
21
|
+
* @param options - Whether the element is a flex container without a display prop.
|
|
20
22
|
* @returns A space-separated string of flexbox helper classes.
|
|
21
23
|
* @example
|
|
22
24
|
* const flexboxClasses = useFlexboxClasses({
|
|
@@ -25,14 +27,24 @@ import { createBulmaClassHelpers, validFlexDirections, validFlexWraps, validJust
|
|
|
25
27
|
* });
|
|
26
28
|
* // flexboxClasses: 'is-justify-content-center'
|
|
27
29
|
* // ('is-flex' itself comes from useVisibilityClasses/useBulmaClasses)
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // An element whose own stylesheet makes it a flex container
|
|
33
|
+
* const flexboxClasses = useFlexboxClasses(
|
|
34
|
+
* { alignItems: 'center' },
|
|
35
|
+
* { flexContainer: true }
|
|
36
|
+
* );
|
|
37
|
+
* // flexboxClasses: 'is-align-items-center'
|
|
28
38
|
*/
|
|
29
|
-
const useFlexboxClasses = (props) => {
|
|
39
|
+
const useFlexboxClasses = (props, options = {}) => {
|
|
30
40
|
const { classPrefix } = useConfig();
|
|
41
|
+
const { flexContainer = false } = options;
|
|
31
42
|
const { flexDirection, flexWrap, justifyContent, alignContent, alignItems, alignSelf, flexGrow, flexShrink, display, displayMobile, displayTablet, displayTabletOnly, displayTouch, displayDesktop, displayDesktopOnly, displayWidescreen, displayWidescreenOnly, displayFullhd, } = props;
|
|
32
43
|
return useMemo(() => {
|
|
33
44
|
const { classes, addClassNoViewport } = createBulmaClassHelpers(classPrefix);
|
|
34
45
|
// Flexbox
|
|
35
|
-
const hasFlexDisplay =
|
|
46
|
+
const hasFlexDisplay = flexContainer ||
|
|
47
|
+
display === 'flex' ||
|
|
36
48
|
display === 'inline-flex' ||
|
|
37
49
|
displayMobile === 'flex' ||
|
|
38
50
|
displayMobile === 'inline-flex' ||
|
|
@@ -68,6 +80,7 @@ const useFlexboxClasses = (props) => {
|
|
|
68
80
|
return classNames(classes);
|
|
69
81
|
}, [
|
|
70
82
|
classPrefix,
|
|
83
|
+
flexContainer,
|
|
71
84
|
flexDirection,
|
|
72
85
|
flexWrap,
|
|
73
86
|
justifyContent,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFlexboxClasses.js","sources":["../../../src/helpers/useFlexboxClasses.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { classNames } from './classNames';\nimport { useConfig } from './Config';\nimport {\n createBulmaClassHelpers,\n validFlexDirections,\n validFlexWraps,\n validJustifyContents,\n validAlignContents,\n validAlignItems,\n validAlignSelfs,\n validFlexGrowShrink,\n BulmaDisplayProps,\n} from './bulmaClassHelpers';\n\n/**\n * Props for applying Bulma flexbox helper classes. The display props are read\n * only to gate the flex container helpers; this hook never emits display\n * classes itself.\n */\nexport interface BulmaFlexboxProps extends BulmaDisplayProps {\n /** Flex direction (e.g., 'row', 'column'). */\n flexDirection?: (typeof validFlexDirections)[number];\n /** Flex wrap (e.g., 'wrap', 'nowrap'). */\n flexWrap?: (typeof validFlexWraps)[number];\n /** Justify content (e.g., 'center', 'space-between'). */\n justifyContent?: (typeof validJustifyContents)[number];\n /** Align content (e.g., 'center', 'stretch'). */\n alignContent?: (typeof validAlignContents)[number];\n /** Align items (e.g., 'center', 'flex-start'). */\n alignItems?: (typeof validAlignItems)[number];\n /** Align self (e.g., 'auto', 'center'). */\n alignSelf?: (typeof validAlignSelfs)[number];\n /** Flex grow value (e.g., '0', '1', '2', '3', '4', '5'). */\n flexGrow?: (typeof validFlexGrowShrink)[number];\n /** Flex shrink value (e.g., '0', '1', '2', '3', '4', '5'). */\n flexShrink?: (typeof validFlexGrowShrink)[number];\n}\n\n/**\n * A hook that generates Bulma flexbox helper classes. Flex container helpers\n * (direction, wrap, justify/align content, align items) are only emitted when\n * one of the display props indicates a flex display; flex item helpers\n *
|
|
1
|
+
{"version":3,"file":"useFlexboxClasses.js","sources":["../../../src/helpers/useFlexboxClasses.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { classNames } from './classNames';\nimport { useConfig } from './Config';\nimport {\n createBulmaClassHelpers,\n validFlexDirections,\n validFlexWraps,\n validJustifyContents,\n validAlignContents,\n validAlignItems,\n validAlignSelfs,\n validFlexGrowShrink,\n BulmaDisplayProps,\n} from './bulmaClassHelpers';\n\n/**\n * Props for applying Bulma flexbox helper classes. The display props are read\n * only to gate the flex container helpers; this hook never emits display\n * classes itself.\n */\nexport interface BulmaFlexboxProps extends BulmaDisplayProps {\n /** Flex direction (e.g., 'row', 'column'). */\n flexDirection?: (typeof validFlexDirections)[number];\n /** Flex wrap (e.g., 'wrap', 'nowrap'). */\n flexWrap?: (typeof validFlexWraps)[number];\n /** Justify content (e.g., 'center', 'space-between'). */\n justifyContent?: (typeof validJustifyContents)[number];\n /** Align content (e.g., 'center', 'stretch'). */\n alignContent?: (typeof validAlignContents)[number];\n /** Align items (e.g., 'center', 'flex-start'). */\n alignItems?: (typeof validAlignItems)[number];\n /** Align self (e.g., 'auto', 'center'). */\n alignSelf?: (typeof validAlignSelfs)[number];\n /** Flex grow value (e.g., '0', '1', '2', '3', '4', '5'). */\n flexGrow?: (typeof validFlexGrowShrink)[number];\n /** Flex shrink value (e.g., '0', '1', '2', '3', '4', '5'). */\n flexShrink?: (typeof validFlexGrowShrink)[number];\n}\n\n/**\n * Options for `useFlexboxClasses`, which `useBulmaClasses` passes through.\n */\nexport interface UseFlexboxClassesOptions {\n /**\n * Whether the element is a flex container by its own CSS, as `Stack` and\n * `Group` are, so the container helpers apply with no `display` prop. It\n * adds no display class: the element's own stylesheet has to make it a flex\n * container.\n * @defaultValue false\n */\n flexContainer?: boolean;\n}\n\n/**\n * A hook that generates Bulma flexbox helper classes. Flex container helpers\n * (direction, wrap, justify/align content, align items) are only emitted when\n * one of the display props indicates a flex display, or when `options` says\n * the element is a flex container by its own CSS; flex item helpers (align\n * self, grow, shrink) are always emitted.\n *\n * Note: this hook reads the display props purely as a gate and never emits\n * display classes — `is-flex` and friends come from useVisibilityClasses (or\n * the useBulmaClasses aggregate).\n *\n * @function useFlexboxClasses\n * @param props - Flexbox-related Bulma helper props.\n * @param options - Whether the element is a flex container without a display prop.\n * @returns A space-separated string of flexbox helper classes.\n * @example\n * const flexboxClasses = useFlexboxClasses({\n * display: 'flex', // gates the container helpers, but emits no 'is-flex'\n * justifyContent: 'center',\n * });\n * // flexboxClasses: 'is-justify-content-center'\n * // ('is-flex' itself comes from useVisibilityClasses/useBulmaClasses)\n *\n * @example\n * // An element whose own stylesheet makes it a flex container\n * const flexboxClasses = useFlexboxClasses(\n * { alignItems: 'center' },\n * { flexContainer: true }\n * );\n * // flexboxClasses: 'is-align-items-center'\n */\nexport const useFlexboxClasses = (\n props: BulmaFlexboxProps,\n options: UseFlexboxClassesOptions = {}\n): string => {\n const { classPrefix } = useConfig();\n const { flexContainer = false } = options;\n\n const {\n flexDirection,\n flexWrap,\n justifyContent,\n alignContent,\n alignItems,\n alignSelf,\n flexGrow,\n flexShrink,\n display,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n } = props;\n\n return useMemo(() => {\n const { classes, addClassNoViewport } =\n createBulmaClassHelpers(classPrefix);\n\n // Flexbox\n const hasFlexDisplay =\n flexContainer ||\n display === 'flex' ||\n display === 'inline-flex' ||\n displayMobile === 'flex' ||\n displayMobile === 'inline-flex' ||\n displayTablet === 'flex' ||\n displayTablet === 'inline-flex' ||\n displayTabletOnly === 'flex' ||\n displayTabletOnly === 'inline-flex' ||\n displayTouch === 'flex' ||\n displayTouch === 'inline-flex' ||\n displayDesktop === 'flex' ||\n displayDesktop === 'inline-flex' ||\n displayDesktopOnly === 'flex' ||\n displayDesktopOnly === 'inline-flex' ||\n displayWidescreen === 'flex' ||\n displayWidescreen === 'inline-flex' ||\n displayWidescreenOnly === 'flex' ||\n displayWidescreenOnly === 'inline-flex' ||\n displayFullhd === 'flex' ||\n displayFullhd === 'inline-flex';\n\n if (hasFlexDisplay) {\n // Flexbox container properties do not support viewport modifiers in Bulma\n addClassNoViewport(\n 'is-flex-direction',\n flexDirection,\n validFlexDirections\n );\n addClassNoViewport('is-flex-wrap', flexWrap, validFlexWraps);\n addClassNoViewport(\n 'is-justify-content',\n justifyContent,\n validJustifyContents\n );\n addClassNoViewport('is-align-content', alignContent, validAlignContents);\n addClassNoViewport('is-align-items', alignItems, validAlignItems);\n }\n\n // Flex item properties (can be applied to any element that is a flex item)\n // These don't require the element itself to have display: flex\n addClassNoViewport('is-align-self', alignSelf, validAlignSelfs);\n addClassNoViewport('is-flex-grow', flexGrow, validFlexGrowShrink);\n addClassNoViewport('is-flex-shrink', flexShrink, validFlexGrowShrink);\n\n return classNames(classes);\n }, [\n classPrefix,\n flexContainer,\n flexDirection,\n flexWrap,\n justifyContent,\n alignContent,\n alignItems,\n alignSelf,\n flexGrow,\n flexShrink,\n display,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n ]);\n};\n"],"names":[],"mappings":";;;;;;;AAqDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BG;AACI,MAAM,iBAAiB,GAAG,CAC/B,KAAwB,EACxB,OAAA,GAAoC,EAAE,KAC5B;AACV,IAAA,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,EAAE;AACnC,IAAA,MAAM,EAAE,aAAa,GAAG,KAAK,EAAE,GAAG,OAAO;AAEzC,IAAA,MAAM,EACJ,aAAa,EACb,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,UAAU,EACV,SAAS,EACT,QAAQ,EACR,UAAU,EACV,OAAO,EACP,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,GACd,GAAG,KAAK;IAET,OAAO,OAAO,CAAC,MAAK;QAClB,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,GACnC,uBAAuB,CAAC,WAAW,CAAC;;QAGtC,MAAM,cAAc,GAClB,aAAa;AACb,YAAA,OAAO,KAAK,MAAM;AAClB,YAAA,OAAO,KAAK,aAAa;AACzB,YAAA,aAAa,KAAK,MAAM;AACxB,YAAA,aAAa,KAAK,aAAa;AAC/B,YAAA,aAAa,KAAK,MAAM;AACxB,YAAA,aAAa,KAAK,aAAa;AAC/B,YAAA,iBAAiB,KAAK,MAAM;AAC5B,YAAA,iBAAiB,KAAK,aAAa;AACnC,YAAA,YAAY,KAAK,MAAM;AACvB,YAAA,YAAY,KAAK,aAAa;AAC9B,YAAA,cAAc,KAAK,MAAM;AACzB,YAAA,cAAc,KAAK,aAAa;AAChC,YAAA,kBAAkB,KAAK,MAAM;AAC7B,YAAA,kBAAkB,KAAK,aAAa;AACpC,YAAA,iBAAiB,KAAK,MAAM;AAC5B,YAAA,iBAAiB,KAAK,aAAa;AACnC,YAAA,qBAAqB,KAAK,MAAM;AAChC,YAAA,qBAAqB,KAAK,aAAa;AACvC,YAAA,aAAa,KAAK,MAAM;YACxB,aAAa,KAAK,aAAa;QAEjC,IAAI,cAAc,EAAE;;AAElB,YAAA,kBAAkB,CAChB,mBAAmB,EACnB,aAAa,EACb,mBAAmB,CACpB;AACD,YAAA,kBAAkB,CAAC,cAAc,EAAE,QAAQ,EAAE,cAAc,CAAC;AAC5D,YAAA,kBAAkB,CAChB,oBAAoB,EACpB,cAAc,EACd,oBAAoB,CACrB;AACD,YAAA,kBAAkB,CAAC,kBAAkB,EAAE,YAAY,EAAE,kBAAkB,CAAC;AACxE,YAAA,kBAAkB,CAAC,gBAAgB,EAAE,UAAU,EAAE,eAAe,CAAC;QACnE;;;AAIA,QAAA,kBAAkB,CAAC,eAAe,EAAE,SAAS,EAAE,eAAe,CAAC;AAC/D,QAAA,kBAAkB,CAAC,cAAc,EAAE,QAAQ,EAAE,mBAAmB,CAAC;AACjE,QAAA,kBAAkB,CAAC,gBAAgB,EAAE,UAAU,EAAE,mBAAmB,CAAC;AAErE,QAAA,OAAO,UAAU,CAAC,OAAO,CAAC;AAC5B,IAAA,CAAC,EAAE;QACD,WAAW;QACX,aAAa;QACb,aAAa;QACb,QAAQ;QACR,cAAc;QACd,YAAY;QACZ,UAAU;QACV,SAAS;QACT,QAAQ;QACR,UAAU;QACV,OAAO;QACP,aAAa;QACb,aAAa;QACb,iBAAiB;QACjB,YAAY;QACZ,cAAc;QACd,kBAAkB;QAClB,iBAAiB;QACjB,qBAAqB;QACrB,aAAa;AACd,KAAA,CAAC;AACJ;;"}
|