create-bestax 4.2.18 → 4.2.19
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/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -1
- package/dist/file-system.d.ts +5 -1
- package/dist/file-system.d.ts.map +1 -1
- package/dist/file-system.js +11 -1
- package/dist/library-range.d.ts +16 -0
- package/dist/library-range.d.ts.map +1 -0
- package/dist/library-range.js +40 -0
- package/dist/project-creator.d.ts.map +1 -1
- package/dist/project-creator.js +4 -1
- package/package.json +7 -7
- package/templates/skills/bestax-custom-component/references/component-catalog.md +3 -1
- package/templates/skills/bestax-form/SKILL.md +40 -15
- package/templates/skills/bestax-form/references/api.md +10 -8
- package/templates/skills/bestax-form/references/patterns.md +68 -0
- package/templates/skills/bestax-layout-scaffold/SKILL.md +16 -5
- package/templates/skills/bestax-layout-scaffold/references/archetypes.md +2 -2
- package/templates/skills/bestax-layout-scaffold/references/layout-components.md +52 -5
- package/templates/skills/bestax-migrate/SKILL.md +11 -3
- package/templates/skills/bestax-migrate/references/bloomer/component-map.md +1 -1
- package/templates/skills/bestax-migrate/references/bloomer/prop-map.md +8 -5
- package/templates/skills/bestax-migrate/references/bulma-classes/component-map.md +144 -101
- package/templates/skills/bestax-migrate/references/bulma-classes/prop-map.md +47 -16
- package/templates/skills/bestax-migrate/references/bulma-classes/unmappables.md +217 -45
- package/templates/skills/bestax-migrate/references/css-migration.md +12 -5
- package/templates/skills/bestax-migrate/references/rbx/prop-map.md +19 -13
- package/templates/skills/bestax-migrate/references/react-bulma-components/prop-map.md +27 -22
- package/templates/skills/bestax-migrate/references/react-bulma-components/unmappables.md +1 -1
- package/templates/skills/bestax-optimize/references/modular-build.md +57 -17
- package/templates/skills/bestax-theming/SKILL.md +19 -8
- package/templates/skills/bestax-theming/examples/dark-mode.tsx +99 -17
- package/templates/skills/bestax-theming/examples/theme-config.tsx +13 -8
- package/templates/skills/bestax-theming/references/css-variables.md +188 -18
- package/templates/skills/bestax-theming/references/themeable-components.md +2 -0
- package/templates/vite/package.json +1 -1
- package/templates/vite/src/main.jsx +1 -1
- package/templates/vite-ts/package.json +1 -1
- package/templates/vite-ts/src/main.tsx +1 -1
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,SAAS,EAAE,QAAQ,EAG/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AACpD,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAsB,CAAC;AAGtD,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAOpC,eAAO,MAAM,QAAQ;;;;;;+CASe,MAAM,QAAQ,MAAM;6CAEtB,MAAM;;;wCAQX,MAAM,WAAW,MAAM,EAAE;oCAE7B,MAAM;wCAIF,MAAM;gDAKE,MAAM,WAAW,MAAM,EAAE;uCAIlC,MAAM;sCACP,MAAM;4CAEA,MAAM;;;;;wCAMV,MAAM,mBAAmB,MAAM;;;;;CAclD,CAAC;AASX,eAAO,MAAM,WAAW,QAed,CAAC;AAEX,eAAO,MAAM,OAAO;;;;;;;CAQV,CAAC;AAOX,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK9D,CAAC;AAKF,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAkCD,eAAO,MAAM,SAAS,GACpB,aAAa,MAAM,EACnB,8BAA8B,eAAe,KAC5C,MA8HF,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IAGzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,cAAc,EAAE,WAAW,EA8DvC,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IAGtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EA8CtC,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,SAAS,EAAE,QAAQ,EAG/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AACpD,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAsB,CAAC;AAGtD,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAOpC,eAAO,MAAM,QAAQ;;;;;;+CASe,MAAM,QAAQ,MAAM;6CAEtB,MAAM;;;wCAQX,MAAM,WAAW,MAAM,EAAE;oCAE7B,MAAM;wCAIF,MAAM;gDAKE,MAAM,WAAW,MAAM,EAAE;uCAIlC,MAAM;sCACP,MAAM;4CAEA,MAAM;;;;;wCAMV,MAAM,mBAAmB,MAAM;;;;;CAclD,CAAC;AASX,eAAO,MAAM,WAAW,QAed,CAAC;AAEX,eAAO,MAAM,OAAO;;;;;;;CAQV,CAAC;AAOX,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK9D,CAAC;AAKF,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAkCD,eAAO,MAAM,SAAS,GACpB,aAAa,MAAM,EACnB,8BAA8B,eAAe,KAC5C,MA8HF,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IAGzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,cAAc,EAAE,WAAW,EA8DvC,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IAGtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EA8CtC,CAAC;AAQF,eAAO,MAAM,iBAAiB,QAEiC,CAAC;AAEhE,MAAM,WAAW,YAAY;IAE3B,IAAI,EAAE,MAAM,CAAC;IAEb,SAAS,EAAE,MAAM,CAAC;IAElB,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD,eAAO,MAAM,0BAA0B,EAAE,YAAY,EAgBpD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAMjB,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -348,8 +348,11 @@ export const BULMA_FLAVORS = [
|
|
|
348
348
|
// Sits above the stylesheet import in the templates' entry files, and the
|
|
349
349
|
// scaffolder writes it back when it swaps in a flavor's stylesheet, so the
|
|
350
350
|
// reason for the import order survives. The no-helpers classes depend on it.
|
|
351
|
+
// It names src/index.css, the app's global stylesheet, rather than the starter
|
|
352
|
+
// page's src/App.css, which loads after bestax's too but goes when a port
|
|
353
|
+
// replaces the page (#1240).
|
|
351
354
|
export const CSS_ORDER_COMMENT = "// Bestax's stylesheet loads before the app's own CSS, so a named class in\n" +
|
|
352
|
-
'// src/
|
|
355
|
+
'// src/index.css wins over a Bulma rule of the same weight.';
|
|
353
356
|
// Every helper prop the starter App uses, with the named class that stands in
|
|
354
357
|
// for it under a `noHelpers` flavor, where the prop would render nothing. The
|
|
355
358
|
// scaffolder swaps each prop for its class and writes the rules into
|
package/dist/file-system.d.ts
CHANGED
|
@@ -25,5 +25,9 @@ export declare function ensureDirectory(targetPath: string): Promise<void>;
|
|
|
25
25
|
export declare function copyDirectory(source: string, destination: string, renames?: Record<string, string>): Promise<void>;
|
|
26
26
|
export declare function readJsonFile<T = unknown>(filePath: string): Promise<T>;
|
|
27
27
|
export declare function writeJsonFile(filePath: string, data: unknown, spaces?: number): Promise<void>;
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Names the copied app's package and sets the given dependencies' ranges. One
|
|
30
|
+
* the template already lists keeps its place among the others.
|
|
31
|
+
*/
|
|
32
|
+
export declare function updatePackageJson(targetPath: string, projectName: string, dependencies?: Record<string, string>): Promise<void>;
|
|
29
33
|
//# sourceMappingURL=file-system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"AAIA,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAM3E;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkBxB;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMlE;AAED,4EAA4E;AAC5E,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAKnB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE5E;AAED,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,MAAM,SAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"AAIA,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAM3E;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkBxB;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMlE;AAED,4EAA4E;AAC5E,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAKnB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE5E;AAED,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,MAAM,SAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACxC,OAAO,CAAC,IAAI,CAAC,CAcf"}
|
package/dist/file-system.js
CHANGED
|
@@ -90,11 +90,21 @@ export async function readJsonFile(filePath) {
|
|
|
90
90
|
export async function writeJsonFile(filePath, data, spaces = 2) {
|
|
91
91
|
await fs.writeJson(filePath, data, { spaces });
|
|
92
92
|
}
|
|
93
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Names the copied app's package and sets the given dependencies' ranges. One
|
|
95
|
+
* the template already lists keeps its place among the others.
|
|
96
|
+
*/
|
|
97
|
+
export async function updatePackageJson(targetPath, projectName, dependencies = {}) {
|
|
94
98
|
const packageJsonPath = path.join(targetPath, 'package.json');
|
|
95
99
|
if (fs.existsSync(packageJsonPath)) {
|
|
96
100
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
97
101
|
packageJson.name = projectName;
|
|
102
|
+
if (Object.keys(dependencies).length > 0) {
|
|
103
|
+
packageJson.dependencies = {
|
|
104
|
+
...packageJson.dependencies,
|
|
105
|
+
...dependencies,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
98
108
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
99
109
|
}
|
|
100
110
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** The library every template is built on. */
|
|
2
|
+
export declare const LIBRARY = "@allxsmith/bestax-bulma";
|
|
3
|
+
/**
|
|
4
|
+
* The range a scaffolded app gives @allxsmith/bestax-bulma: the one this
|
|
5
|
+
* CLI's own manifest declares, so the app is floored at the release the CLI
|
|
6
|
+
* was built with (#1240). The manifest spells it `workspace:^`, which
|
|
7
|
+
* `pnpm publish` rewrites to a caret on the library's version in the checkout
|
|
8
|
+
* it packs, and CI releases bulma-ui first in the same job. A published CLI
|
|
9
|
+
* therefore reads a plain range, the one `npm create bestax` installed the
|
|
10
|
+
* library from beside it. Run from this repo, the protocol is still there,
|
|
11
|
+
* and it is resolved the way `pnpm publish` resolves it, from the version of
|
|
12
|
+
* the library the workspace links, so a scaffold made with a checkout's build
|
|
13
|
+
* gets that checkout's release.
|
|
14
|
+
*/
|
|
15
|
+
export declare function libraryRange(packageRoot?: string): string;
|
|
16
|
+
//# sourceMappingURL=library-range.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"library-range.d.ts","sourceRoot":"","sources":["../src/library-range.ts"],"names":[],"mappings":"AAIA,8CAA8C;AAC9C,eAAO,MAAM,OAAO,4BAA4B,CAAC;AAqBjD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,WAAW,GAAE,MAAqB,GAAG,MAAM,CAkBvE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
/** The library every template is built on. */
|
|
5
|
+
export const LIBRARY = '@allxsmith/bestax-bulma';
|
|
6
|
+
const WORKSPACE = 'workspace:';
|
|
7
|
+
// dist/ and src/ both sit directly under the package root.
|
|
8
|
+
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
+
function readManifest(dir) {
|
|
10
|
+
return JSON.parse(readFileSync(path.join(dir, 'package.json'), 'utf8'));
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The range a scaffolded app gives @allxsmith/bestax-bulma: the one this
|
|
14
|
+
* CLI's own manifest declares, so the app is floored at the release the CLI
|
|
15
|
+
* was built with (#1240). The manifest spells it `workspace:^`, which
|
|
16
|
+
* `pnpm publish` rewrites to a caret on the library's version in the checkout
|
|
17
|
+
* it packs, and CI releases bulma-ui first in the same job. A published CLI
|
|
18
|
+
* therefore reads a plain range, the one `npm create bestax` installed the
|
|
19
|
+
* library from beside it. Run from this repo, the protocol is still there,
|
|
20
|
+
* and it is resolved the way `pnpm publish` resolves it, from the version of
|
|
21
|
+
* the library the workspace links, so a scaffold made with a checkout's build
|
|
22
|
+
* gets that checkout's release.
|
|
23
|
+
*/
|
|
24
|
+
export function libraryRange(packageRoot = PACKAGE_ROOT) {
|
|
25
|
+
const declared = readManifest(packageRoot).dependencies?.[LIBRARY];
|
|
26
|
+
if (declared === undefined) {
|
|
27
|
+
throw new Error(`${path.join(packageRoot, 'package.json')} declares no ${LIBRARY} dependency.`);
|
|
28
|
+
}
|
|
29
|
+
if (!declared.startsWith(WORKSPACE))
|
|
30
|
+
return declared;
|
|
31
|
+
const { version } = readManifest(path.join(packageRoot, 'node_modules', LIBRARY));
|
|
32
|
+
// What pnpm publish writes for each form: `*` the version itself, `^` and
|
|
33
|
+
// `~` that operator on it, and a range as written.
|
|
34
|
+
const spec = declared.slice(WORKSPACE.length);
|
|
35
|
+
if (spec === '*')
|
|
36
|
+
return version;
|
|
37
|
+
if (spec === '^' || spec === '~')
|
|
38
|
+
return `${spec}${version}`;
|
|
39
|
+
return spec;
|
|
40
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"AA+BA,OAAO,EAWL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"AA+BA,OAAO,EAWL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAaxB,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AAEnE,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAAS;gBAEjB,YAAY,CAAC,EAAE,MAAM;IAIjC;;;OAGG;IACG,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAajE;;;;;;;;;;OAUG;IACG,sBAAsB,CAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,qBAAqB,CAAC;IAqCjC;;;;;;OAMG;YACW,qBAAqB;IAanC;;;OAGG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI;IAmBpD,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAInC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMjE,oBAAoB,CACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAgBhB;;;;;OAKG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBpE,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC;IAqBV,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAyDhB;;;;;OAKG;IACG,yBAAyB,CAC7B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmBhB,OAAO,CAAC,WAAW;IA2BnB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAcd,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAsJV,mBAAmB,CACvB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmEV,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA0ItE;;;;;;;;OAQG;YACW,eAAe;CA2B9B"}
|
package/dist/project-creator.js
CHANGED
|
@@ -9,6 +9,7 @@ import { displayHeader, displaySuccess, displayError, displayCancelled, } from '
|
|
|
9
9
|
import { validateProjectDirectory, toValidPackageName } from './validators.js';
|
|
10
10
|
import { MESSAGES, TEMPLATES, ICON_LIBRARIES, BULMA_FLAVORS, CLAUDE_MD, LAUNCH_JSON, CONFIG_PROVIDER_ICON_VALUES, NO_HELPERS_STARTER_CLASSES, NO_HELPERS_APP_CSS, CSS_ORDER_COMMENT, } from './constants.js';
|
|
11
11
|
import { detectPackageManager } from './package-manager.js';
|
|
12
|
+
import { LIBRARY, libraryRange } from './library-range.js';
|
|
12
13
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
// npm strips literal `.gitignore` files from published tarballs, so the
|
|
14
15
|
// templates track `_gitignore` instead and the scaffolder renames it back
|
|
@@ -511,6 +512,8 @@ export class ProjectCreator {
|
|
|
511
512
|
}
|
|
512
513
|
}
|
|
513
514
|
try {
|
|
515
|
+
// Read before the directory is emptied, so a failed read costs nothing.
|
|
516
|
+
const libraryDependency = { [LIBRARY]: libraryRange() };
|
|
514
517
|
if (directoryPlan === 'overwrite') {
|
|
515
518
|
console.log(chalk.yellow(MESSAGES.EMPTYING_DIRECTORY(targetDir)));
|
|
516
519
|
await emptyDirectory(targetPath);
|
|
@@ -523,7 +526,7 @@ export class ProjectCreator {
|
|
|
523
526
|
console.log(chalk.dim(MESSAGES.PACKAGE_NAME_NORMALIZED(packageName)));
|
|
524
527
|
}
|
|
525
528
|
await this.copyTemplate(template, targetPath);
|
|
526
|
-
await updatePackageJson(targetPath, packageName);
|
|
529
|
+
await updatePackageJson(targetPath, packageName, libraryDependency);
|
|
527
530
|
await this.updateIndexHtmlTitle(targetPath, projectName);
|
|
528
531
|
await this.updateReadme(targetPath, iconLibrary);
|
|
529
532
|
await this.setupBulmaFlavor(targetPath, bulmaFlavor, template);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-bestax",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.19",
|
|
4
4
|
"description": "Create a new bestax-bulma project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -34,26 +34,26 @@
|
|
|
34
34
|
"url": "https://github.com/allxsmith/bestax/issues"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"chalk": "^6.0.
|
|
37
|
+
"chalk": "^6.0.1",
|
|
38
38
|
"commander": "^15.0.0",
|
|
39
39
|
"figures": "^6.1.0",
|
|
40
|
-
"fs-extra": "^11.4.
|
|
40
|
+
"fs-extra": "^11.4.1",
|
|
41
41
|
"prompts": "^2.4.2",
|
|
42
42
|
"react": "^18.0.0 || ^19.0.0",
|
|
43
43
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
44
|
-
"@allxsmith/bestax-bulma": "^5.
|
|
44
|
+
"@allxsmith/bestax-bulma": "^5.44.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@jest/globals": "^30.0.0",
|
|
48
|
-
"@playwright/test": "^1.
|
|
48
|
+
"@playwright/test": "^1.64.1",
|
|
49
49
|
"@types/fs-extra": "^11.0.4",
|
|
50
50
|
"@types/jest": "^30.0.0",
|
|
51
|
-
"@types/node": "^26.6.
|
|
51
|
+
"@types/node": "^26.6.5",
|
|
52
52
|
"@types/prompts": "^2.4.9",
|
|
53
53
|
"@types/wait-on": "^5.3.4",
|
|
54
54
|
"jest": "^30.2.0",
|
|
55
55
|
"semver": "^7.8.5",
|
|
56
|
-
"ts-jest": "^29.4.
|
|
56
|
+
"ts-jest": "^29.4.15",
|
|
57
57
|
"typescript": "^6.0.3",
|
|
58
58
|
"wait-on": "^9.5.1"
|
|
59
59
|
},
|
|
@@ -37,7 +37,7 @@ instead of hand-writing markup.
|
|
|
37
37
|
- Raw `*Base` form exports (`InputBase`, `SelectBase`, `TextAreaBase`, …) are
|
|
38
38
|
escape-hatch variants of the convenience wrappers above them; see the Form docs.
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
104 documented components. Generated from the API docs — every exported
|
|
41
41
|
component is guaranteed to appear (the generator fails if one lacks an API page).
|
|
42
42
|
|
|
43
43
|
## Elements
|
|
@@ -114,6 +114,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
114
114
|
- [Autocomplete](https://bestax.io/docs/api/form/autocomplete) — The `Autocomplete` component provides an input field with dropdown suggestions that filter based on user input.
|
|
115
115
|
- [Checkbox](https://bestax.io/docs/api/form/checkbox) — The `Checkbox` component provides a Bulma-styled checkbox input.
|
|
116
116
|
- [Checkboxes](https://bestax.io/docs/api/form/checkboxes) — The `Checkboxes` component wraps multiple `Checkbox` components in a Bulma-styled group.
|
|
117
|
+
- [Combobox](https://bestax.io/docs/api/form/combobox) — The `Combobox` component is a text field with a list to pick from under it, for a searchable select, a multi-select with tags, grouped items or a server search…
|
|
117
118
|
- [Control](https://bestax.io/docs/api/form/control) — The `Control` component is a Bulma-styled wrapper for form controls (`Input`, `Select`, `TextArea`, etc.), supporting icons (left/right), loading state…
|
|
118
119
|
- [DateInput](https://bestax.io/docs/api/form/datetime/dateinput) — The `DateInput` component is a form input that opens a popover calendar for date selection.
|
|
119
120
|
- [DateRangeInput](https://bestax.io/docs/api/form/datetime/daterangeinput) — The `DateRangeInput` component is a form input for a start and end date, picked from one popover calendar or typed into two segmented inputs.
|
|
@@ -122,6 +123,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
122
123
|
- [File](https://bestax.io/docs/api/form/file) — The `File` component provides a Bulma-styled file input, supporting color, size, boxed/fullwidth/align styles, icons, "has name", and filename display.
|
|
123
124
|
- [Input](https://bestax.io/docs/api/form/input) — The `Input` component provides a Bulma-styled text input, supporting colors, sizes, rounded corners, static state, hover/focus/loading states…
|
|
124
125
|
- [Numberinput](https://bestax.io/docs/api/form/numberinput) — The `Numberinput` component provides a number input with increment/decrement buttons.
|
|
126
|
+
- [PinInput](https://bestax.io/docs/api/form/pininput) — The `PinInput` component is a field for a short code, such as a one-time code or a PIN, drawn as a row of boxes with one character in each.
|
|
125
127
|
- [Radio](https://bestax.io/docs/api/form/radio) — The `Radio` component provides a Bulma-styled radio button input with flexible labels and helper classes.
|
|
126
128
|
- [Radios](https://bestax.io/docs/api/form/radios) — The `Radios` component wraps multiple `Radio` components in a Bulma-styled group.
|
|
127
129
|
- [Rate](https://bestax.io/docs/api/form/rate) — The `Rate` component provides a star/icon-based rating system.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bestax-form
|
|
3
|
-
description: Build forms with @allxsmith/bestax-bulma — Field/Control/Label/Help composition, inputs, selects, checkboxes, radios, switches, and advanced controls (Autocomplete, Slider, Numberinput, Rate, Taginput, File, date/time). There is no form/validation library; this skill shows the components and the validate-it-yourself error pattern. Use when building a form, wiring inputs to state, or showing validation/error state.
|
|
3
|
+
description: Build forms with @allxsmith/bestax-bulma — Field/Control/Label/Help composition, inputs, selects, checkboxes, radios, switches, and advanced controls (Autocomplete, Combobox, Slider, Numberinput, PinInput, Rate, Taginput, File, date/time). There is no form/validation library; this skill shows the components and the validate-it-yourself error pattern. Use when building a form, wiring inputs to state, or showing validation/error state.
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -93,9 +93,11 @@ All import from `@allxsmith/bestax-bulma`. Convenience components auto-wrap Fiel
|
|
|
93
93
|
| `Radio` / `Radios` | Single radio / managed single-select group. |
|
|
94
94
|
| `Switch` | Toggle switch (`isRounded`, `isThin`, `isOutlined`, RTL). |
|
|
95
95
|
| `File` | File upload input with label/message. |
|
|
96
|
-
| `Autocomplete` |
|
|
96
|
+
| `Autocomplete` | Free text with suggestions it filters for you, + keyboard nav. |
|
|
97
|
+
| `Combobox` | A value from a list: `data` for a select, tags or groups, or parts. |
|
|
97
98
|
| `Slider` | Range slider; single/dual thumbs, steps, tooltips, vertical. |
|
|
98
99
|
| `Numberinput` | Numeric input with increment/decrement, min/max, step, stepper. |
|
|
100
|
+
| `PinInput` | A one-time code or PIN as a row of boxes, all in one real input. |
|
|
99
101
|
| `Rate` | Star rating; `max`, `precision` (half/quarter), custom icons, `disabled`. |
|
|
100
102
|
| `Taginput` | Tag/chip input; suggestions, confirm keys, closable tags. |
|
|
101
103
|
| `DateInput` / `TimeInput` / `DateTimeInput` (+ `*Base`) | Date / time / datetime pickers; month or year via `granularity`. |
|
|
@@ -104,6 +106,27 @@ All import from `@allxsmith/bestax-bulma`. Convenience components auto-wrap Fiel
|
|
|
104
106
|
(`NumberInput` and `TagInput` also exist as deprecated aliases of `Numberinput`/`Taginput` —
|
|
105
107
|
same components; prefer the lowercase-second-word spellings.)
|
|
106
108
|
|
|
109
|
+
**Autocomplete or Combobox?** A field whose text is the value, with suggestions to help type it,
|
|
110
|
+
is `Autocomplete`, which filters its `data` itself. A value picked from a list is `Combobox`. Given
|
|
111
|
+
`data`, it filters by label, keeps the value (`value`/`onChange`) and shows the picks as tags with
|
|
112
|
+
`multiple`, so a searchable select, a multi-select or a grouped list is one element. Build it from
|
|
113
|
+
parts for a server search whose answer must show unfiltered, or a "Create" item: then it keeps no
|
|
114
|
+
value and never sets the field's text, so you filter the items, keep the value and set the text in
|
|
115
|
+
`onSelect`.
|
|
116
|
+
|
|
117
|
+
**A verification code or a PIN is `PinInput`**, not a row of one-character `Input`s with focus
|
|
118
|
+
moved by hand. It holds the code in one real input under a row of boxes, so a screen reader hears
|
|
119
|
+
one field named by its `label`, and paste, autofill from a text message (`autoComplete` is
|
|
120
|
+
`"one-time-code"`) and password managers work as they do on any input. Give it `length` (6 by
|
|
121
|
+
default), `value`/`onChange` with the code as a string, and verify in `onComplete`, which fires
|
|
122
|
+
once each time a change fills the last box. It takes digits unless `type="alphanumeric"`, turns a
|
|
123
|
+
digit from any script (Arabic-Indic, Persian) into its ASCII digit, drops anything else typed or
|
|
124
|
+
pasted, and `isMasked` shows dots for a PIN. A paste replaces the whole code. Its default `pattern`
|
|
125
|
+
asks for the full length, so `required` rejects a partial code, not just an empty one. Say the
|
|
126
|
+
length in the `label` ("Enter the 6-digit code"): the boxes are hidden from a screen reader, and the
|
|
127
|
+
`message` isn't tied to the input, so it isn't read on focus. Separate help text needs
|
|
128
|
+
`aria-describedby` on the PinInput.
|
|
129
|
+
|
|
107
130
|
## Common props
|
|
108
131
|
|
|
109
132
|
Across the convenience inputs (`Input`, `Select`, `TextArea`, and similar):
|
|
@@ -130,13 +153,15 @@ spellings compile only where they historically existed: `isFullWidth` everywhere
|
|
|
130
153
|
`Sidebar`, `fullwidth` on `Tabs` only, `fullWidth` on `Sidebar` only.
|
|
131
154
|
|
|
132
155
|
**Labels.** Default to the convenience `label` prop, with `horizontal` for a side-by-side row.
|
|
133
|
-
`Input`, `Select`, `TextArea`, `File`, `Numberinput`, `
|
|
134
|
-
`DateTimeInput`, `Autocomplete` and `Taginput` then render the `Field` and the label
|
|
135
|
-
`htmlFor`/`id`, to your `id` when you pass one
|
|
136
|
-
an explicit `labelProps={{ htmlFor }}` wins.
|
|
137
|
-
`
|
|
138
|
-
|
|
139
|
-
|
|
156
|
+
`Input`, `Select`, `TextArea`, `File`, `Numberinput`, `PinInput`, `Slider`, `DateInput`,
|
|
157
|
+
`TimeInput`, `DateTimeInput`, `Autocomplete` and `Taginput` then render the `Field` and the label
|
|
158
|
+
and wire `htmlFor`/`id`, to your `id` when you pass one
|
|
159
|
+
(`<Input label="Email" id="email" horizontal />`); an explicit `labelProps={{ htmlFor }}` wins. `Combobox` renders its `Field` and label too, and wires
|
|
160
|
+
them to its field. Given `data`, its `id`, `aria-label` and `aria-labelledby` go to the field;
|
|
161
|
+
built from parts, put them on `Combobox.Input`, since on the `Combobox` they land on its wrapper
|
|
162
|
+
`div`. The group inputs (`Checkboxes`, `Radios`, `Rate`, `DateRangeInput`) name their
|
|
163
|
+
`role="group"`/`"radiogroup"` wrapper through `aria-labelledby` instead. Inside a `Field` a
|
|
164
|
+
convenience input drops its `label`, and an `inline` date/time picker has no visible input to wire.
|
|
140
165
|
|
|
141
166
|
Composed, a labeled `Field` names the one control it holds: a composed `InputBase`/`SelectBase`/
|
|
142
167
|
`TextAreaBase`, a composed `DateInputBase`/`TimeInputBase`/`DateTimeInputBase` that is not
|
|
@@ -152,9 +177,9 @@ unmatched `for` out of server-rendered HTML.
|
|
|
152
177
|
|
|
153
178
|
A `range` `Slider` points the `for` at its low thumb and starts both thumbs' names with the label
|
|
154
179
|
("Price range Minimum value"); its `ariaLabel={[low, high]}` replaces those names outright, so
|
|
155
|
-
leave it off when a label already names the Slider. Those thumbs and an `Autocomplete`'s
|
|
156
|
-
`Taginput`'s
|
|
157
|
-
hand has only when you pass one, `labelProps={{ htmlFor, id }}`; without it they keep their
|
|
180
|
+
leave it off when a label already names the Slider. Those thumbs and an `Autocomplete`'s, a
|
|
181
|
+
`Combobox`'s or a `Taginput`'s list point `aria-labelledby` at the label's `id`, which a label
|
|
182
|
+
wired by hand has only when you pass one, `labelProps={{ htmlFor, id }}`; without it they keep their
|
|
158
183
|
fallback names ("Minimum value"/"Maximum value", "Suggestions"). A `<label htmlFor>` you put in
|
|
159
184
|
`Field.Label` yourself (`references/patterns.md`) names its control through the `for` alone, so
|
|
160
185
|
label a row holding either with the `Field`'s `label` prop. An `aria-label` or `aria-labelledby`
|
|
@@ -243,9 +268,9 @@ multi-field form plus the advanced inputs.
|
|
|
243
268
|
|
|
244
269
|
## Reuse the shipped components
|
|
245
270
|
|
|
246
|
-
bestax ships the whole form surface — Input, Select, TextArea, Checkbox(es), Radio(s), Switch,
|
|
247
|
-
|
|
248
|
-
inventory above). **Compose these; don't hand-roll raw `<input class="input">` markup or
|
|
271
|
+
bestax ships the whole form surface — Input, Select, TextArea, Checkbox(es), Radio(s), Switch, File,
|
|
272
|
+
Autocomplete, Combobox, Slider, Numberinput, PinInput, Rate, Taginput, and the date/time inputs (see
|
|
273
|
+
the inventory above). **Compose these; don't hand-roll raw `<input class="input">` markup or
|
|
249
274
|
reinvent a control.** If you think a control is missing, check `bulma-ui/src/index.ts` and
|
|
250
275
|
`docs/docs/api/form/` first — it's probably already there under a different name.
|
|
251
276
|
|
|
@@ -19,7 +19,7 @@ Container and layout. Compound parts: `Field.Label`, `Field.Body`.
|
|
|
19
19
|
| `labelProps` | label attributes | Props for the `<label>`. An explicit `htmlFor` (even `undefined`) takes over the association; otherwise it is set automatically. For its `id`, see below. |
|
|
20
20
|
| `textColor` / `bgColor` | Bulma color | Helper colors for the field. |
|
|
21
21
|
|
|
22
|
-
An `id` in `labelProps` is what a group's, a range `Slider`'s and an `Autocomplete` or `Taginput`
|
|
22
|
+
An `id` in `labelProps` is what a group's, a range `Slider`'s and an `Autocomplete`'s, a `Combobox`'s or a `Taginput`'s list's `aria-labelledby` points at. A label wired by hand with `htmlFor` gets no generated id, so give it one, `labelProps={{ htmlFor, id }}`, for a range `Slider`, an `Autocomplete`, a `Combobox` or a `Taginput`. No group points at a label wired by hand.
|
|
23
23
|
|
|
24
24
|
## Control — `form/Control.tsx`
|
|
25
25
|
|
|
@@ -87,13 +87,15 @@ File input with `label`, `message`, color/size, and icon support.
|
|
|
87
87
|
|
|
88
88
|
## Advanced inputs
|
|
89
89
|
|
|
90
|
-
| Component | Key props
|
|
91
|
-
| -------------- |
|
|
92
|
-
| `Autocomplete` | `data` (`string[]` or item objects), `value`, `onInput(value)` for typing, `onSelect(item)` for picks, `clearable` (the clear button hides while `loading`, or a loading `Control` around it, puts a spinner at the same edge), `openOnFocus`, `loading`.
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
90
|
+
| Component | Key props |
|
|
91
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
92
|
+
| `Autocomplete` | `data` (`string[]` or item objects), `value`, `onInput(value)` for typing, `onSelect(item)` for picks, `clearable` (the clear button hides while `loading`, or a loading `Control` around it, puts a spinner at the same edge), `openOnFocus`, `loading`. |
|
|
93
|
+
| `Combobox` | Either way: `multiple`, `keepFirst`, `inline`, `size`, `position` (`'auto'` keeps the list in the viewport), `appendToBody` (in a `Modal`, point a `ConfigProvider`'s `portalContainer` inside the modal), `open`/`defaultOpen`/`onOpenChange`, `onSelect(value)` and the Field props. Given `data` (strings or `{ value, label?, disabled?, group? }`): `value`/`defaultValue`/`onChange` (`string \| null`, or `string[]` with `multiple`, shown as tags), `placeholder`, `name`, `empty`, `itemTemplate`, and `disabled`, `required`, `form`, `id`, `aria-label`/`aria-labelledby` for the field; leaving the field empty clears one value. From parts: `.Input` (an `Input`, with `size` and the Field props on the root), `.Menu`, `.List`, `.Item` (`value`, `checked`, `disabled`), `.Group` (`label`), `.Empty`; you filter, keep the value and set the field's text in `onSelect`. |
|
|
94
|
+
| `Slider` | `min`, `max`, `step`, single or dual thumb (`value` number or `[number, number]`), `tooltip` (`'auto' \| 'always' \| 'hidden'`), vertical orientation, `color`, `size`. |
|
|
95
|
+
| `Numberinput` | `value`/`onChange`, `min`, `max`, `step`, increment/decrement buttons, stepper variant, `color`, `size`, `isLoading` (set it here, not on a `Control` around it, whose spinner covers the right-hand button). |
|
|
96
|
+
| `PinInput` | `length` (6), `value`/`defaultValue`/`onChange` (the code as a string), `onComplete(code)` once a change fills the last box, `type` (`'number'` or `'alphanumeric'`), `isMasked`, `name`, `color`, `size`, `isRounded`, `disabled` and the Field props. One input holds the code (`autoComplete="one-time-code"`); a paste replaces it. |
|
|
97
|
+
| `Rate` | `value`/`onChange`, `max`, `precision` (1 / 0.5 / 0.25), `customIcon` or `iconName`, `showScore`/`showText`/`texts`, `disabled`. |
|
|
98
|
+
| `Taginput` | `value` (array)/`onChange`, autocomplete suggestions, configurable confirm keys, closable tags, `color`, `size`. |
|
|
97
99
|
|
|
98
100
|
## Date / time inputs
|
|
99
101
|
|
|
@@ -214,6 +214,60 @@ const [city, setCity] = useState('');
|
|
|
214
214
|
openOnFocus
|
|
215
215
|
/>;
|
|
216
216
|
|
|
217
|
+
// Combobox: a value from a list. Given data, it filters by label, keeps the
|
|
218
|
+
// value and shows its label in the field.
|
|
219
|
+
const [country, setCountry] = useState<string | null>(null);
|
|
220
|
+
<Combobox
|
|
221
|
+
label="Country"
|
|
222
|
+
data={['Canada', 'France', 'Japan']}
|
|
223
|
+
value={country}
|
|
224
|
+
onChange={setCountry}
|
|
225
|
+
empty="No countries match"
|
|
226
|
+
/>;
|
|
227
|
+
|
|
228
|
+
// Several values, shown as tags under the field; groups come from `group`.
|
|
229
|
+
const [labels, setLabels] = useState<string[]>([]);
|
|
230
|
+
<Combobox
|
|
231
|
+
label="Labels"
|
|
232
|
+
multiple
|
|
233
|
+
data={[
|
|
234
|
+
{ value: 'bug', label: 'Bug', group: 'Type' },
|
|
235
|
+
{ value: 'docs', label: 'Docs', group: 'Area' },
|
|
236
|
+
]}
|
|
237
|
+
value={labels}
|
|
238
|
+
onChange={setLabels}
|
|
239
|
+
/>;
|
|
240
|
+
|
|
241
|
+
// From parts, when data doesn't fit (a server search, a "Create" item): you
|
|
242
|
+
// filter, keep the value and set the text, and a pick calls onSelect.
|
|
243
|
+
const countries = ['Canada', 'France', 'Japan'];
|
|
244
|
+
const [choice, setChoice] = useState<string | null>(null);
|
|
245
|
+
const [query, setQuery] = useState('');
|
|
246
|
+
const term = query === choice ? '' : query.trim().toLowerCase();
|
|
247
|
+
const matches = countries.filter(c => c.toLowerCase().includes(term));
|
|
248
|
+
<Combobox
|
|
249
|
+
label="Country"
|
|
250
|
+
onSelect={picked => {
|
|
251
|
+
setChoice(picked);
|
|
252
|
+
setQuery(picked);
|
|
253
|
+
}}
|
|
254
|
+
>
|
|
255
|
+
<Combobox.Input value={query} onChange={e => setQuery(e.target.value)} />
|
|
256
|
+
<Combobox.Menu>
|
|
257
|
+
{matches.length > 0 ? (
|
|
258
|
+
<Combobox.List>
|
|
259
|
+
{matches.map(c => (
|
|
260
|
+
<Combobox.Item key={c} value={c} checked={c === choice}>
|
|
261
|
+
{c}
|
|
262
|
+
</Combobox.Item>
|
|
263
|
+
))}
|
|
264
|
+
</Combobox.List>
|
|
265
|
+
) : (
|
|
266
|
+
<Combobox.Empty>No countries match</Combobox.Empty>
|
|
267
|
+
)}
|
|
268
|
+
</Combobox.Menu>
|
|
269
|
+
</Combobox>;
|
|
270
|
+
|
|
217
271
|
// Slider (single and dual thumb)
|
|
218
272
|
const [volume, setVolume] = useState(50);
|
|
219
273
|
<Slider value={volume} onChange={setVolume} min={0} max={100} tooltip="auto" />;
|
|
@@ -233,6 +287,20 @@ const [range, setRange] = useState<[number, number]>([20, 80]);
|
|
|
233
287
|
const [qty, setQty] = useState(1);
|
|
234
288
|
<Numberinput value={qty} onChange={setQty} min={1} max={10} step={1} />;
|
|
235
289
|
|
|
290
|
+
// PinInput: a one-time code or PIN, a box per character, all in one input.
|
|
291
|
+
// onComplete fires once each time a change fills the last box.
|
|
292
|
+
const [code, setCode] = useState('');
|
|
293
|
+
const verify = (otp: string) => {
|
|
294
|
+
// Send it to your server, then show the result with color and message.
|
|
295
|
+
};
|
|
296
|
+
<PinInput
|
|
297
|
+
label="Enter the 6-digit code"
|
|
298
|
+
name="otp"
|
|
299
|
+
value={code}
|
|
300
|
+
onChange={setCode}
|
|
301
|
+
onComplete={verify}
|
|
302
|
+
/>;
|
|
303
|
+
|
|
236
304
|
// Rate
|
|
237
305
|
const [stars, setStars] = useState(3);
|
|
238
306
|
<Rate value={stars} onChange={setStars} max={5} />;
|
|
@@ -110,8 +110,18 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
|
|
|
110
110
|
other: `<TreeView aria-label="Files" selectionMode="single">` with `selected`/`onSelectedChange`
|
|
111
111
|
picks one, and `selectionMode="checkbox"` with `checked`/`onCheckedChange` draws boxes with
|
|
112
112
|
mixed parents, where a parent's value checks its whole branch. Map the data with a recursive
|
|
113
|
-
function, and render an item's children whether it's open or not.
|
|
114
|
-
|
|
113
|
+
function, and render an item's children whether it's open or not. A folder that loads when it
|
|
114
|
+
opens takes `hasChildren`, `isLoading` while its children are on their way, and
|
|
115
|
+
`loadError="Couldn't load src"` with `onRetry` when the load fails, setting `isLoading` again
|
|
116
|
+
while the retry runs: the tree draws the Loading… and Retry rows and announces them, so don't
|
|
117
|
+
hand-write a placeholder item. Its items aren't
|
|
118
|
+
links, so the app's sidebar and any list of pages stay on `Menu` with `Menu.Item href`.
|
|
119
|
+
- **A sidebar whose groups of links open and close is `Menu.Section`**, one per group, directly
|
|
120
|
+
in `Menu` (never inside a `Menu.Item` or `Menu.List`):
|
|
121
|
+
`<Menu.Section label="Components" defaultOpen>` around that group's `Menu.List`, with
|
|
122
|
+
`defaultOpen` on the group holding the current page (or `open`/`onOpenChange` to hold the state).
|
|
123
|
+
Its heading is a real button with `aria-expanded`, and its links stay links, each a tab stop.
|
|
124
|
+
Not `TreeView`, and not a `Collapse` inside a hand-written `<li>`.
|
|
115
125
|
- Link lists (footer nav, sidebars): a bare `UnorderedList` of `ListItem`s is already
|
|
116
126
|
marker-less and flush — Bulma's reset unstyles `ul` — so no prop or CSS is needed;
|
|
117
127
|
bullets appear only inside `Content`.
|
|
@@ -167,9 +177,10 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
|
|
|
167
177
|
(`Hero color="primary"`, a dark banner), use **filled** buttons — `color="light"` or
|
|
168
178
|
`color="primary" isInverted` — never a thin `isOutlined` secondary: a light outline + light
|
|
169
179
|
label on a dark surface is low-contrast and gets worse under OS dark mode. And when the page's
|
|
170
|
-
design is single-mode (a fixed light or dark look), pin it
|
|
171
|
-
`<
|
|
172
|
-
colors out from under the fixed palette
|
|
180
|
+
design is single-mode (a fixed light or dark look), pin it on the `<html>` in `index.html`,
|
|
181
|
+
`<html lang="en" data-theme="light">`, so a visitor's OS dark mode can't flip Bulma's text
|
|
182
|
+
colors out from under the fixed palette, from the first paint on (details: the
|
|
183
|
+
`bestax-theming` skill's contrast rules).
|
|
173
184
|
|
|
174
185
|
## Three components core Bulma will talk you out of
|
|
175
186
|
|
|
@@ -164,8 +164,8 @@ style, so bands cost zero custom CSS. The same values work on `Hero` (and its
|
|
|
164
164
|
**Hero CTAs:** on a colored hero use **filled** buttons only — `color="light"` for the primary
|
|
165
165
|
CTA and `color="primary" isInverted` (solid white, primary text) for a secondary. A thin
|
|
166
166
|
`isOutlined` button on a fixed-color surface is low-contrast and degrades further under OS dark
|
|
167
|
-
mode. Single-mode page designs should also pin the scheme
|
|
168
|
-
(`<
|
|
167
|
+
mode. Single-mode page designs should also pin the scheme on the `<html>` in `index.html`
|
|
168
|
+
(`<html lang="en" data-theme="light">`); see the `bestax-theming` skill's contrast rules.
|
|
169
169
|
|
|
170
170
|
---
|
|
171
171
|
|