From 82f3fa6e224bfe9cd0cb5a95b405039ad7c22925 Mon Sep 17 00:00:00 2001 From: iberdinsky-skilld Date: Tue, 14 Oct 2025 07:17:49 +0200 Subject: [PATCH 01/12] Use case + Readme update --- README.md | 15 +++++++----- .../addon-features-examples.component.yml | 5 ++++ src/storiesGenerator.ts | 24 +++++++++++-------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 58f3399..e1cc546 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ This addon streamlines the integration of Drupal Single Directory Components (SD - [Configuration Options](#configuration-options) - [Setting Default Values](#setting-default-values) - [Story Configuration via thirdPartySettings.sdcStorybook](#story-configuration-via-thirdpartysettingssdcstorybook) +- [UI Patterns](#ui-patterns) - [Why Choose SDC Storybook Over Alternatives?](#why-choose-sdc-storybook-over-alternatives) - [Dependencies](#dependencies) - [Known Issues](#known-issues) -- [UI Patterns](#ui-patterns) --- @@ -637,6 +637,14 @@ thirdPartySettings: This configuration creates an additional story named **Preview**, rendered using the provided `args` and `parameters`. +## UI Patterns + +Addon supports UI Patterns `library_wrapper` [for wrapping stories](https://project.pages.drupalcode.org/ui_patterns/2-authors/1-stories-and-library/#library-wrappers) in a specific HTML structure. + +--- + +- Custom Twig filters and functions are not supported ([UI Patterns TwigExtension](https://git.drupalcode.org/project/ui_patterns/-/blob/8.x-1.x/src/Template/TwigExtension.php)). + ## Why Choose SDC Storybook Over Alternatives? While solutions like [SDC Styleguide](https://www.drupal.org/project/sdc_styleguide) and [Drupal Storybook](https://www.drupal.org/project/storybook) are valuable, the SDC Storybook addon offers distinct advantages: @@ -690,8 +698,3 @@ While using Drupal to render components offers tighter integration, there are st ## Known Issues - The addon relies on [Experimental indexers](https://storybook.js.org/docs/api/main-config/main-config-indexers). - -## UI Patterns - -- Variants are partially supported ([Issue 3390712](https://www.drupal.org/project/drupal/issues/3390712)). -- Custom Twig filters and functions are not supported ([UI Patterns TwigExtension](https://git.drupalcode.org/project/ui_patterns/-/blob/8.x-1.x/src/Template/TwigExtension.php)). diff --git a/components/addon-features-examples/addon-features-examples.component.yml b/components/addon-features-examples/addon-features-examples.component.yml index e7c4317..9c9e5e4 100644 --- a/components/addon-features-examples/addon-features-examples.component.yml +++ b/components/addon-features-examples/addon-features-examples.component.yml @@ -26,6 +26,11 @@ thirdPartySettings: preview: name: Preview Example description: 'Addon Features preview description' + library_wrapper: ' +
+ UI PATTERNS library_wrapper + {{ _story }} +
' slots: content: - type: component diff --git a/src/storiesGenerator.ts b/src/storiesGenerator.ts index 6a3357f..1f2e456 100644 --- a/src/storiesGenerator.ts +++ b/src/storiesGenerator.ts @@ -25,16 +25,20 @@ export const ${capitalize(storyKey)} = { ${generateArgs(slots, true)} ${generateVariants(variants)} }, - ${library_wrapper ? `decorators: [ - (Story) => { - const wrapper = ${JSON.stringify(library_wrapper)}; - if (!wrapper) return Story(); - - // Replace {{ _story }} with the actual story component - const wrappedHtml = wrapper.replace('{{ _story }}', Story()); - return wrappedHtml; - } - ],` : ''} + ${ + library_wrapper + ? `decorators: [ + (Story) => { + const wrapper = ${JSON.stringify(library_wrapper)}; + if (!wrapper) return Story(); + + // Replace {{ _story }} with the actual story component + const wrappedHtml = wrapper.replace('{{ _story }}', Story()); + return wrappedHtml; + } + ],` + : '' + } play: async ({ canvasElement }) => { Drupal.attachBehaviors(canvasElement, window.drupalSettings); }, From 3166b6a4f3f55e376ff3e613f5ded601640ddfa6 Mon Sep 17 00:00:00 2001 From: iberdinsky-skilld Date: Tue, 14 Oct 2025 07:20:29 +0200 Subject: [PATCH 02/12] Bump --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 96c3241..e323a98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "storybook-addon-sdc", - "version": "0.14.2", + "version": "0.15.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "storybook-addon-sdc", - "version": "0.14.2", + "version": "0.15.0", "license": "MIT", "dependencies": { "glob": "^11.0.1", diff --git a/package.json b/package.json index 2802b35..921f9f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "storybook-addon-sdc", - "version": "0.14.2", + "version": "0.15.0", "description": "Drupal Single Directory Components as stories", "keywords": [ "SDC", From 391c4a39dd94524848d4bb7820c0f708e541d42d Mon Sep 17 00:00:00 2001 From: iberdinsky-skilld Date: Tue, 14 Oct 2025 07:20:45 +0200 Subject: [PATCH 03/12] Update CHANGELOG.md [skip ci] --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9562215..d33cd97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# v0.14.3 (Tue Oct 14 2025) + +#### 🐛 Bug Fix + +- Add library_wrapper ui_pattern story feature [#88](https://github.com/iberdinsky-skilld/sdc-addon/pull/88) ([@Fred-Boulanger](https://github.com/Fred-Boulanger) [@iberdinsky-skilld](https://github.com/iberdinsky-skilld)) + +#### âš ī¸ Pushed to `main` + +- Bump (iberdinsky@skilld.cloud) + +#### Authors: 3 + +- [@iberdinsky-skilld](https://github.com/iberdinsky-skilld) +- fredb ([@Fred-Boulanger](https://github.com/Fred-Boulanger)) +- iberdinsky-skilld (iberdinsky@skilld.cloud) + +--- + # v0.14.2 (Fri Oct 10 2025) #### âš ī¸ Pushed to `main` From 2a125bb8ad1c076936d72d8dd4a0c2095349d539 Mon Sep 17 00:00:00 2001 From: iberdinsky-skilld Date: Tue, 14 Oct 2025 07:20:46 +0200 Subject: [PATCH 04/12] Bump version to: 0.15.1 [skip ci] --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e323a98..fbbd188 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "storybook-addon-sdc", - "version": "0.15.0", + "version": "0.15.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "storybook-addon-sdc", - "version": "0.15.0", + "version": "0.15.1", "license": "MIT", "dependencies": { "glob": "^11.0.1", diff --git a/package.json b/package.json index 921f9f6..f1ef4c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "storybook-addon-sdc", - "version": "0.15.0", + "version": "0.15.1", "description": "Drupal Single Directory Components as stories", "keywords": [ "SDC", From 928cc5716851d63752cea4efa16203e46551c164 Mon Sep 17 00:00:00 2001 From: GitFred Date: Tue, 14 Oct 2025 11:24:45 +0200 Subject: [PATCH 05/12] Add utility classes generator feature based on ui_styles files --- UTILITY_CLASSES_GENERATOR.md | 201 ++++++ package.json | 3 +- .../parent-namespace.ui_styles.yml | 63 ++ scripts/generate-utility-stories.mjs | 355 ++++++++++ sdc-addon.ui_styles.yml | 114 ++++ src/preset.ts | 3 + src/utilityClassesGenerator.ts | 331 ++++++++++ src/vite-plugin-utility-classes.ts | 405 ++++++++++++ stories/utility-classes.css | 130 ++++ ...tility-classes.parent-namespace.stories.js | 425 ++++++++++++ stories/utility-classes.sdc-addon.stories.js | 613 ++++++++++++++++++ 11 files changed, 2642 insertions(+), 1 deletion(-) create mode 100644 UTILITY_CLASSES_GENERATOR.md create mode 100644 parent-namespace/parent-namespace.ui_styles.yml create mode 100644 scripts/generate-utility-stories.mjs create mode 100644 sdc-addon.ui_styles.yml create mode 100644 src/utilityClassesGenerator.ts create mode 100644 src/vite-plugin-utility-classes.ts create mode 100644 stories/utility-classes.css create mode 100644 stories/utility-classes.parent-namespace.stories.js create mode 100644 stories/utility-classes.sdc-addon.stories.js diff --git a/UTILITY_CLASSES_GENERATOR.md b/UTILITY_CLASSES_GENERATOR.md new file mode 100644 index 0000000..0df7d91 --- /dev/null +++ b/UTILITY_CLASSES_GENERATOR.md @@ -0,0 +1,201 @@ +# Utility Classes Storybook Generator + +This module automatically generates Storybook documentation for utility classes defined in any `*.ui_styles.yml` file. + +## Overview + +The utility classes generator creates interactive Storybook stories that showcase all utility classes defined in your YAML configuration. Each utility class group gets its own story with live previews of all available options. + +## Features + +- **Automatic Discovery**: Finds all `*.ui_styles.yml` files in your project automatically +- **Multi-file Support**: Merges utility classes from multiple YAML files +- **Automatic Story Generation**: Parses YAML configuration and generates complete Storybook stories +- **Interactive Previews**: Each utility class option is displayed with live previews +- **Organized Categories**: Stories are organized by category and label from the YAML +- **Responsive Design**: Demo layouts adapt to different screen sizes +- **Documentation Links**: Includes related links when specified in the YAML +- **Disabled Class Support**: Automatically skips utility classes marked as `enabled: false` + +## Usage + +### Automatic Generation + +The utility classes stories are **automatically generated** during Storybook build and development. No manual intervention is required! + +When you run: +- `npm run storybook` (development server) +- `npm run build-storybook` (production build) + +The system will: +1. Automatically find all `*.ui_styles.yml` files in your project +2. Parse and merge all utility class configurations +3. Generate `stories/utility-classes.stories.js` with all utility class stories +4. Generate `stories/utility-classes.css` with demo styling +5. Automatically include the stories in your Storybook + +### Manual Generation (Optional) + +If you need to manually regenerate the stories: + +```bash +npm run generate:utility-stories +``` + +### YAML Structure + +The generator expects a YAML file with the following structure: + +```yaml +utility_group_name: + category: "Category Name" + label: "Display Label" + description: "Optional description" + links: + - "https://example.com/docs" + options: + class-name: "Display Name" + another-class: "Another Display Name" + previewed_with: + - "additional-class-1" + - "additional-class-2" + enabled: true # Optional, defaults to true +``` + +### Example YAML Entry + +```yaml +font_size_dt: + category: "Typography DT" + label: "Font size" + description: "We use specifics size classes (with custom tokens) for a responsive typography display" + options: + title-2xl: Title 2XL + title-xl: Title XL + title-lg: Title LG + previewed_with: + - mb-4 +``` + +## Generated Stories Structure + +Each utility class group becomes a Storybook story with: + +- **Title**: `Utility Classes/{category}/{label}` +- **Description**: From the YAML `description` field +- **Interactive Previews**: Each option with live demo +- **Related Links**: External documentation links +- **Responsive Layout**: Grid layout that adapts to screen size + +## Demo Content + +The generator automatically creates appropriate demo content based on the utility type: + +- **Typography**: Sample text for font size, family, etc. +- **Spacing**: Content with spacing demonstrations +- **Borders**: Border width and style examples +- **Rounded**: Rounded corner demonstrations +- **Line Clamp**: Long text for truncation examples +- **Cover Links**: Interactive link examples + +## Customization + +### Adding New Utility Types + +To add support for new utility class types, modify the `generateDemoContent` function in `scripts/generate-utility-stories.mjs`: + +```javascript +const generateDemoContent = (groupKey, className) => { + if (groupKey.includes('your_new_type')) { + return 'Your custom demo content' + } + // ... existing conditions +} +``` + +### Styling + +The generated CSS in `stories/utility-classes.css` can be customized to match your design system. Key classes: + +- `.utility-classes-demo` - Main container +- `.utility-classes-grid` - Grid layout for options +- `.utility-preview-item` - Individual option container +- `.utility-preview-demo` - Demo area for each option + +## Integration with Storybook + +The generated stories integrate seamlessly with your existing Storybook setup: + +1. Stories follow the same structure as your component stories +2. Include Drupal behavior attachment for compatibility +3. Use the same documentation format +4. Support Storybook's docs addon + +## File Structure + +``` +├── scripts/ +│ └── generate-utility-stories.mjs # Main generator script +├── src/ +│ └── utilityClassesGenerator.ts # TypeScript generator module +├── stories/ +│ ├── utility-classes.stories.js # Generated stories +│ └── utility-classes.css # Generated demo styles +├── unami.ui_styles.yml # Source YAML configuration +├── theme.ui_styles.yml # Additional YAML configuration (optional) +└── any-name.ui_styles.yml # Any file with *.ui_styles.yml extension +``` + +## Development + +### TypeScript Module + +The `src/utilityClassesGenerator.ts` module provides a TypeScript interface for programmatic generation: + +```typescript +import { generateUtilityClassesFromYaml } from './utilityClassesGenerator' + +generateUtilityClassesFromYaml( + 'path/to/yaml/file.yml', + 'path/to/output/directory' +) +``` + +### Script Options + +The generator script supports: + +- **YAML File Path**: Configurable source file location +- **Output Directory**: Configurable output location +- **Error Handling**: Graceful error reporting +- **Progress Logging**: Detailed generation progress + +## Troubleshooting + +### Common Issues + +1. **YAML Parse Errors**: Ensure your YAML syntax is valid +2. **Missing Dependencies**: Run `npm install` to ensure all dependencies are available +3. **File Permissions**: Ensure write permissions for the output directory + +### Debug Mode + +For debugging, you can modify the script to add console logging: + +```javascript +console.log('Parsed config:', config) +console.log('Generated stories:', stories) +``` + +## Contributing + +When adding new features to the utility classes generator: + +1. Update the TypeScript interfaces in `utilityClassesGenerator.ts` +2. Modify the generation logic in `generate-utility-stories.mjs` +3. Update this documentation +4. Test with various YAML configurations + +## License + +This utility classes generator is part of the storybook-addon-sdc project and follows the same MIT license. diff --git a/package.json b/package.json index 2802b35..d4739d8 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "serve": "npx http-server ./storybook-static", - "format": "prettier --write ." + "format": "prettier --write .", + "generate:utility-stories": "node scripts/generate-utility-stories.mjs" }, "dependencies": { "glob": "^11.0.1", diff --git a/parent-namespace/parent-namespace.ui_styles.yml b/parent-namespace/parent-namespace.ui_styles.yml new file mode 100644 index 0000000..d444101 --- /dev/null +++ b/parent-namespace/parent-namespace.ui_styles.yml @@ -0,0 +1,63 @@ +################################################################################ +# UI Suite additional classes. +################################################################################ +font_size: + category: "Typography" + label: "Font size" + description: "We use specifics size classes (with custom tokens) for a responsive typography display" + options: + title-2xl: Title 2XL + title-xl: Title XL + title-lg: Title LG + title-md: Title MD + title-sm: Title SM + title-xs: Title XS + body-xl: Body XL + body-lg: Body LG + body-md: Body MD + body-sm: Body SM + body-xs: Body XS + label-xl: Label XL + label-lg: Label LG + label-md: Label MD + label-sm: Label SM + label-xs: Label XS + previewed_with: + - mb-4 + +line_clamp: + category: "Utility" + label: "Line Clamp" + description: "Utilities for clamping text to a specific number of lines." + links: + - "https://tailwindcss.com/docs/line-clamp" + options: + line-clamp-1: line-clamp-1 + line-clamp-2: line-clamp-2 + line-clamp-3: line-clamp-3 + line-clamp-4: line-clamp-4 + line-clamp-5: line-clamp-5 + previewed_with: + - mb-4 + +typography: + category: "Utility" + label: "Typography" + options: + prose: Formatted Text (prose) + previewed_with: + - mb-4 + +font_family: + category: "Typography" + label: "Font family" + description: "You can control the typeface of text using the font family utilities." + links: + - "https://tailwindcss.com/docs/font-family#setting-the-font-family" + options: + font-mono: "Mono" + font-sans: "Sans" + +thirdPartySettings: + sdcStorybook: + disableUtilityClasses: false \ No newline at end of file diff --git a/scripts/generate-utility-stories.mjs b/scripts/generate-utility-stories.mjs new file mode 100644 index 0000000..c726212 --- /dev/null +++ b/scripts/generate-utility-stories.mjs @@ -0,0 +1,355 @@ +#!/usr/bin/env node + +import { readFileSync, writeFileSync, existsSync } from 'fs' +import { join, dirname } from 'path' +import { fileURLToPath } from 'url' +import { globSync } from 'glob' +import yaml from 'yaml' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +// Helper function to capitalize strings +const capitalize = (str) => str[0].toUpperCase() + str.slice(1) + +// Helper function to convert snake_case to Title Case +const toTitleCase = (str) => str.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) + +// Find all *.ui_styles.yml files +const findUtilityStyleFiles = (rootDir) => { + try { + const pattern = join(rootDir, '**', '*.ui_styles.yml') + const files = globSync(pattern) + return files + } catch (error) { + console.warn(`Error searching for *.ui_styles.yml files: ${error}`) + return [] + } +} + +// Merge multiple YAML configurations +const mergeConfigurations = (configs) => { + const merged = {} + + configs.forEach((config, index) => { + Object.entries(config).forEach(([key, value]) => { + // If key already exists, append index to make it unique + const uniqueKey = merged[key] ? `${key}_${index}` : key + merged[uniqueKey] = value + }) + }) + + return merged +} + +// Paths +const rootDir = join(__dirname, '..') +const yamlFiles = findUtilityStyleFiles(rootDir) +const outputDir = join(__dirname, '../stories') + +console.log('🚀 Generating utility classes stories...') +console.log(`📁 Found ${yamlFiles.length} utility style file(s): ${yamlFiles.join(', ')}`) +console.log(`📁 Output directory: ${outputDir}`) + +if (yamlFiles.length === 0) { + console.warn('❌ No *.ui_styles.yml files found in project') + process.exit(1) +} + +try { + // Read and parse all YAML files + const configs = [] + const sourceFiles = [] + + yamlFiles.forEach(yamlFilePath => { + if (existsSync(yamlFilePath)) { + const fileContent = readFileSync(yamlFilePath, 'utf8') + const config = yaml.parse(fileContent) + configs.push(config) + sourceFiles.push(yamlFilePath) + console.log(`📄 Parsed utility classes from: ${yamlFilePath}`) + } + }) + + if (configs.length === 0) { + console.warn('❌ No valid utility style configurations found') + process.exit(1) + } + + // Merge all configurations + const config = mergeConfigurations(configs) + + // Generate demo content based on utility type + const generateDemoContent = (groupKey, className) => { + if (groupKey.includes('font_size') || groupKey.includes('typography')) { + return 'Sample text to demonstrate typography' + } + if (groupKey.includes('margin') || groupKey.includes('padding')) { + return 'Content with spacing' + } + if (groupKey.includes('border')) { + return 'Border demo' + } + if (groupKey.includes('rounded')) { + return 'Rounded corners' + } + if (groupKey.includes('line_clamp')) { + return 'This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines.' + } + if (groupKey.includes('coverlink')) { + return `Cover link demo` + } + return 'Demo content' + } + + // Generate preview HTML for each option + const generatePreviewHtml = (groupKey, className, label, previewedWith = []) => { + const previewClasses = previewedWith.join(' ') + const combinedClasses = `${className} ${previewClasses}`.trim() + const demoContent = generateDemoContent(groupKey, className) + + return ` +
+
+ ${className} + ${label} +
+
+
+ ${demoContent} +
+
+
` + } + + // Generate story for a utility class group + const generateStory = (groupKey, definition) => { + const { category, label, description, options, previewed_with = [], links = [] } = definition + + // Skip disabled utility classes + if (definition.enabled === false) { + return '' + } + + const storyKey = groupKey + + // Generate all option previews + const optionPreviews = Object.entries(options) + .map(([className, optionLabel]) => generatePreviewHtml(groupKey, className, optionLabel, previewed_with)) + .join('\n') + + // Generate links section if available + const linksSection = links.length > 0 + ? ` + ` + : '' + + return ` +export const ${storyKey} = { + title: 'Utility Classes/${category}/${label}', + parameters: { + docs: { + description: { + story: \`${description || `Utility classes for ${label.toLowerCase()}`}\` + } + } + }, + render: () => { + return \` +
+
+

${label}

+ ${description ? `

${description}

` : ''} + ${linksSection} +
+
+ ${optionPreviews} +
+
+ \` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +}` + } + + // Generate all stories + const stories = Object.entries(config) + .map(([groupKey, definition]) => generateStory(groupKey, definition)) + .filter(Boolean) // Remove empty strings from disabled utilities + .join('\n\n') + + // Generate the complete stories file content + const sourceFilesList = sourceFiles.map(file => `// Generated from ${file}`).join('\n') + const storiesContent = `// Auto-generated utility classes stories +${sourceFilesList} + +${stories} + +// Default export for the story file +export default { + title: 'Utility Classes', + parameters: { + docs: { + description: { + story: 'Utility classes documentation generated from YAML configuration files' + } + } + } +}` + + // Generate CSS content + const cssContent = `/* Utility Classes Demo Styles */ +.utility-classes-demo { + max-width: 100%; + padding: 1rem; +} + +.utility-classes-header { + margin-bottom: 2rem; + padding-bottom: 1rem; + border-bottom: 1px solid #e5e7eb; +} + +.utility-classes-header h2 { + margin: 0 0 0.5rem 0; + color: #1f2937; +} + +.utility-classes-description { + margin: 0 0 1rem 0; + color: #6b7280; + font-size: 0.875rem; +} + +.utility-links { + margin-top: 1rem; +} + +.utility-links h4 { + margin: 0 0 0.5rem 0; + font-size: 0.875rem; + font-weight: 600; + color: #374151; +} + +.utility-links ul { + margin: 0; + padding-left: 1.5rem; +} + +.utility-links li { + margin-bottom: 0.25rem; +} + +.utility-links a { + color: #3b82f6; + text-decoration: none; + font-size: 0.875rem; +} + +.utility-links a:hover { + text-decoration: underline; +} + +.utility-classes-grid { + display: grid; + gap: 1.5rem; +} + +.utility-preview-item { + border: 1px solid #e5e7eb; + border-radius: 0.5rem; + padding: 1rem; + background: #ffffff; +} + +.utility-preview-label { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid #f3f4f6; +} + +.utility-preview-label code { + background: #f3f4f6; + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 0.75rem; + color: #dc2626; + font-weight: 600; +} + +.utility-preview-description { + color: #6b7280; + font-size: 0.875rem; + font-weight: 500; +} + +.utility-preview-demo { + min-height: 2rem; + display: flex; + align-items: center; + padding: 0.5rem; + background: #f9fafb; + border-radius: 0.25rem; +} + +/* Specific utility class demo styles */ +.utility-preview-demo .line-clamp-1, +.utility-preview-demo .line-clamp-2, +.utility-preview-demo .line-clamp-3, +.utility-preview-demo .line-clamp-4, +.utility-preview-demo .line-clamp-5 { + max-width: 200px; +} + +.utility-preview-demo .coverlink, +.utility-preview-demo .coverlink-over { + position: relative; + display: block; + padding: 0.5rem; + background: #3b82f6; + color: white; + text-decoration: none; + border-radius: 0.25rem; +} + +.utility-preview-demo .coverlink:hover, +.utility-preview-demo .coverlink-over:hover { + background: #2563eb; +} + +/* Responsive adjustments */ +@media (min-width: 768px) { + .utility-classes-grid { + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + } +}` + + // Write the stories file + const storiesPath = join(outputDir, 'utility-classes.stories.js') + writeFileSync(storiesPath, storiesContent, 'utf8') + + // Write the CSS file + const cssPath = join(outputDir, 'utility-classes.css') + writeFileSync(cssPath, cssContent, 'utf8') + + console.log(`✅ Generated utility classes stories: ${storiesPath}`) + console.log(`✅ Generated utility classes CSS: ${cssPath}`) + console.log(`✅ Processed ${configs.length} utility style file(s)`) + console.log('✅ Utility classes stories generated successfully!') + +} catch (error) { + console.error('❌ Failed to generate utility classes stories:', error.message) + process.exit(1) +} diff --git a/sdc-addon.ui_styles.yml b/sdc-addon.ui_styles.yml new file mode 100644 index 0000000..3dc969d --- /dev/null +++ b/sdc-addon.ui_styles.yml @@ -0,0 +1,114 @@ +################################################################################ +# UI Styles file pattern +################################################################################ + +rounded: + category: "Rounded" + label: "Rounded" + options: + "rounded-sm": "sm" + "rounded": "default" + "rounded-md": "md" + "rounded-lg": "lg" + "rounded-xl": "xl" + "rounded-2xl": "2xl" + "rounded-3xl": "3xl" + "rounded-none": "none" + "rounded-tr-3xl rounded-bl-3xl": "theme" + previewed_with: + - inline-flex items-center justify-center text-center w-[200px] h-[200px] bg-secondary text-white m-4 + +margin_top: + category: "Spacing" + label: "Margin top" + options: + "mt-xs": xs + "mt-xs md:mt-sm": sm + "mt-xs md:mt-md": md + "mt-xs md:mt-md lg:mt-lg": lg + "mt-xs md:mt-md lg:mt-xl": xl + "mt-xs md:mt-md lg:mt-2xl": 2xl + + previewed_with: + - bg-neutral-content + +margin_bottom: + category: "Spacing" + label: "Margin bottom" + options: + "mb-xs": xs + "mb-xs md:mb-sm": sm + "mb-xs md:mb-md": md + "mb-xs md:mb-md lg:mb-lg": lg + "mb-xs md:mb-md lg:mb-xl": xl + "mb-xs md:mb-md lg:mb-2xl": 2xl + + previewed_with: + - bg-neutral-content + +margin_start: + enabled: false + category: "Spacing" + label: "Margin start" + options: + "ms-xs": xs + "ms-xs md:ms-sm": sm + "ms-xs md:ms-md": md + "ms-xs md:ms-md lg:ms-lg": lg + "ms-xs md:ms-md lg:ms-xl": xl + "ms-xs md:ms-md lg:ms-2xl": 2xl + + previewed_with: + - bg-neutral-content + +margin_end: + enabled: false + category: "Spacing" + label: "Margin end" + options: + "me-xs": xs + "me-xs md:me-sm": sm + "me-xs md:me-md": md + "me-xs md:me-md lg:me-lg": lg + "me-xs md:me-md lg:me-xl": xl + "me-xs md:me-md lg:me-2xl": 2xl + previewed_with: + - bg-neutral-content + +gap: + category: "Spacing" + label: "Gap" + options: + "gap-between-xs": xs + "gap-between-xs md:gap-between-sm": sm + "gap-between-xs md:gap-between-md": md + "gap-between-xs md:gap-between-md gap-between-lg": lg + "gap-between-xs md:gap-between-md gap-between-xl": xl + "gap-between-xs md:gap-between-md lg:gap-between-2xl": 2xl + previewed_with: + - bg-neutral-content + +border_width: + category: "Borders" + label: "Border width" + options: + border-0: none + border: 1px + border-2: 2px + border-4: 4px + border-8: 8px + +border_style: + category: "Borders" + label: "Border style" + options: + border-solid: solid + border-dashed: dashed + border-dotted: dotted + border-double: double + previewed_with: + - border border-2 + +thirdPartySettings: + sdcStorybook: + disableUtilityClasses: false \ No newline at end of file diff --git a/src/preset.ts b/src/preset.ts index 4691a0c..615f321 100644 --- a/src/preset.ts +++ b/src/preset.ts @@ -1,6 +1,7 @@ import YamlStoriesPlugin, { yamlStoriesIndexer, } from './vite-plugin-storybook-yaml-stories.ts' +import UtilityClassesPlugin from './vite-plugin-utility-classes.ts' import { mergeConfig } from 'vite' import type { UserConfig } from 'vite' import type { Indexer } from 'storybook/internal/types' @@ -53,6 +54,7 @@ export async function viteFinal(config: UserConfig, options: SDCAddonOptions) { }), ...(twigPlugin ? [twigPlugin] : []), YamlStoriesPlugin({ ...options, globalDefs, namespaces }), + UtilityClassesPlugin(), ], optimizeDeps: { exclude: ['vite-plugin-twig-drupal', 'vite-plugin-twing-drupal'], @@ -82,6 +84,7 @@ export const previewHead: StorybookConfig['previewHead'] = (head: string) => ` word-wrap: normal; } + diff --git a/src/utilityClassesGenerator.ts b/src/utilityClassesGenerator.ts new file mode 100644 index 0000000..bd86197 --- /dev/null +++ b/src/utilityClassesGenerator.ts @@ -0,0 +1,331 @@ +import { readFileSync } from 'fs' +import { join } from 'path' +import yaml from 'yaml' +import { capitalize } from './utils.js' + +export interface UtilityClassDefinition { + category: string + label: string + description?: string + links?: string[] + options: Record + previewed_with?: string[] + enabled?: boolean +} + +export interface UtilityClassesConfig { + [key: string]: UtilityClassDefinition +} + +/** + * Parses the utility classes YAML file and returns the configuration + */ +export function parseUtilityClassesYaml(filePath: string): UtilityClassesConfig { + try { + const fileContent = readFileSync(filePath, 'utf8') + return yaml.parse(fileContent) + } catch (error) { + throw new Error(`Failed to parse utility classes YAML: ${error}`) + } +} + +/** + * Generates a story template for a utility class group + */ +export function generateUtilityClassStory( + groupKey: string, + definition: UtilityClassDefinition +): string { + const { category, label, description, options, previewed_with = [], links = [] } = definition + + // Skip disabled utility classes + if (definition.enabled === false) { + return '' + } + + const storyName = capitalize(groupKey.replace(/_/g, ' ')) + const storyKey = groupKey + + // Generate the preview HTML for each option + const generatePreviewHtml = (className: string, label: string): string => { + const previewClasses = previewed_with.join(' ') + const combinedClasses = `${className} ${previewClasses}`.trim() + + return ` +
+
+ ${className} + ${label} +
+
+
+ ${generateDemoContent(groupKey, className)} +
+
+
` + } + + // Generate demo content based on utility type + const generateDemoContent = (groupKey: string, className: string): string => { + if (groupKey.includes('font_size') || groupKey.includes('typography')) { + return 'Sample text to demonstrate typography' + } + if (groupKey.includes('margin') || groupKey.includes('padding')) { + return 'Content with spacing' + } + if (groupKey.includes('border')) { + return 'Border demo' + } + if (groupKey.includes('rounded')) { + return 'Rounded corners' + } + if (groupKey.includes('line_clamp')) { + return 'This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines.' + } + if (groupKey.includes('coverlink')) { + return 'Cover link demo' + } + return 'Demo content' + } + + // Generate all option previews + const optionPreviews = Object.entries(options) + .map(([className, label]) => generatePreviewHtml(className, label)) + .join('\n') + + // Generate links section if available + const linksSection = links.length > 0 + ? ` + ` + : '' + + return ` +export const ${storyKey} = { + title: 'Utility Classes/${category}/${label}', + parameters: { + docs: { + description: { + story: \`${description || `Utility classes for ${label.toLowerCase()}`}\` + } + } + }, + render: () => { + return \` +
+
+

${label}

+ ${description ? `

${description}

` : ''} + ${linksSection} +
+
+ ${optionPreviews} +
+
+ \` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +}` +} + +/** + * Generates the complete stories file content for utility classes + */ +export function generateUtilityClassesStories(config: UtilityClassesConfig): string { + const stories = Object.entries(config) + .map(([groupKey, definition]) => generateUtilityClassStory(groupKey, definition)) + .filter(Boolean) // Remove empty strings from disabled utilities + .join('\n\n') + + return `// Auto-generated utility classes stories +// Generated from *.ui_styles.yml files + +${stories} + +// Default export for the story file +export default { + title: 'Utility Classes', + parameters: { + docs: { + description: { + story: 'Utility classes documentation generated from YAML configuration' + } + } + } +}` +} + +/** + * Generates CSS styles for the utility classes demo + */ +export function generateUtilityClassesCSS(): string { + return ` +/* Utility Classes Demo Styles */ +.utility-classes-demo { + max-width: 100%; + padding: 1rem; +} + +.utility-classes-header { + margin-bottom: 2rem; + padding-bottom: 1rem; + border-bottom: 1px solid #e5e7eb; +} + +.utility-classes-header h2 { + margin: 0 0 0.5rem 0; + color: #1f2937; +} + +.utility-classes-description { + margin: 0 0 1rem 0; + color: #6b7280; + font-size: 0.875rem; +} + +.utility-links { + margin-top: 1rem; +} + +.utility-links h4 { + margin: 0 0 0.5rem 0; + font-size: 0.875rem; + font-weight: 600; + color: #374151; +} + +.utility-links ul { + margin: 0; + padding-left: 1.5rem; +} + +.utility-links li { + margin-bottom: 0.25rem; +} + +.utility-links a { + color: #3b82f6; + text-decoration: none; + font-size: 0.875rem; +} + +.utility-links a:hover { + text-decoration: underline; +} + +.utility-classes-grid { + display: grid; + gap: 1.5rem; +} + +.utility-preview-item { + border: 1px solid #e5e7eb; + border-radius: 0.5rem; + padding: 1rem; + background: #ffffff; +} + +.utility-preview-label { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid #f3f4f6; +} + +.utility-preview-label code { + background: #f3f4f6; + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 0.75rem; + color: #dc2626; + font-weight: 600; +} + +.utility-preview-description { + color: #6b7280; + font-size: 0.875rem; + font-weight: 500; +} + +.utility-preview-demo { + min-height: 2rem; + display: flex; + align-items: center; + padding: 0.5rem; + background: #f9fafb; + border-radius: 0.25rem; +} + +/* Specific utility class demo styles */ +.utility-preview-demo .line-clamp-1, +.utility-preview-demo .line-clamp-2, +.utility-preview-demo .line-clamp-3, +.utility-preview-demo .line-clamp-4, +.utility-preview-demo .line-clamp-5 { + max-width: 200px; +} + +.utility-preview-demo .coverlink, +.utility-preview-demo .coverlink-over { + position: relative; + display: block; + padding: 0.5rem; + background: #3b82f6; + color: white; + text-decoration: none; + border-radius: 0.25rem; +} + +.utility-preview-demo .coverlink:hover, +.utility-preview-demo .coverlink-over:hover { + background: #2563eb; +} + +/* Responsive adjustments */ +@media (min-width: 768px) { + .utility-classes-grid { + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + } +} +` +} + +/** + * Main function to generate utility classes stories from YAML file + */ +export function generateUtilityClassesFromYaml(yamlFilePath: string, outputDir: string): void { + try { + // Parse the YAML configuration + const config = parseUtilityClassesYaml(yamlFilePath) + + // Generate the stories content + const storiesContent = generateUtilityClassesStories(config) + + // Generate CSS content + const cssContent = generateUtilityClassesCSS() + + // Write the stories file + const storiesPath = join(outputDir, 'utility-classes.stories.js') + require('fs').writeFileSync(storiesPath, storiesContent, 'utf8') + + // Write the CSS file + const cssPath = join(outputDir, 'utility-classes.css') + require('fs').writeFileSync(cssPath, cssContent, 'utf8') + + console.log(`✅ Generated utility classes stories: ${storiesPath}`) + console.log(`✅ Generated utility classes CSS: ${cssPath}`) + + } catch (error) { + console.error('❌ Error generating utility classes stories:', error) + throw error + } +} diff --git a/src/vite-plugin-utility-classes.ts b/src/vite-plugin-utility-classes.ts new file mode 100644 index 0000000..b5e123d --- /dev/null +++ b/src/vite-plugin-utility-classes.ts @@ -0,0 +1,405 @@ +import { readFileSync, writeFileSync, existsSync, readdirSync, unlinkSync } from 'fs' +import { join, dirname, extname } from 'path' +import { parse as parseYaml } from 'yaml' +import { globSync } from 'glob' +import { logger } from './logger.ts' + +// Helper function to capitalize strings +const capitalize = (str: string) => str[0].toUpperCase() + str.slice(1) + +// Helper function to convert snake_case to Title Case +const toTitleCase = (str: string) => str.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) + +// Generate demo content based on utility type +const generateDemoContent = (groupKey: string, className: string): string => { + if (groupKey.includes('font_size') || groupKey.includes('typography')) { + return 'Sample text to demonstrate typography' + } + if (groupKey.includes('margin') || groupKey.includes('padding')) { + return 'Content with spacing' + } + if (groupKey.includes('border')) { + return 'Border demo' + } + if (groupKey.includes('rounded')) { + return 'Rounded corners' + } + if (groupKey.includes('line_clamp')) { + return 'This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines.' + } + if (groupKey.includes('coverlink')) { + return `Cover link demo` + } + return 'Demo content' +} + +// Generate preview HTML for each option +const generatePreviewHtml = (groupKey: string, className: string, label: string, previewedWith: string[] = []): string => { + const previewClasses = previewedWith.join(' ') + const combinedClasses = `${className} ${previewClasses}`.trim() + const demoContent = generateDemoContent(groupKey, className) + + return ` +
+
+ ${className} + ${label} +
+
+
+ ${demoContent} +
+
+
` +} + +// Generate story for a utility class group +const generateStory = (namespace: string, groupKey: string, definition: any): string => { + const { category, label, description, options, previewed_with = [], links = [] } = definition + + // Skip disabled utility classes + if (definition.enabled === false) { + return '' + } + + const storyKey = groupKey + + // Generate all option previews + const optionPreviews = Object.entries(options) + .map(([className, optionLabel]) => generatePreviewHtml(groupKey, className, optionLabel as string, previewed_with)) + .join('\n') + + // Generate links section if available + const linksSection = links.length > 0 + ? ` + ` + : '' + + return ` +export const ${storyKey} = { + title: '${namespace}/Utility Classes/${category}/${label}', + parameters: { + docs: { + description: { + story: \`${description || `Utility classes for ${label.toLowerCase()}`}\` + } + } + }, + render: () => { + return \` +
+
+

${label}

+ ${description ? `

${description}

` : ''} + ${linksSection} +
+
+ ${optionPreviews} +
+
+ \` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +}` +} + +// Generate CSS content +const generateCSS = (): string => { + return `/* Utility Classes Demo Styles */ +.utility-classes-demo { + max-width: 100%; + padding: 1rem; +} + +.utility-classes-header { + margin-bottom: 2rem; + padding-bottom: 1rem; + border-bottom: 1px solid #e5e7eb; +} + +.utility-classes-header h2 { + margin: 0 0 0.5rem 0; + color: #1f2937; +} + +.utility-classes-description { + margin: 0 0 1rem 0; + color: #6b7280; + font-size: 0.875rem; +} + +.utility-links { + margin-top: 1rem; +} + +.utility-links h4 { + margin: 0 0 0.5rem 0; + font-size: 0.875rem; + font-weight: 600; + color: #374151; +} + +.utility-links ul { + margin: 0; + padding-left: 1.5rem; +} + +.utility-links li { + margin-bottom: 0.25rem; +} + +.utility-links a { + color: #3b82f6; + text-decoration: none; + font-size: 0.875rem; +} + +.utility-links a:hover { + text-decoration: underline; +} + +.utility-classes-grid { + display: grid; + gap: 1.5rem; +} + +.utility-preview-item { + border: 1px solid #e5e7eb; + border-radius: 0.5rem; + padding: 1rem; + background: #ffffff; +} + +.utility-preview-label { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid #f3f4f6; +} + +.utility-preview-label code { + background: #f3f4f6; + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 0.75rem; + color: #dc2626; + font-weight: 600; +} + +.utility-preview-description { + color: #6b7280; + font-size: 0.875rem; + font-weight: 500; +} + +.utility-preview-demo { + min-height: 2rem; + display: flex; + align-items: center; + padding: 0.5rem; + background: #f9fafb; + border-radius: 0.25rem; +} + +/* Specific utility class demo styles */ +.utility-preview-demo .line-clamp-1, +.utility-preview-demo .line-clamp-2, +.utility-preview-demo .line-clamp-3, +.utility-preview-demo .line-clamp-4, +.utility-preview-demo .line-clamp-5 { + max-width: 200px; +} + +.utility-preview-demo .coverlink, +.utility-preview-demo .coverlink-over { + position: relative; + display: block; + padding: 0.5rem; + background: #3b82f6; + color: white; + text-decoration: none; + border-radius: 0.25rem; +} + +.utility-preview-demo .coverlink:hover, +.utility-preview-demo .coverlink-over:hover { + background: #2563eb; +} + +/* Responsive adjustments */ +@media (min-width: 768px) { + .utility-classes-grid { + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + } +}` +} + +// Find all *.ui_styles.yml files in the project +const findUtilityStyleFiles = (rootDir: string): string[] => { + try { + // Search for all *.ui_styles.yml files recursively + const pattern = join(rootDir, '**', '*.ui_styles.yml') + const files = globSync(pattern) + return files + } catch (error) { + logger.warn(`Error searching for *.ui_styles.yml files: ${error}`) + return [] + } +} + +// Merge multiple YAML configurations (kept for potential future use) +const mergeConfigurations = (configs: Record[]): Record => { + const merged: Record = {} + + configs.forEach((config, index) => { + Object.entries(config) + // Only include entries that look like utility definitions (must have options object) + .filter(([, value]) => isUtilityDefinition(value)) + .forEach(([key, value]) => { + // If key already exists, append index to make it unique + const uniqueKey = merged[key] ? `${key}_${index}` : key + merged[uniqueKey] = value + }) + }) + + return merged +} + +// Determine if a config entry is a utility definition +const isUtilityDefinition = (value: any): boolean => { + return ( + value && + typeof value === 'object' && + typeof value.options === 'object' && + value.options !== null + ) +} + +// Derive namespace from YAML file path +const deriveNamespaceFromPath = (rootDir: string, yamlPath: string): string => { + const rel = yamlPath.startsWith(rootDir) ? yamlPath.slice(rootDir.length + 1) : yamlPath + const parts = rel.split('/') + // Prefer parent directory name as namespace; fallback to filename without extension(s) + if (parts.length > 1) { + return parts[parts.length - 2] + } + const base = parts[parts.length - 1] + return base.replace(/\.ui_styles\.yml$/, '') +} + +// Check if a parsed ui_styles config requests disabling generation +const isConfigDisabled = (config: Record): boolean => { + const tps = config?.thirdPartySettings || {} + const sdcStorybook = tps?.sdcStorybook || {} + const utilityClassesA = tps?.utilityClasses || {} + const utilityClassesB = sdcStorybook?.utilityClasses || {} + const disableA = tps?.disableUtilityClasses + const disableB = sdcStorybook?.disableUtilityClasses + // Also support top-level flags if provided + const topLevel = config?.utilityClasses || {} + return ( + config?.enabled === false || + utilityClassesA?.enabled === false || + utilityClassesB?.enabled === false || + topLevel?.enabled === false || + disableA === true || + disableB === true || + config?.disableUtilityClasses === true + ) +} + +// Vite plugin for utility classes generation +export default (options: { yamlPath?: string; outputDir?: string } = {}) => ({ + name: 'vite-plugin-utility-classes', + buildStart() { + const rootDir = process.cwd() + const outputDir = options.outputDir || join(rootDir, 'stories') + + // Find all *.ui_styles.yml files + const yamlFiles = findUtilityStyleFiles(rootDir) + + if (yamlFiles.length === 0) { + logger.warn(`No *.ui_styles.yml files found in project`) + return + } + + try { + logger.info(`Found ${yamlFiles.length} utility style file(s): ${yamlFiles.join(', ')}`) + + // Clean legacy combined stories file if present + const legacyStoriesPath = join(outputDir, 'utility-classes.stories.js') + if (existsSync(legacyStoriesPath)) { + try { unlinkSync(legacyStoriesPath) } catch {} + } + + let numGenerated = 0 + let wroteCss = false + + yamlFiles.forEach(yamlPath => { + if (!existsSync(yamlPath)) return + const fileContent = readFileSync(yamlPath, 'utf8') + const config = parseYaml(fileContent) as Record + + // Respect disable flags in the config + if (isConfigDisabled(config)) { + logger.info(`Skipping utility classes generation for disabled file: ${yamlPath}`) + // Also remove a previously generated file for this namespace, if exists + const ns = deriveNamespaceFromPath(rootDir, yamlPath) + const perFileStoriesPath = join(outputDir, `utility-classes.${ns}.stories.js`) + try { if (existsSync(perFileStoriesPath)) unlinkSync(perFileStoriesPath) } catch {} + return + } + + // Extract only utility definition entries + const utilities = Object.entries(config) + .filter(([, value]) => isUtilityDefinition(value)) + + if (utilities.length === 0) return + + const namespace = deriveNamespaceFromPath(rootDir, yamlPath) + const stories = utilities + .map(([groupKey, definition]) => generateStory(namespace, groupKey as string, definition)) + .filter(Boolean) + .join('\n\n') + + const storiesContent = `// Auto-generated utility classes stories\n// Generated from ${yamlPath}\n\nexport default {\n title: '${namespace}/Utility Classes',\n parameters: {\n docs: {\n description: {\n story: 'Utility classes documentation generated from ${yamlPath}'\n }\n }\n }\n}\n\n${stories}` + + const storiesPath = join(outputDir, `utility-classes.${namespace}.stories.js`) + writeFileSync(storiesPath, storiesContent, 'utf8') + numGenerated += 1 + logger.info(`✅ Generated utility classes stories: ${storiesPath}`) + + if (!wroteCss) { + const cssContent = generateCSS() + const cssPath = join(outputDir, 'utility-classes.css') + writeFileSync(cssPath, cssContent, 'utf8') + logger.info(`✅ Generated utility classes CSS: ${cssPath}`) + wroteCss = true + } + }) + + if (numGenerated === 0) { + logger.warn(`No valid utility style configurations found`) + // Cleanup shared CSS if present + const cssPath = join(outputDir, 'utility-classes.css') + try { if (existsSync(cssPath)) unlinkSync(cssPath) } catch {} + } else { + logger.info(`✅ Processed ${numGenerated} utility style file(s)`) + } + + } catch (error) { + logger.error(`❌ Failed to generate utility classes stories: ${error}`) + throw error + } + }, +}) diff --git a/stories/utility-classes.css b/stories/utility-classes.css new file mode 100644 index 0000000..f453614 --- /dev/null +++ b/stories/utility-classes.css @@ -0,0 +1,130 @@ +/* Utility Classes Demo Styles */ +.utility-classes-demo { + max-width: 100%; + padding: 1rem; +} + +.utility-classes-header { + margin-bottom: 2rem; + padding-bottom: 1rem; + border-bottom: 1px solid #e5e7eb; +} + +.utility-classes-header h2 { + margin: 0 0 0.5rem 0; + color: #1f2937; +} + +.utility-classes-description { + margin: 0 0 1rem 0; + color: #6b7280; + font-size: 0.875rem; +} + +.utility-links { + margin-top: 1rem; +} + +.utility-links h4 { + margin: 0 0 0.5rem 0; + font-size: 0.875rem; + font-weight: 600; + color: #374151; +} + +.utility-links ul { + margin: 0; + padding-left: 1.5rem; +} + +.utility-links li { + margin-bottom: 0.25rem; +} + +.utility-links a { + color: #3b82f6; + text-decoration: none; + font-size: 0.875rem; +} + +.utility-links a:hover { + text-decoration: underline; +} + +.utility-classes-grid { + display: grid; + gap: 1.5rem; +} + +.utility-preview-item { + border: 1px solid #e5e7eb; + border-radius: 0.5rem; + padding: 1rem; + background: #ffffff; +} + +.utility-preview-label { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid #f3f4f6; +} + +.utility-preview-label code { + background: #f3f4f6; + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 0.75rem; + color: #dc2626; + font-weight: 600; +} + +.utility-preview-description { + color: #6b7280; + font-size: 0.875rem; + font-weight: 500; +} + +.utility-preview-demo { + min-height: 2rem; + display: flex; + align-items: center; + padding: 0.5rem; + background: #f9fafb; + border-radius: 0.25rem; +} + +/* Specific utility class demo styles */ +.utility-preview-demo .line-clamp-1, +.utility-preview-demo .line-clamp-2, +.utility-preview-demo .line-clamp-3, +.utility-preview-demo .line-clamp-4, +.utility-preview-demo .line-clamp-5 { + max-width: 200px; +} + +.utility-preview-demo .coverlink, +.utility-preview-demo .coverlink-over { + position: relative; + display: block; + padding: 0.5rem; + background: #3b82f6; + color: white; + text-decoration: none; + border-radius: 0.25rem; +} + +.utility-preview-demo .coverlink:hover, +.utility-preview-demo .coverlink-over:hover { + background: #2563eb; +} + +/* Responsive adjustments */ +@media (min-width: 768px) { + .utility-classes-grid { + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + } +} \ No newline at end of file diff --git a/stories/utility-classes.parent-namespace.stories.js b/stories/utility-classes.parent-namespace.stories.js new file mode 100644 index 0000000..5d54cfa --- /dev/null +++ b/stories/utility-classes.parent-namespace.stories.js @@ -0,0 +1,425 @@ +// Auto-generated utility classes stories +// Generated from /Users/fred/Local/sdc-addon/parent-namespace/parent-namespace.ui_styles.yml + +export default { + title: 'parent-namespace/Utility Classes', + parameters: { + docs: { + description: { + story: 'Utility classes documentation generated from /Users/fred/Local/sdc-addon/parent-namespace/parent-namespace.ui_styles.yml' + } + } + } +} + + +export const font_size = { + title: 'parent-namespace/Utility Classes/Typography/Font size', + parameters: { + docs: { + description: { + story: `We use specifics size classes (with custom tokens) for a responsive typography display` + } + } + }, + render: () => { + return ` +
+
+

Font size

+

We use specifics size classes (with custom tokens) for a responsive typography display

+ +
+
+ +
+
+ title-2xl + Title 2XL +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ title-xl + Title XL +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ title-lg + Title LG +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ title-md + Title MD +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ title-sm + Title SM +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ title-xs + Title XS +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ body-xl + Body XL +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ body-lg + Body LG +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ body-md + Body MD +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ body-sm + Body SM +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ body-xs + Body XS +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ label-xl + Label XL +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ label-lg + Label LG +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ label-md + Label MD +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ label-sm + Label SM +
+
+
+ Sample text to demonstrate typography +
+
+
+ +
+
+ label-xs + Label XS +
+
+
+ Sample text to demonstrate typography +
+
+
+
+
+ ` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +} + + +export const line_clamp = { + title: 'parent-namespace/Utility Classes/Utility/Line Clamp', + parameters: { + docs: { + description: { + story: `Utilities for clamping text to a specific number of lines.` + } + } + }, + render: () => { + return ` +
+
+

Line Clamp

+

Utilities for clamping text to a specific number of lines.

+ + +
+
+ +
+
+ line-clamp-1 + line-clamp-1 +
+
+
+ This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines. +
+
+
+ +
+
+ line-clamp-2 + line-clamp-2 +
+
+
+ This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines. +
+
+
+ +
+
+ line-clamp-3 + line-clamp-3 +
+
+
+ This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines. +
+
+
+ +
+
+ line-clamp-4 + line-clamp-4 +
+
+
+ This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines. +
+
+
+ +
+
+ line-clamp-5 + line-clamp-5 +
+
+
+ This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines. +
+
+
+
+
+ ` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +} + + +export const typography = { + title: 'parent-namespace/Utility Classes/Utility/Typography', + parameters: { + docs: { + description: { + story: `Utility classes for typography` + } + } + }, + render: () => { + return ` +
+
+

Typography

+ + +
+
+ +
+
+ prose + Formatted Text (prose) +
+
+
+ Sample text to demonstrate typography +
+
+
+
+
+ ` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +} + + +export const font_family = { + title: 'parent-namespace/Utility Classes/Typography/Font family', + parameters: { + docs: { + description: { + story: `You can control the typeface of text using the font family utilities.` + } + } + }, + render: () => { + return ` +
+
+

Font family

+

You can control the typeface of text using the font family utilities.

+ + +
+
+ +
+
+ font-mono + Mono +
+
+
+ Demo content +
+
+
+ +
+
+ font-sans + Sans +
+
+
+ Demo content +
+
+
+
+
+ ` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +} \ No newline at end of file diff --git a/stories/utility-classes.sdc-addon.stories.js b/stories/utility-classes.sdc-addon.stories.js new file mode 100644 index 0000000..88c0375 --- /dev/null +++ b/stories/utility-classes.sdc-addon.stories.js @@ -0,0 +1,613 @@ +// Auto-generated utility classes stories +// Generated from /Users/fred/Local/sdc-addon/sdc-addon.ui_styles.yml + +export default { + title: 'sdc-addon/Utility Classes', + parameters: { + docs: { + description: { + story: 'Utility classes documentation generated from /Users/fred/Local/sdc-addon/sdc-addon.ui_styles.yml' + } + } + } +} + + +export const rounded = { + title: 'sdc-addon/Utility Classes/Rounded/Rounded', + parameters: { + docs: { + description: { + story: `Utility classes for rounded` + } + } + }, + render: () => { + return ` +
+
+

Rounded

+ + +
+
+ +
+
+ rounded-sm + sm +
+
+
+ Rounded corners +
+
+
+ +
+
+ rounded + default +
+
+
+ Rounded corners +
+
+
+ +
+
+ rounded-md + md +
+
+
+ Rounded corners +
+
+
+ +
+
+ rounded-lg + lg +
+
+
+ Rounded corners +
+
+
+ +
+
+ rounded-xl + xl +
+
+
+ Rounded corners +
+
+
+ +
+
+ rounded-2xl + 2xl +
+
+
+ Rounded corners +
+
+
+ +
+
+ rounded-3xl + 3xl +
+
+
+ Rounded corners +
+
+
+ +
+
+ rounded-none + none +
+
+
+ Rounded corners +
+
+
+ +
+
+ rounded-tr-3xl rounded-bl-3xl + theme +
+
+
+ Rounded corners +
+
+
+
+
+ ` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +} + + +export const margin_top = { + title: 'sdc-addon/Utility Classes/Spacing/Margin top', + parameters: { + docs: { + description: { + story: `Utility classes for margin top` + } + } + }, + render: () => { + return ` +
+
+

Margin top

+ + +
+
+ +
+
+ mt-xs + xs +
+
+
+ Content with spacing +
+
+
+ +
+
+ mt-xs md:mt-sm + sm +
+
+
+ Content with spacing +
+
+
+ +
+
+ mt-xs md:mt-md + md +
+
+
+ Content with spacing +
+
+
+ +
+
+ mt-xs md:mt-md lg:mt-lg + lg +
+
+
+ Content with spacing +
+
+
+ +
+
+ mt-xs md:mt-md lg:mt-xl + xl +
+
+
+ Content with spacing +
+
+
+ +
+
+ mt-xs md:mt-md lg:mt-2xl + 2xl +
+
+
+ Content with spacing +
+
+
+
+
+ ` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +} + + +export const margin_bottom = { + title: 'sdc-addon/Utility Classes/Spacing/Margin bottom', + parameters: { + docs: { + description: { + story: `Utility classes for margin bottom` + } + } + }, + render: () => { + return ` +
+
+

Margin bottom

+ + +
+
+ +
+
+ mb-xs + xs +
+
+
+ Content with spacing +
+
+
+ +
+
+ mb-xs md:mb-sm + sm +
+
+
+ Content with spacing +
+
+
+ +
+
+ mb-xs md:mb-md + md +
+
+
+ Content with spacing +
+
+
+ +
+
+ mb-xs md:mb-md lg:mb-lg + lg +
+
+
+ Content with spacing +
+
+
+ +
+
+ mb-xs md:mb-md lg:mb-xl + xl +
+
+
+ Content with spacing +
+
+
+ +
+
+ mb-xs md:mb-md lg:mb-2xl + 2xl +
+
+
+ Content with spacing +
+
+
+
+
+ ` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +} + + +export const gap = { + title: 'sdc-addon/Utility Classes/Spacing/Gap', + parameters: { + docs: { + description: { + story: `Utility classes for gap` + } + } + }, + render: () => { + return ` +
+
+

Gap

+ + +
+
+ +
+
+ gap-between-xs + xs +
+
+
+ Demo content +
+
+
+ +
+
+ gap-between-xs md:gap-between-sm + sm +
+
+
+ Demo content +
+
+
+ +
+
+ gap-between-xs md:gap-between-md + md +
+
+
+ Demo content +
+
+
+ +
+
+ gap-between-xs md:gap-between-md gap-between-lg + lg +
+
+
+ Demo content +
+
+
+ +
+
+ gap-between-xs md:gap-between-md gap-between-xl + xl +
+
+
+ Demo content +
+
+
+ +
+
+ gap-between-xs md:gap-between-md lg:gap-between-2xl + 2xl +
+
+
+ Demo content +
+
+
+
+
+ ` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +} + + +export const border_width = { + title: 'sdc-addon/Utility Classes/Borders/Border width', + parameters: { + docs: { + description: { + story: `Utility classes for border width` + } + } + }, + render: () => { + return ` +
+
+

Border width

+ + +
+
+ +
+
+ border-0 + none +
+
+
+ Border demo +
+
+
+ +
+
+ border + 1px +
+
+
+ Border demo +
+
+
+ +
+
+ border-2 + 2px +
+
+
+ Border demo +
+
+
+ +
+
+ border-4 + 4px +
+
+
+ Border demo +
+
+
+ +
+
+ border-8 + 8px +
+
+
+ Border demo +
+
+
+
+
+ ` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +} + + +export const border_style = { + title: 'sdc-addon/Utility Classes/Borders/Border style', + parameters: { + docs: { + description: { + story: `Utility classes for border style` + } + } + }, + render: () => { + return ` +
+
+

Border style

+ + +
+
+ +
+
+ border-solid + solid +
+
+
+ Border demo +
+
+
+ +
+
+ border-dashed + dashed +
+
+
+ Border demo +
+
+
+ +
+
+ border-dotted + dotted +
+
+
+ Border demo +
+
+
+ +
+
+ border-double + double +
+
+
+ Border demo +
+
+
+
+
+ ` + }, + play: async ({ canvasElement }) => { + Drupal.attachBehaviors(canvasElement, window.drupalSettings) + }, +} \ No newline at end of file From f22f45039943d3382db4df1faded650653ab5936 Mon Sep 17 00:00:00 2001 From: GitFred Date: Tue, 14 Oct 2025 11:32:13 +0200 Subject: [PATCH 06/12] delete cbd_default component --- .../cbd_default/cbd_default.component.yml | 101 ------------------ .../cbd_default/cbd_default.default.story.yml | 20 ---- components/cbd_default/cbd_default.twig | 35 ------ 3 files changed, 156 deletions(-) delete mode 100644 components/cbd_default/cbd_default.component.yml delete mode 100644 components/cbd_default/cbd_default.default.story.yml delete mode 100644 components/cbd_default/cbd_default.twig diff --git a/components/cbd_default/cbd_default.component.yml b/components/cbd_default/cbd_default.component.yml deleted file mode 100644 index aecfcb1..0000000 --- a/components/cbd_default/cbd_default.component.yml +++ /dev/null @@ -1,101 +0,0 @@ -name: "CBD default" -description: "This is a starter kit CBD component. It contains standard slots and props type definitions." -group: "CBD" -status: "experimental" -links: - - "https://cbd.manager.com" -slots: - title: - title: Title - description: "Text for heading" - examples: - - "A starterkit kit CBD component" - top: - title: Top - description: "Top content" - examples: - - "This is a boilerplate by ui suite CBD module" - content: - title: Content - description: "Main content" - examples: - - 'Image' - bottom: - title: Bottom - description: "Bottom content" - label: - title: Label - description: "Expect text label" - icon: - title: Icon - description: "Expect svg icon" -props: - type: object - properties: - extra_classes: - type: array - items: - type: string - examples: - - 'my-class' - margin_top: - title: "Margin top" - $ref: "ui-patterns://enum" - enum: - - mb-8 - - mb-16 - - mb-24 - - mb-32 - - mb-40 - - mb-48 - - mb-56 - - mb-64 - examples: - - 'mb-8' - component_id: - title: "Component ID" - type: string - $ref: 'ui-patterns://identifier' - heading_level: - title: "Heading level" - $ref: "ui-patterns://enum" - enum: - - 2 - - 3 - - 4 - - 5 - - 6 - "meta:enum": - 2: "h2 (Default)" - 3: h3 - 4: h4 - 5: h5 - 6: h6 - url: - title: "URL" - description: "An url string for the link" - type: string - format: uri - external: - title: "External url" - description: "Set to True to add a target _blank attribute to the link" - type: boolean - color: - title: "Color" - description: "A list of available colors" - $ref: "ui-patterns://enum" - enum: - - primary - - secondary - - tertiary - - black - - white - examples: - - secondary - bg_color: - title: "Background color style" - description: "Expect a free background color used as inline style. Must be a value accepted by background-color css property" - type: string -thirdPartySettings: - sdcStorybook: - disableBasicStory: true diff --git a/components/cbd_default/cbd_default.default.story.yml b/components/cbd_default/cbd_default.default.story.yml deleted file mode 100644 index 2603bc1..0000000 --- a/components/cbd_default/cbd_default.default.story.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Default -library_wrapper: '
{{ _story }}
' -slots: - title: "The default CDB component." - content: - - "A starterkit kit CBD component" - - theme: image - uri: 'https://dummyimage.com/280x150/35997b/fff' - alt: Image - attributes: - class: 'foo' - -props: - component_id: "component-1" - url: "http://www.google.fr" - margin_top: "mb-8" - extra_classes: - - "bg-base-100" - - "shadow-xl" - diff --git a/components/cbd_default/cbd_default.twig b/components/cbd_default/cbd_default.twig deleted file mode 100644 index 76b927a..0000000 --- a/components/cbd_default/cbd_default.twig +++ /dev/null @@ -1,35 +0,0 @@ -{% set attributes = extra_classes ? attributes.addClass(extra_classes) : attributes %} -{% set attributes = margin ? attributes.addClass(margin) : attributes %} - -{% set attributes = attributes.setAttribute('id', component_id|default('modal-'~ random())) %} - -{% set heading_level = heading_level|default(2) %} - -{% set color_cl = 'text-current' %} -{% set color_cl = color == 'primary' ? 'text-primary' : color_cl %} -{% set color_cl = color == 'secondary' ? 'text-secondary' : color_cl %} -{% set color_cl = color == 'tertiary' ? 'text-tertiary' : color_cl %} -{% set color_cl = color == 'black' ? 'text-black' : color_cl %} -{% set color_cl = color == 'white' ? 'text-white' : color_cl %} - -
- {% if title %} - - {{ title }} - - {% endif %} - {% if top %} -
- {{ top }} -
- {% endif %} - {% if content %} -
- {{ content }} -
- {% endif %} - {% if url %} - A simple link example - {% endif %} - -
From 7593f89206e86f7874d72c45c70c848d2e95b647 Mon Sep 17 00:00:00 2001 From: GitFred Date: Tue, 14 Oct 2025 11:40:54 +0200 Subject: [PATCH 07/12] add ui_styles reference --- UTILITY_CLASSES_GENERATOR.md | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/UTILITY_CLASSES_GENERATOR.md b/UTILITY_CLASSES_GENERATOR.md index 0df7d91..e51e7ea 100644 --- a/UTILITY_CLASSES_GENERATOR.md +++ b/UTILITY_CLASSES_GENERATOR.md @@ -44,38 +44,8 @@ npm run generate:utility-stories ### YAML Structure -The generator expects a YAML file with the following structure: - -```yaml -utility_group_name: - category: "Category Name" - label: "Display Label" - description: "Optional description" - links: - - "https://example.com/docs" - options: - class-name: "Display Name" - another-class: "Another Display Name" - previewed_with: - - "additional-class-1" - - "additional-class-2" - enabled: true # Optional, defaults to true -``` - -### Example YAML Entry - -```yaml -font_size_dt: - category: "Typography DT" - label: "Font size" - description: "We use specifics size classes (with custom tokens) for a responsive typography display" - options: - title-2xl: Title 2XL - title-xl: Title XL - title-lg: Title LG - previewed_with: - - mb-4 -``` +The generator expects a YAML file with the ui_styles module pattern +https://www.drupal.org/project/ui_styles ## Generated Stories Structure From acf0af53e0d4dace6df8b865cf13ffd2a96d3354 Mon Sep 17 00:00:00 2001 From: GitFred Date: Tue, 14 Oct 2025 12:17:37 +0200 Subject: [PATCH 08/12] remove harcoded references --- src/utilityClassesGenerator.ts | 2 +- src/vite-plugin-utility-classes.ts | 167 ++---------------- stories/utility-classes.css | 3 +- ...tility-classes.parent-namespace.stories.js | 48 ++--- stories/utility-classes.sdc-addon.stories.js | 72 ++++---- 5 files changed, 73 insertions(+), 219 deletions(-) diff --git a/src/utilityClassesGenerator.ts b/src/utilityClassesGenerator.ts index bd86197..0551a8f 100644 --- a/src/utilityClassesGenerator.ts +++ b/src/utilityClassesGenerator.ts @@ -1,7 +1,7 @@ import { readFileSync } from 'fs' import { join } from 'path' import yaml from 'yaml' -import { capitalize } from './utils.js' +import { capitalize } from './utils.ts' export interface UtilityClassDefinition { category: string diff --git a/src/vite-plugin-utility-classes.ts b/src/vite-plugin-utility-classes.ts index b5e123d..e8194f0 100644 --- a/src/vite-plugin-utility-classes.ts +++ b/src/vite-plugin-utility-classes.ts @@ -3,6 +3,7 @@ import { join, dirname, extname } from 'path' import { parse as parseYaml } from 'yaml' import { globSync } from 'glob' import { logger } from './logger.ts' +import { generateUtilityClassesCSS } from './utilityClassesGenerator.ts' // Helper function to capitalize strings const capitalize = (str: string) => str[0].toUpperCase() + str.slice(1) @@ -10,34 +11,19 @@ const capitalize = (str: string) => str[0].toUpperCase() + str.slice(1) // Helper function to convert snake_case to Title Case const toTitleCase = (str: string) => str.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) -// Generate demo content based on utility type -const generateDemoContent = (groupKey: string, className: string): string => { - if (groupKey.includes('font_size') || groupKey.includes('typography')) { - return 'Sample text to demonstrate typography' +// Generate demo content without hardcoded group checks; allow YAML to override via preview_content +const generateDemoContent = (definition: any, className: string): string => { + if (typeof definition?.preview_content === 'string') { + return definition.preview_content } - if (groupKey.includes('margin') || groupKey.includes('padding')) { - return 'Content with spacing' - } - if (groupKey.includes('border')) { - return 'Border demo' - } - if (groupKey.includes('rounded')) { - return 'Rounded corners' - } - if (groupKey.includes('line_clamp')) { - return 'This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines.' - } - if (groupKey.includes('coverlink')) { - return `Cover link demo` - } - return 'Demo content' + return 'Demo' } // Generate preview HTML for each option -const generatePreviewHtml = (groupKey: string, className: string, label: string, previewedWith: string[] = []): string => { +const generatePreviewHtml = (definition: any, className: string, label: string, previewedWith: string[] = []): string => { const previewClasses = previewedWith.join(' ') const combinedClasses = `${className} ${previewClasses}`.trim() - const demoContent = generateDemoContent(groupKey, className) + const demoContent = generateDemoContent(definition, className) return `
@@ -66,7 +52,7 @@ const generateStory = (namespace: string, groupKey: string, definition: any): st // Generate all option previews const optionPreviews = Object.entries(options) - .map(([className, optionLabel]) => generatePreviewHtml(groupKey, className, optionLabel as string, previewed_with)) + .map(([className, optionLabel]) => generatePreviewHtml(definition, className as string, optionLabel as string, previewed_with)) .join('\n') // Generate links section if available @@ -110,139 +96,6 @@ export const ${storyKey} = { }` } -// Generate CSS content -const generateCSS = (): string => { - return `/* Utility Classes Demo Styles */ -.utility-classes-demo { - max-width: 100%; - padding: 1rem; -} - -.utility-classes-header { - margin-bottom: 2rem; - padding-bottom: 1rem; - border-bottom: 1px solid #e5e7eb; -} - -.utility-classes-header h2 { - margin: 0 0 0.5rem 0; - color: #1f2937; -} - -.utility-classes-description { - margin: 0 0 1rem 0; - color: #6b7280; - font-size: 0.875rem; -} - -.utility-links { - margin-top: 1rem; -} - -.utility-links h4 { - margin: 0 0 0.5rem 0; - font-size: 0.875rem; - font-weight: 600; - color: #374151; -} - -.utility-links ul { - margin: 0; - padding-left: 1.5rem; -} - -.utility-links li { - margin-bottom: 0.25rem; -} - -.utility-links a { - color: #3b82f6; - text-decoration: none; - font-size: 0.875rem; -} - -.utility-links a:hover { - text-decoration: underline; -} - -.utility-classes-grid { - display: grid; - gap: 1.5rem; -} - -.utility-preview-item { - border: 1px solid #e5e7eb; - border-radius: 0.5rem; - padding: 1rem; - background: #ffffff; -} - -.utility-preview-label { - display: flex; - align-items: center; - gap: 0.5rem; - margin-bottom: 0.75rem; - padding-bottom: 0.5rem; - border-bottom: 1px solid #f3f4f6; -} - -.utility-preview-label code { - background: #f3f4f6; - padding: 0.25rem 0.5rem; - border-radius: 0.25rem; - font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; - font-size: 0.75rem; - color: #dc2626; - font-weight: 600; -} - -.utility-preview-description { - color: #6b7280; - font-size: 0.875rem; - font-weight: 500; -} - -.utility-preview-demo { - min-height: 2rem; - display: flex; - align-items: center; - padding: 0.5rem; - background: #f9fafb; - border-radius: 0.25rem; -} - -/* Specific utility class demo styles */ -.utility-preview-demo .line-clamp-1, -.utility-preview-demo .line-clamp-2, -.utility-preview-demo .line-clamp-3, -.utility-preview-demo .line-clamp-4, -.utility-preview-demo .line-clamp-5 { - max-width: 200px; -} - -.utility-preview-demo .coverlink, -.utility-preview-demo .coverlink-over { - position: relative; - display: block; - padding: 0.5rem; - background: #3b82f6; - color: white; - text-decoration: none; - border-radius: 0.25rem; -} - -.utility-preview-demo .coverlink:hover, -.utility-preview-demo .coverlink-over:hover { - background: #2563eb; -} - -/* Responsive adjustments */ -@media (min-width: 768px) { - .utility-classes-grid { - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - } -}` -} // Find all *.ui_styles.yml files in the project const findUtilityStyleFiles = (rootDir: string): string[] => { @@ -380,7 +233,7 @@ export default (options: { yamlPath?: string; outputDir?: string } = {}) => ({ logger.info(`✅ Generated utility classes stories: ${storiesPath}`) if (!wroteCss) { - const cssContent = generateCSS() + const cssContent = generateUtilityClassesCSS() const cssPath = join(outputDir, 'utility-classes.css') writeFileSync(cssPath, cssContent, 'utf8') logger.info(`✅ Generated utility classes CSS: ${cssPath}`) diff --git a/stories/utility-classes.css b/stories/utility-classes.css index f453614..1f048aa 100644 --- a/stories/utility-classes.css +++ b/stories/utility-classes.css @@ -1,3 +1,4 @@ + /* Utility Classes Demo Styles */ .utility-classes-demo { max-width: 100%; @@ -127,4 +128,4 @@ .utility-classes-grid { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); } -} \ No newline at end of file +} diff --git a/stories/utility-classes.parent-namespace.stories.js b/stories/utility-classes.parent-namespace.stories.js index 5d54cfa..51e1504 100644 --- a/stories/utility-classes.parent-namespace.stories.js +++ b/stories/utility-classes.parent-namespace.stories.js @@ -39,7 +39,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -51,7 +51,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -63,7 +63,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -75,7 +75,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -87,7 +87,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -99,7 +99,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -111,7 +111,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -123,7 +123,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -135,7 +135,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -147,7 +147,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -159,7 +159,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -171,7 +171,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -183,7 +183,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -195,7 +195,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -207,7 +207,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -219,7 +219,7 @@ export const font_size = {
- Sample text to demonstrate typography + Demo
@@ -265,7 +265,7 @@ export const line_clamp = {
- This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines. + Demo
@@ -277,7 +277,7 @@ export const line_clamp = {
- This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines. + Demo
@@ -289,7 +289,7 @@ export const line_clamp = {
- This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines. + Demo
@@ -301,7 +301,7 @@ export const line_clamp = {
- This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines. + Demo
@@ -313,7 +313,7 @@ export const line_clamp = {
- This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines. + Demo
@@ -353,7 +353,7 @@ export const typography = {
- Sample text to demonstrate typography + Demo
@@ -399,7 +399,7 @@ export const font_family = {
- Demo content + Demo
@@ -411,7 +411,7 @@ export const font_family = {
- Demo content + Demo
diff --git a/stories/utility-classes.sdc-addon.stories.js b/stories/utility-classes.sdc-addon.stories.js index 88c0375..d35b668 100644 --- a/stories/utility-classes.sdc-addon.stories.js +++ b/stories/utility-classes.sdc-addon.stories.js @@ -39,7 +39,7 @@ export const rounded = {
- Rounded corners + Demo
@@ -51,7 +51,7 @@ export const rounded = {
- Rounded corners + Demo
@@ -63,7 +63,7 @@ export const rounded = {
- Rounded corners + Demo
@@ -75,7 +75,7 @@ export const rounded = {
- Rounded corners + Demo
@@ -87,7 +87,7 @@ export const rounded = {
- Rounded corners + Demo
@@ -99,7 +99,7 @@ export const rounded = {
- Rounded corners + Demo
@@ -111,7 +111,7 @@ export const rounded = {
- Rounded corners + Demo
@@ -123,7 +123,7 @@ export const rounded = {
- Rounded corners + Demo
@@ -135,7 +135,7 @@ export const rounded = {
- Rounded corners + Demo
@@ -175,7 +175,7 @@ export const margin_top = {
- Content with spacing + Demo
@@ -187,7 +187,7 @@ export const margin_top = {
- Content with spacing + Demo
@@ -199,7 +199,7 @@ export const margin_top = {
- Content with spacing + Demo
@@ -211,7 +211,7 @@ export const margin_top = {
- Content with spacing + Demo
@@ -223,7 +223,7 @@ export const margin_top = {
- Content with spacing + Demo
@@ -235,7 +235,7 @@ export const margin_top = {
- Content with spacing + Demo
@@ -275,7 +275,7 @@ export const margin_bottom = {
- Content with spacing + Demo
@@ -287,7 +287,7 @@ export const margin_bottom = {
- Content with spacing + Demo
@@ -299,7 +299,7 @@ export const margin_bottom = {
- Content with spacing + Demo
@@ -311,7 +311,7 @@ export const margin_bottom = {
- Content with spacing + Demo
@@ -323,7 +323,7 @@ export const margin_bottom = {
- Content with spacing + Demo
@@ -335,7 +335,7 @@ export const margin_bottom = {
- Content with spacing + Demo
@@ -375,7 +375,7 @@ export const gap = {
- Demo content + Demo
@@ -387,7 +387,7 @@ export const gap = {
- Demo content + Demo
@@ -399,7 +399,7 @@ export const gap = {
- Demo content + Demo
@@ -411,7 +411,7 @@ export const gap = {
- Demo content + Demo
@@ -423,7 +423,7 @@ export const gap = {
- Demo content + Demo
@@ -435,7 +435,7 @@ export const gap = {
- Demo content + Demo
@@ -475,7 +475,7 @@ export const border_width = {
- Border demo + Demo
@@ -487,7 +487,7 @@ export const border_width = {
- Border demo + Demo
@@ -499,7 +499,7 @@ export const border_width = {
- Border demo + Demo
@@ -511,7 +511,7 @@ export const border_width = {
- Border demo + Demo
@@ -523,7 +523,7 @@ export const border_width = {
- Border demo + Demo
@@ -563,7 +563,7 @@ export const border_style = {
- Border demo + Demo
@@ -575,7 +575,7 @@ export const border_style = {
- Border demo + Demo
@@ -587,7 +587,7 @@ export const border_style = {
- Border demo + Demo
@@ -599,7 +599,7 @@ export const border_style = {
- Border demo + Demo
From 0e76e02e5f34fdf4bd6e5efb184d3b3a0977f538 Mon Sep 17 00:00:00 2001 From: GitFred Date: Tue, 14 Oct 2025 14:29:12 +0200 Subject: [PATCH 09/12] factorisation --- UTILITY_CLASSES_GENERATOR.md | 76 +- scripts/generate-utility-stories.mjs | 355 ------- sdc-addon.ui_styles.yml | 5 + src/utilityClassesGenerator.ts | 225 ++--- src/vite-plugin-utility-classes.ts | 134 +-- stories/utility-classes.css | 106 +-- ...tility-classes.parent-namespace.stories.js | 564 ++++++----- stories/utility-classes.sdc-addon.stories.js | 882 +++++++++--------- 8 files changed, 866 insertions(+), 1481 deletions(-) delete mode 100644 scripts/generate-utility-stories.mjs diff --git a/UTILITY_CLASSES_GENERATOR.md b/UTILITY_CLASSES_GENERATOR.md index e51e7ea..2d1d6ce 100644 --- a/UTILITY_CLASSES_GENERATOR.md +++ b/UTILITY_CLASSES_GENERATOR.md @@ -9,10 +9,10 @@ The utility classes generator creates interactive Storybook stories that showcas ## Features - **Automatic Discovery**: Finds all `*.ui_styles.yml` files in your project automatically -- **Multi-file Support**: Merges utility classes from multiple YAML files +- **Individual File Processing**: Each YAML file generates its own namespace-specific stories - **Automatic Story Generation**: Parses YAML configuration and generates complete Storybook stories - **Interactive Previews**: Each utility class option is displayed with live previews -- **Organized Categories**: Stories are organized by category and label from the YAML +- **Organized Categories**: Stories are organized by namespace, category and label from the YAML - **Responsive Design**: Demo layouts adapt to different screen sizes - **Documentation Links**: Includes related links when specified in the YAML - **Disabled Class Support**: Automatically skips utility classes marked as `enabled: false` @@ -29,18 +29,14 @@ When you run: The system will: 1. Automatically find all `*.ui_styles.yml` files in your project -2. Parse and merge all utility class configurations -3. Generate `stories/utility-classes.stories.js` with all utility class stories -4. Generate `stories/utility-classes.css` with demo styling +2. Process each file individually and generate namespace-specific stories +3. Generate `stories/utility-classes.{namespace}.stories.js` for each YAML file +4. Generate `stories/utility-classes.css` with shared demo styling 5. Automatically include the stories in your Storybook ### Manual Generation (Optional) -If you need to manually regenerate the stories: - -```bash -npm run generate:utility-stories -``` +Manual generation is no longer needed as the system automatically generates stories during Storybook build. The generation is handled by the Vite plugin integrated into the Storybook preset. ### YAML Structure @@ -51,36 +47,37 @@ https://www.drupal.org/project/ui_styles Each utility class group becomes a Storybook story with: -- **Title**: `Utility Classes/{category}/{label}` +- **Title**: `{namespace}/Utility Classes/{category}/{label}` - **Description**: From the YAML `description` field - **Interactive Previews**: Each option with live demo - **Related Links**: External documentation links - **Responsive Layout**: Grid layout that adapts to screen size +The namespace is derived from the YAML file location (parent directory name or filename without extension). + ## Demo Content -The generator automatically creates appropriate demo content based on the utility type: +The generator creates demo content based on the YAML configuration: -- **Typography**: Sample text for font size, family, etc. -- **Spacing**: Content with spacing demonstrations -- **Borders**: Border width and style examples -- **Rounded**: Rounded corner demonstrations -- **Line Clamp**: Long text for truncation examples -- **Cover Links**: Interactive link examples +- **Custom Content**: If `preview_content` is specified in the YAML definition, it will be used +- **Generic Fallback**: If no custom content is provided, a generic "Demo" text is displayed +- **Data-driven**: No hardcoded utility type detection - all content comes from the YAML configuration ## Customization -### Adding New Utility Types +### Adding Custom Demo Content -To add support for new utility class types, modify the `generateDemoContent` function in `scripts/generate-utility-stories.mjs`: +To add custom demo content for specific utility classes, add a `preview_content` field to your YAML definition: -```javascript -const generateDemoContent = (groupKey, className) => { - if (groupKey.includes('your_new_type')) { - return 'Your custom demo content' - } - // ... existing conditions -} +```yaml +font_size: + category: typography + label: Font Size + description: Control text size + preview_content: "Sample text to demonstrate font size changes" + options: + text-sm: Small text + text-lg: Large text ``` ### Styling @@ -104,16 +101,15 @@ The generated stories integrate seamlessly with your existing Storybook setup: ## File Structure ``` -├── scripts/ -│ └── generate-utility-stories.mjs # Main generator script ├── src/ -│ └── utilityClassesGenerator.ts # TypeScript generator module +│ ├── utilityClassesGenerator.ts # TypeScript generator module +│ └── vite-plugin-utility-classes.ts # Vite plugin for automatic generation ├── stories/ -│ ├── utility-classes.stories.js # Generated stories -│ └── utility-classes.css # Generated demo styles -├── unami.ui_styles.yml # Source YAML configuration -├── theme.ui_styles.yml # Additional YAML configuration (optional) -└── any-name.ui_styles.yml # Any file with *.ui_styles.yml extension +│ ├── utility-classes.{namespace}.stories.js # Generated stories per namespace +│ └── utility-classes.css # Generated demo styles (shared) +├── unami.ui_styles.yml # Source YAML configuration +├── theme.ui_styles.yml # Additional YAML configuration (optional) +└── any-name.ui_styles.yml # Any file with *.ui_styles.yml extension ``` ## Development @@ -131,12 +127,12 @@ generateUtilityClassesFromYaml( ) ``` -### Script Options +### Vite Plugin Options -The generator script supports: +The Vite plugin supports: -- **YAML File Path**: Configurable source file location -- **Output Directory**: Configurable output location +- **Automatic Discovery**: Finds all `*.ui_styles.yml` files automatically +- **Namespace Derivation**: Creates separate stories per YAML file - **Error Handling**: Graceful error reporting - **Progress Logging**: Detailed generation progress @@ -162,7 +158,7 @@ console.log('Generated stories:', stories) When adding new features to the utility classes generator: 1. Update the TypeScript interfaces in `utilityClassesGenerator.ts` -2. Modify the generation logic in `generate-utility-stories.mjs` +2. Modify the generation logic in `vite-plugin-utility-classes.ts` 3. Update this documentation 4. Test with various YAML configurations diff --git a/scripts/generate-utility-stories.mjs b/scripts/generate-utility-stories.mjs deleted file mode 100644 index c726212..0000000 --- a/scripts/generate-utility-stories.mjs +++ /dev/null @@ -1,355 +0,0 @@ -#!/usr/bin/env node - -import { readFileSync, writeFileSync, existsSync } from 'fs' -import { join, dirname } from 'path' -import { fileURLToPath } from 'url' -import { globSync } from 'glob' -import yaml from 'yaml' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = dirname(__filename) - -// Helper function to capitalize strings -const capitalize = (str) => str[0].toUpperCase() + str.slice(1) - -// Helper function to convert snake_case to Title Case -const toTitleCase = (str) => str.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) - -// Find all *.ui_styles.yml files -const findUtilityStyleFiles = (rootDir) => { - try { - const pattern = join(rootDir, '**', '*.ui_styles.yml') - const files = globSync(pattern) - return files - } catch (error) { - console.warn(`Error searching for *.ui_styles.yml files: ${error}`) - return [] - } -} - -// Merge multiple YAML configurations -const mergeConfigurations = (configs) => { - const merged = {} - - configs.forEach((config, index) => { - Object.entries(config).forEach(([key, value]) => { - // If key already exists, append index to make it unique - const uniqueKey = merged[key] ? `${key}_${index}` : key - merged[uniqueKey] = value - }) - }) - - return merged -} - -// Paths -const rootDir = join(__dirname, '..') -const yamlFiles = findUtilityStyleFiles(rootDir) -const outputDir = join(__dirname, '../stories') - -console.log('🚀 Generating utility classes stories...') -console.log(`📁 Found ${yamlFiles.length} utility style file(s): ${yamlFiles.join(', ')}`) -console.log(`📁 Output directory: ${outputDir}`) - -if (yamlFiles.length === 0) { - console.warn('❌ No *.ui_styles.yml files found in project') - process.exit(1) -} - -try { - // Read and parse all YAML files - const configs = [] - const sourceFiles = [] - - yamlFiles.forEach(yamlFilePath => { - if (existsSync(yamlFilePath)) { - const fileContent = readFileSync(yamlFilePath, 'utf8') - const config = yaml.parse(fileContent) - configs.push(config) - sourceFiles.push(yamlFilePath) - console.log(`📄 Parsed utility classes from: ${yamlFilePath}`) - } - }) - - if (configs.length === 0) { - console.warn('❌ No valid utility style configurations found') - process.exit(1) - } - - // Merge all configurations - const config = mergeConfigurations(configs) - - // Generate demo content based on utility type - const generateDemoContent = (groupKey, className) => { - if (groupKey.includes('font_size') || groupKey.includes('typography')) { - return 'Sample text to demonstrate typography' - } - if (groupKey.includes('margin') || groupKey.includes('padding')) { - return 'Content with spacing' - } - if (groupKey.includes('border')) { - return 'Border demo' - } - if (groupKey.includes('rounded')) { - return 'Rounded corners' - } - if (groupKey.includes('line_clamp')) { - return 'This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines.' - } - if (groupKey.includes('coverlink')) { - return `Cover link demo` - } - return 'Demo content' - } - - // Generate preview HTML for each option - const generatePreviewHtml = (groupKey, className, label, previewedWith = []) => { - const previewClasses = previewedWith.join(' ') - const combinedClasses = `${className} ${previewClasses}`.trim() - const demoContent = generateDemoContent(groupKey, className) - - return ` -
-
- ${className} - ${label} -
-
-
- ${demoContent} -
-
-
` - } - - // Generate story for a utility class group - const generateStory = (groupKey, definition) => { - const { category, label, description, options, previewed_with = [], links = [] } = definition - - // Skip disabled utility classes - if (definition.enabled === false) { - return '' - } - - const storyKey = groupKey - - // Generate all option previews - const optionPreviews = Object.entries(options) - .map(([className, optionLabel]) => generatePreviewHtml(groupKey, className, optionLabel, previewed_with)) - .join('\n') - - // Generate links section if available - const linksSection = links.length > 0 - ? ` - ` - : '' - - return ` -export const ${storyKey} = { - title: 'Utility Classes/${category}/${label}', - parameters: { - docs: { - description: { - story: \`${description || `Utility classes for ${label.toLowerCase()}`}\` - } - } - }, - render: () => { - return \` -
-
-

${label}

- ${description ? `

${description}

` : ''} - ${linksSection} -
-
- ${optionPreviews} -
-
- \` - }, - play: async ({ canvasElement }) => { - Drupal.attachBehaviors(canvasElement, window.drupalSettings) - }, -}` - } - - // Generate all stories - const stories = Object.entries(config) - .map(([groupKey, definition]) => generateStory(groupKey, definition)) - .filter(Boolean) // Remove empty strings from disabled utilities - .join('\n\n') - - // Generate the complete stories file content - const sourceFilesList = sourceFiles.map(file => `// Generated from ${file}`).join('\n') - const storiesContent = `// Auto-generated utility classes stories -${sourceFilesList} - -${stories} - -// Default export for the story file -export default { - title: 'Utility Classes', - parameters: { - docs: { - description: { - story: 'Utility classes documentation generated from YAML configuration files' - } - } - } -}` - - // Generate CSS content - const cssContent = `/* Utility Classes Demo Styles */ -.utility-classes-demo { - max-width: 100%; - padding: 1rem; -} - -.utility-classes-header { - margin-bottom: 2rem; - padding-bottom: 1rem; - border-bottom: 1px solid #e5e7eb; -} - -.utility-classes-header h2 { - margin: 0 0 0.5rem 0; - color: #1f2937; -} - -.utility-classes-description { - margin: 0 0 1rem 0; - color: #6b7280; - font-size: 0.875rem; -} - -.utility-links { - margin-top: 1rem; -} - -.utility-links h4 { - margin: 0 0 0.5rem 0; - font-size: 0.875rem; - font-weight: 600; - color: #374151; -} - -.utility-links ul { - margin: 0; - padding-left: 1.5rem; -} - -.utility-links li { - margin-bottom: 0.25rem; -} - -.utility-links a { - color: #3b82f6; - text-decoration: none; - font-size: 0.875rem; -} - -.utility-links a:hover { - text-decoration: underline; -} - -.utility-classes-grid { - display: grid; - gap: 1.5rem; -} - -.utility-preview-item { - border: 1px solid #e5e7eb; - border-radius: 0.5rem; - padding: 1rem; - background: #ffffff; -} - -.utility-preview-label { - display: flex; - align-items: center; - gap: 0.5rem; - margin-bottom: 0.75rem; - padding-bottom: 0.5rem; - border-bottom: 1px solid #f3f4f6; -} - -.utility-preview-label code { - background: #f3f4f6; - padding: 0.25rem 0.5rem; - border-radius: 0.25rem; - font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; - font-size: 0.75rem; - color: #dc2626; - font-weight: 600; -} - -.utility-preview-description { - color: #6b7280; - font-size: 0.875rem; - font-weight: 500; -} - -.utility-preview-demo { - min-height: 2rem; - display: flex; - align-items: center; - padding: 0.5rem; - background: #f9fafb; - border-radius: 0.25rem; -} - -/* Specific utility class demo styles */ -.utility-preview-demo .line-clamp-1, -.utility-preview-demo .line-clamp-2, -.utility-preview-demo .line-clamp-3, -.utility-preview-demo .line-clamp-4, -.utility-preview-demo .line-clamp-5 { - max-width: 200px; -} - -.utility-preview-demo .coverlink, -.utility-preview-demo .coverlink-over { - position: relative; - display: block; - padding: 0.5rem; - background: #3b82f6; - color: white; - text-decoration: none; - border-radius: 0.25rem; -} - -.utility-preview-demo .coverlink:hover, -.utility-preview-demo .coverlink-over:hover { - background: #2563eb; -} - -/* Responsive adjustments */ -@media (min-width: 768px) { - .utility-classes-grid { - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - } -}` - - // Write the stories file - const storiesPath = join(outputDir, 'utility-classes.stories.js') - writeFileSync(storiesPath, storiesContent, 'utf8') - - // Write the CSS file - const cssPath = join(outputDir, 'utility-classes.css') - writeFileSync(cssPath, cssContent, 'utf8') - - console.log(`✅ Generated utility classes stories: ${storiesPath}`) - console.log(`✅ Generated utility classes CSS: ${cssPath}`) - console.log(`✅ Processed ${configs.length} utility style file(s)`) - console.log('✅ Utility classes stories generated successfully!') - -} catch (error) { - console.error('❌ Failed to generate utility classes stories:', error.message) - process.exit(1) -} diff --git a/sdc-addon.ui_styles.yml b/sdc-addon.ui_styles.yml index 3dc969d..fdede7e 100644 --- a/sdc-addon.ui_styles.yml +++ b/sdc-addon.ui_styles.yml @@ -21,6 +21,7 @@ rounded: margin_top: category: "Spacing" label: "Margin top" + description: "Margin top for different screen sizes" options: "mt-xs": xs "mt-xs md:mt-sm": sm @@ -35,6 +36,7 @@ margin_top: margin_bottom: category: "Spacing" label: "Margin bottom" + description: "Margin bottom for different screen sizes" options: "mb-xs": xs "mb-xs md:mb-sm": sm @@ -50,6 +52,7 @@ margin_start: enabled: false category: "Spacing" label: "Margin start" + description: "Margin start for different screen sizes" options: "ms-xs": xs "ms-xs md:ms-sm": sm @@ -65,6 +68,7 @@ margin_end: enabled: false category: "Spacing" label: "Margin end" + description: "Margin end for different screen sizes" options: "me-xs": xs "me-xs md:me-sm": sm @@ -78,6 +82,7 @@ margin_end: gap: category: "Spacing" label: "Gap" + description: "Gap for different screen sizes" options: "gap-between-xs": xs "gap-between-xs md:gap-between-sm": sm diff --git a/src/utilityClassesGenerator.ts b/src/utilityClassesGenerator.ts index 0551a8f..41f310a 100644 --- a/src/utilityClassesGenerator.ts +++ b/src/utilityClassesGenerator.ts @@ -30,12 +30,40 @@ export function parseUtilityClassesYaml(filePath: string): UtilityClassesConfig } /** - * Generates a story template for a utility class group + * Generate demo content without hardcoded group checks; allow YAML to override via preview_content */ -export function generateUtilityClassStory( - groupKey: string, - definition: UtilityClassDefinition -): string { +export function generateDemoContent(definition: any, className: string, label?: string): string { + if (typeof definition?.preview_content === 'string') { + return definition.preview_content + } + return label || 'Demo' +} + +/** + * Generate preview HTML for each option + */ +export function generatePreviewHtml(definition: any, className: string, label: string, previewedWith: string[] = []): string { + const previewClasses = previewedWith.join(' ') + const combinedClasses = `${className} ${previewClasses}`.trim() + const demoContent = generateDemoContent(definition, className, label) + + return ` +
+
+ ${className} +
+
+
+ ${demoContent} +
+
+
` +} + +/** + * Generates a story template for a utility class group (generic version) + */ +export function generateStory(namespace: string, groupKey: string, definition: any): string { const { category, label, description, options, previewed_with = [], links = [] } = definition // Skip disabled utility classes @@ -43,70 +71,25 @@ export function generateUtilityClassStory( return '' } - const storyName = capitalize(groupKey.replace(/_/g, ' ')) const storyKey = groupKey - - // Generate the preview HTML for each option - const generatePreviewHtml = (className: string, label: string): string => { - const previewClasses = previewed_with.join(' ') - const combinedClasses = `${className} ${previewClasses}`.trim() - - return ` -
-
- ${className} - ${label} -
-
-
- ${generateDemoContent(groupKey, className)} -
-
-
` - } - - // Generate demo content based on utility type - const generateDemoContent = (groupKey: string, className: string): string => { - if (groupKey.includes('font_size') || groupKey.includes('typography')) { - return 'Sample text to demonstrate typography' - } - if (groupKey.includes('margin') || groupKey.includes('padding')) { - return 'Content with spacing' - } - if (groupKey.includes('border')) { - return 'Border demo' - } - if (groupKey.includes('rounded')) { - return 'Rounded corners' - } - if (groupKey.includes('line_clamp')) { - return 'This is a long text that will be clamped to demonstrate the line clamp utility. It should show how the text gets truncated when it exceeds the specified number of lines.' - } - if (groupKey.includes('coverlink')) { - return 'Cover link demo' - } - return 'Demo content' - } // Generate all option previews const optionPreviews = Object.entries(options) - .map(([className, label]) => generatePreviewHtml(className, label)) + .map(([className, optionLabel]) => generatePreviewHtml(definition, className as string, optionLabel as string, previewed_with)) .join('\n') // Generate links section if available const linksSection = links.length > 0 ? ` - ` +

Related Links:

+
    + ${links.map((link: string) => `
  • ${link}
  • `).join('')} +
` : '' return ` export const ${storyKey} = { - title: 'Utility Classes/${category}/${label}', + title: '${namespace}/Utility Classes/${category}/${label}', parameters: { docs: { description: { @@ -116,13 +99,11 @@ export const ${storyKey} = { }, render: () => { return \` -
-
-

${label}

- ${description ? `

${description}

` : ''} - ${linksSection} -
-
+
+

${label}

+ ${description ? `

${description}

` : ''} + ${linksSection} +
${optionPreviews}
@@ -135,7 +116,17 @@ export const ${storyKey} = { } /** - * Generates the complete stories file content for utility classes + * Generates a story template for a utility class group (legacy version for backward compatibility) + */ +export function generateUtilityClassStory( + groupKey: string, + definition: UtilityClassDefinition +): string { + return generateStory('Utility Classes', groupKey, definition) +} + +/** + * Generates the complete stories file content for utility classes (legacy function) */ export function generateUtilityClassesStories(config: UtilityClassesConfig): string { const stories = Object.entries(config) @@ -166,136 +157,92 @@ export default { */ export function generateUtilityClassesCSS(): string { return ` -/* Utility Classes Demo Styles */ -.utility-classes-demo { - max-width: 100%; +/* Utility Classes Demo Styles - Using Storybook-compatible classes */ +.utility-demo { + /* Use Storybook's docs container styling */ padding: 1rem; } -.utility-classes-header { - margin-bottom: 2rem; - padding-bottom: 1rem; - border-bottom: 1px solid #e5e7eb; -} - -.utility-classes-header h2 { - margin: 0 0 0.5rem 0; - color: #1f2937; +.utility-demo h2 { + margin: 0 0 1rem 0; + border-bottom: 1px solid var(--color-border, #e5e7eb); + padding-bottom: 0.5rem; } -.utility-classes-description { +.utility-demo p { margin: 0 0 1rem 0; - color: #6b7280; + color: var(--color-text-secondary, #6b7280); font-size: 0.875rem; } -.utility-links { - margin-top: 1rem; -} - -.utility-links h4 { - margin: 0 0 0.5rem 0; +.utility-demo h4 { + margin: 1rem 0 0.5rem 0; font-size: 0.875rem; font-weight: 600; - color: #374151; + color: var(--color-text, #374151); } -.utility-links ul { +.utility-demo ul { margin: 0; padding-left: 1.5rem; } -.utility-links li { +.utility-demo li { margin-bottom: 0.25rem; } -.utility-links a { - color: #3b82f6; +.utility-demo a { + color: var(--color-primary, #3b82f6); text-decoration: none; font-size: 0.875rem; } -.utility-links a:hover { +.utility-demo a:hover { text-decoration: underline; } -.utility-classes-grid { +/* Use CSS Grid with CSS custom properties for better Storybook integration */ +.utility-grid { display: grid; gap: 1.5rem; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); } -.utility-preview-item { - border: 1px solid #e5e7eb; +.utility-item { + border: 1px solid var(--color-border, #e5e7eb); border-radius: 0.5rem; padding: 1rem; - background: #ffffff; + background: var(--color-background, #ffffff); } -.utility-preview-label { +.utility-label { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; padding-bottom: 0.5rem; - border-bottom: 1px solid #f3f4f6; + border-bottom: 1px solid var(--color-border-subtle, #f3f4f6); } -.utility-preview-label code { - background: #f3f4f6; +.utility-label code { + background: var(--color-background-subtle, #f3f4f6); padding: 0.25rem 0.5rem; border-radius: 0.25rem; - font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-family: var(--font-family-mono, 'Monaco', 'Menlo', 'Ubuntu Mono', monospace); font-size: 0.75rem; - color: #dc2626; + color: var(--color-error, #dc2626); font-weight: 600; } -.utility-preview-description { - color: #6b7280; - font-size: 0.875rem; - font-weight: 500; -} -.utility-preview-demo { +.utility-preview { min-height: 2rem; display: flex; align-items: center; padding: 0.5rem; - background: #f9fafb; + background: var(--color-background-subtle, #f9fafb); border-radius: 0.25rem; } - -/* Specific utility class demo styles */ -.utility-preview-demo .line-clamp-1, -.utility-preview-demo .line-clamp-2, -.utility-preview-demo .line-clamp-3, -.utility-preview-demo .line-clamp-4, -.utility-preview-demo .line-clamp-5 { - max-width: 200px; -} - -.utility-preview-demo .coverlink, -.utility-preview-demo .coverlink-over { - position: relative; - display: block; - padding: 0.5rem; - background: #3b82f6; - color: white; - text-decoration: none; - border-radius: 0.25rem; -} - -.utility-preview-demo .coverlink:hover, -.utility-preview-demo .coverlink-over:hover { - background: #2563eb; -} - -/* Responsive adjustments */ -@media (min-width: 768px) { - .utility-classes-grid { - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - } -} ` } diff --git a/src/vite-plugin-utility-classes.ts b/src/vite-plugin-utility-classes.ts index e8194f0..f42a04b 100644 --- a/src/vite-plugin-utility-classes.ts +++ b/src/vite-plugin-utility-classes.ts @@ -3,7 +3,7 @@ import { join, dirname, extname } from 'path' import { parse as parseYaml } from 'yaml' import { globSync } from 'glob' import { logger } from './logger.ts' -import { generateUtilityClassesCSS } from './utilityClassesGenerator.ts' +import { generateUtilityClassesCSS, generateStory } from './utilityClassesGenerator.ts' // Helper function to capitalize strings const capitalize = (str: string) => str[0].toUpperCase() + str.slice(1) @@ -11,90 +11,7 @@ const capitalize = (str: string) => str[0].toUpperCase() + str.slice(1) // Helper function to convert snake_case to Title Case const toTitleCase = (str: string) => str.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) -// Generate demo content without hardcoded group checks; allow YAML to override via preview_content -const generateDemoContent = (definition: any, className: string): string => { - if (typeof definition?.preview_content === 'string') { - return definition.preview_content - } - return 'Demo' -} - -// Generate preview HTML for each option -const generatePreviewHtml = (definition: any, className: string, label: string, previewedWith: string[] = []): string => { - const previewClasses = previewedWith.join(' ') - const combinedClasses = `${className} ${previewClasses}`.trim() - const demoContent = generateDemoContent(definition, className) - - return ` -
-
- ${className} - ${label} -
-
-
- ${demoContent} -
-
-
` -} - -// Generate story for a utility class group -const generateStory = (namespace: string, groupKey: string, definition: any): string => { - const { category, label, description, options, previewed_with = [], links = [] } = definition - - // Skip disabled utility classes - if (definition.enabled === false) { - return '' - } - - const storyKey = groupKey - - // Generate all option previews - const optionPreviews = Object.entries(options) - .map(([className, optionLabel]) => generatePreviewHtml(definition, className as string, optionLabel as string, previewed_with)) - .join('\n') - - // Generate links section if available - const linksSection = links.length > 0 - ? ` - ` - : '' - - return ` -export const ${storyKey} = { - title: '${namespace}/Utility Classes/${category}/${label}', - parameters: { - docs: { - description: { - story: \`${description || `Utility classes for ${label.toLowerCase()}`}\` - } - } - }, - render: () => { - return \` -
-
-

${label}

- ${description ? `

${description}

` : ''} - ${linksSection} -
-
- ${optionPreviews} -
-
- \` - }, - play: async ({ canvasElement }) => { - Drupal.attachBehaviors(canvasElement, window.drupalSettings) - }, -}` -} +// All story generation logic is now imported from utilityClassesGenerator.ts // Find all *.ui_styles.yml files in the project @@ -110,23 +27,6 @@ const findUtilityStyleFiles = (rootDir: string): string[] => { } } -// Merge multiple YAML configurations (kept for potential future use) -const mergeConfigurations = (configs: Record[]): Record => { - const merged: Record = {} - - configs.forEach((config, index) => { - Object.entries(config) - // Only include entries that look like utility definitions (must have options object) - .filter(([, value]) => isUtilityDefinition(value)) - .forEach(([key, value]) => { - // If key already exists, append index to make it unique - const uniqueKey = merged[key] ? `${key}_${index}` : key - merged[uniqueKey] = value - }) - }) - - return merged -} // Determine if a config entry is a utility definition const isUtilityDefinition = (value: any): boolean => { @@ -198,26 +98,31 @@ export default (options: { yamlPath?: string; outputDir?: string } = {}) => ({ let numGenerated = 0 let wroteCss = false + // Process each YAML file individually (no merging) yamlFiles.forEach(yamlPath => { if (!existsSync(yamlPath)) return + const fileContent = readFileSync(yamlPath, 'utf8') const config = parseYaml(fileContent) as Record // Respect disable flags in the config if (isConfigDisabled(config)) { logger.info(`Skipping utility classes generation for disabled file: ${yamlPath}`) - // Also remove a previously generated file for this namespace, if exists + // Remove previously generated file for this namespace, if exists const ns = deriveNamespaceFromPath(rootDir, yamlPath) const perFileStoriesPath = join(outputDir, `utility-classes.${ns}.stories.js`) try { if (existsSync(perFileStoriesPath)) unlinkSync(perFileStoriesPath) } catch {} return } - // Extract only utility definition entries + // Extract only utility definition entries from this file const utilities = Object.entries(config) .filter(([, value]) => isUtilityDefinition(value)) - if (utilities.length === 0) return + if (utilities.length === 0) { + logger.info(`No utility definitions found in ${yamlPath}`) + return + } const namespace = deriveNamespaceFromPath(rootDir, yamlPath) const stories = utilities @@ -225,13 +130,28 @@ export default (options: { yamlPath?: string; outputDir?: string } = {}) => ({ .filter(Boolean) .join('\n\n') - const storiesContent = `// Auto-generated utility classes stories\n// Generated from ${yamlPath}\n\nexport default {\n title: '${namespace}/Utility Classes',\n parameters: {\n docs: {\n description: {\n story: 'Utility classes documentation generated from ${yamlPath}'\n }\n }\n }\n}\n\n${stories}` + const storiesContent = `// Auto-generated utility classes stories +// Generated from ${yamlPath} + +export default { + title: '${namespace}/Utility Classes', + parameters: { + docs: { + description: { + story: 'Utility classes documentation generated from ${yamlPath}' + } + } + } +} + +${stories}` const storiesPath = join(outputDir, `utility-classes.${namespace}.stories.js`) writeFileSync(storiesPath, storiesContent, 'utf8') numGenerated += 1 logger.info(`✅ Generated utility classes stories: ${storiesPath}`) + // Generate CSS only once (shared across all namespaces) if (!wroteCss) { const cssContent = generateUtilityClassesCSS() const cssPath = join(outputDir, 'utility-classes.css') @@ -247,7 +167,7 @@ export default (options: { yamlPath?: string; outputDir?: string } = {}) => ({ const cssPath = join(outputDir, 'utility-classes.css') try { if (existsSync(cssPath)) unlinkSync(cssPath) } catch {} } else { - logger.info(`✅ Processed ${numGenerated} utility style file(s)`) + logger.info(`✅ Processed ${numGenerated} utility style file(s) individually`) } } catch (error) { diff --git a/stories/utility-classes.css b/stories/utility-classes.css index 1f048aa..41a1ad7 100644 --- a/stories/utility-classes.css +++ b/stories/utility-classes.css @@ -1,131 +1,87 @@ -/* Utility Classes Demo Styles */ -.utility-classes-demo { - max-width: 100%; +/* Utility Classes Demo Styles - Using Storybook-compatible classes */ +.utility-demo { + /* Use Storybook's docs container styling */ padding: 1rem; } -.utility-classes-header { - margin-bottom: 2rem; - padding-bottom: 1rem; - border-bottom: 1px solid #e5e7eb; -} - -.utility-classes-header h2 { - margin: 0 0 0.5rem 0; - color: #1f2937; +.utility-demo h2 { + margin: 0 0 1rem 0; + border-bottom: 1px solid var(--color-border, #e5e7eb); + padding-bottom: 0.5rem; } -.utility-classes-description { +.utility-demo p { margin: 0 0 1rem 0; - color: #6b7280; + color: var(--color-text-secondary, #6b7280); font-size: 0.875rem; } -.utility-links { - margin-top: 1rem; -} - -.utility-links h4 { - margin: 0 0 0.5rem 0; +.utility-demo h4 { + margin: 1rem 0 0.5rem 0; font-size: 0.875rem; font-weight: 600; - color: #374151; + color: var(--color-text, #374151); } -.utility-links ul { +.utility-demo ul { margin: 0; padding-left: 1.5rem; } -.utility-links li { +.utility-demo li { margin-bottom: 0.25rem; } -.utility-links a { - color: #3b82f6; +.utility-demo a { + color: var(--color-primary, #3b82f6); text-decoration: none; font-size: 0.875rem; } -.utility-links a:hover { +.utility-demo a:hover { text-decoration: underline; } -.utility-classes-grid { +/* Use CSS Grid with CSS custom properties for better Storybook integration */ +.utility-grid { display: grid; gap: 1.5rem; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); } -.utility-preview-item { - border: 1px solid #e5e7eb; +.utility-item { + border: 1px solid var(--color-border, #e5e7eb); border-radius: 0.5rem; padding: 1rem; - background: #ffffff; + background: var(--color-background, #ffffff); } -.utility-preview-label { +.utility-label { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; padding-bottom: 0.5rem; - border-bottom: 1px solid #f3f4f6; + border-bottom: 1px solid var(--color-border-subtle, #f3f4f6); } -.utility-preview-label code { - background: #f3f4f6; +.utility-label code { + background: var(--color-background-subtle, #f3f4f6); padding: 0.25rem 0.5rem; border-radius: 0.25rem; - font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-family: var(--font-family-mono, 'Monaco', 'Menlo', 'Ubuntu Mono', monospace); font-size: 0.75rem; - color: #dc2626; + color: var(--color-error, #dc2626); font-weight: 600; } -.utility-preview-description { - color: #6b7280; - font-size: 0.875rem; - font-weight: 500; -} -.utility-preview-demo { +.utility-preview { min-height: 2rem; display: flex; align-items: center; padding: 0.5rem; - background: #f9fafb; + background: var(--color-background-subtle, #f9fafb); border-radius: 0.25rem; } - -/* Specific utility class demo styles */ -.utility-preview-demo .line-clamp-1, -.utility-preview-demo .line-clamp-2, -.utility-preview-demo .line-clamp-3, -.utility-preview-demo .line-clamp-4, -.utility-preview-demo .line-clamp-5 { - max-width: 200px; -} - -.utility-preview-demo .coverlink, -.utility-preview-demo .coverlink-over { - position: relative; - display: block; - padding: 0.5rem; - background: #3b82f6; - color: white; - text-decoration: none; - border-radius: 0.25rem; -} - -.utility-preview-demo .coverlink:hover, -.utility-preview-demo .coverlink-over:hover { - background: #2563eb; -} - -/* Responsive adjustments */ -@media (min-width: 768px) { - .utility-classes-grid { - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - } -} diff --git a/stories/utility-classes.parent-namespace.stories.js b/stories/utility-classes.parent-namespace.stories.js index 51e1504..a4e385c 100644 --- a/stories/utility-classes.parent-namespace.stories.js +++ b/stories/utility-classes.parent-namespace.stories.js @@ -24,205 +24,187 @@ export const font_size = { }, render: () => { return ` -
-
-

Font size

-

We use specifics size classes (with custom tokens) for a responsive typography display

+
+

Font size

+

We use specifics size classes (with custom tokens) for a responsive typography display

+ +
+
+
+ title-2xl +
+
+
+ Title 2XL
-
- -
-
- title-2xl - Title 2XL -
-
-
- Demo -
-
-
+
+
-
-
- title-xl - Title XL -
-
-
- Demo -
-
-
+
+
+ title-xl +
+
+
+ Title XL +
+
+
-
-
- title-lg - Title LG -
-
-
- Demo -
-
-
+
+
+ title-lg +
+
+
+ Title LG +
+
+
-
-
- title-md - Title MD -
-
-
- Demo -
-
-
+
+
+ title-md +
+
+
+ Title MD +
+
+
-
-
- title-sm - Title SM -
-
-
- Demo -
-
-
+
+
+ title-sm +
+
+
+ Title SM +
+
+
-
-
- title-xs - Title XS -
-
-
- Demo -
-
-
+
+
+ title-xs +
+
+
+ Title XS +
+
+
-
-
- body-xl - Body XL -
-
-
- Demo -
-
-
+
+
+ body-xl +
+
+
+ Body XL +
+
+
-
-
- body-lg - Body LG -
-
-
- Demo -
-
-
+
+
+ body-lg +
+
+
+ Body LG +
+
+
-
-
- body-md - Body MD -
-
-
- Demo -
-
-
+
+
+ body-md +
+
+
+ Body MD +
+
+
-
-
- body-sm - Body SM -
-
-
- Demo -
-
-
+
+
+ body-sm +
+
+
+ Body SM +
+
+
-
-
- body-xs - Body XS -
-
-
- Demo -
-
-
+
+
+ body-xs +
+
+
+ Body XS +
+
+
-
-
- label-xl - Label XL -
-
-
- Demo -
-
-
+
+
+ label-xl +
+
+
+ Label XL +
+
+
-
-
- label-lg - Label LG -
-
-
- Demo -
-
-
+
+
+ label-lg +
+
+
+ Label LG +
+
+
-
-
- label-md - Label MD -
-
-
- Demo -
-
-
+
+
+ label-md +
+
+
+ Label MD +
+
+
-
-
- label-sm - Label SM -
-
-
- Demo -
-
-
+
+
+ label-sm +
+
+
+ Label SM +
+
+
-
-
- label-xs - Label XS -
-
-
- Demo -
-
-
+
+
+ label-xs +
+
+
+ Label XS +
+
+
` @@ -244,79 +226,70 @@ export const line_clamp = { }, render: () => { return ` -
-
-

Line Clamp

-

Utilities for clamping text to a specific number of lines.

+
+

Line Clamp

+

Utilities for clamping text to a specific number of lines.

+ +

Related Links:

+ +
- +
+
+ line-clamp-1 +
+
+
+ line-clamp-1
-
- -
-
- line-clamp-1 - line-clamp-1 -
-
-
- Demo -
-
-
+
+
-
-
- line-clamp-2 - line-clamp-2 -
-
-
- Demo -
-
-
+
+
+ line-clamp-2 +
+
+
+ line-clamp-2 +
+
+
-
-
- line-clamp-3 - line-clamp-3 -
-
-
- Demo -
-
-
+
+
+ line-clamp-3 +
+
+
+ line-clamp-3 +
+
+
-
-
- line-clamp-4 - line-clamp-4 -
-
-
- Demo -
-
-
+
+
+ line-clamp-4 +
+
+
+ line-clamp-4 +
+
+
-
-
- line-clamp-5 - line-clamp-5 -
-
-
- Demo -
-
-
+
+
+ line-clamp-5 +
+
+
+ line-clamp-5 +
+
+
` @@ -338,25 +311,22 @@ export const typography = { }, render: () => { return ` -
-
-

Typography

- +
+

Typography

+ + +
+
+
+ prose +
+
+
+ Formatted Text (prose)
-
- -
-
- prose - Formatted Text (prose) -
-
-
- Demo -
-
-
+
+
` @@ -378,43 +348,37 @@ export const font_family = { }, render: () => { return ` -
-
-

Font family

-

You can control the typeface of text using the font family utilities.

+
+

Font family

+

You can control the typeface of text using the font family utilities.

+ +

Related Links:

+ +
- +
+
+ font-mono +
+
+
+ Mono
-
- -
-
- font-mono - Mono -
-
-
- Demo -
-
-
+
+
-
-
- font-sans - Sans -
-
-
- Demo -
-
-
+
+
+ font-sans +
+
+
+ Sans +
+
+
` diff --git a/stories/utility-classes.sdc-addon.stories.js b/stories/utility-classes.sdc-addon.stories.js index d35b668..829ad9e 100644 --- a/stories/utility-classes.sdc-addon.stories.js +++ b/stories/utility-classes.sdc-addon.stories.js @@ -24,121 +24,110 @@ export const rounded = { }, render: () => { return ` -
-
-

Rounded

- +
+

Rounded

+ + +
+
+
+ rounded-sm +
+
+
+ sm
-
- -
-
- rounded-sm - sm -
-
-
- Demo -
-
-
- -
-
- rounded - default -
-
-
- Demo -
-
-
- -
-
- rounded-md - md -
-
-
- Demo -
-
-
- -
-
- rounded-lg - lg -
-
-
- Demo -
-
-
- -
-
- rounded-xl - xl -
-
-
- Demo -
-
-
- -
-
- rounded-2xl - 2xl -
-
-
- Demo -
-
-
- -
-
- rounded-3xl - 3xl -
-
-
- Demo -
-
-
- -
-
- rounded-none - none -
-
-
- Demo -
-
-
- -
-
- rounded-tr-3xl rounded-bl-3xl - theme -
-
-
- Demo -
-
-
+
+
+ +
+
+ rounded +
+
+
+ default +
+
+
+ +
+
+ rounded-md +
+
+
+ md +
+
+
+ +
+
+ rounded-lg +
+
+
+ lg +
+
+
+ +
+
+ rounded-xl +
+
+
+ xl +
+
+
+ +
+
+ rounded-2xl +
+
+
+ 2xl +
+
+
+ +
+
+ rounded-3xl +
+
+
+ 3xl +
+
+
+ +
+
+ rounded-none +
+
+
+ none +
+
+
+ +
+
+ rounded-tr-3xl rounded-bl-3xl +
+
+
+ theme +
+
+
` @@ -154,91 +143,83 @@ export const margin_top = { parameters: { docs: { description: { - story: `Utility classes for margin top` + story: `Margin top for different screen sizes` } } }, render: () => { return ` -
-
-

Margin top

- +
+

Margin top

+

Margin top for different screen sizes

+ +
+
+
+ mt-xs +
+
+
+ xs
-
- -
-
- mt-xs - xs -
-
-
- Demo -
-
-
- -
-
- mt-xs md:mt-sm - sm -
-
-
- Demo -
-
-
- -
-
- mt-xs md:mt-md - md -
-
-
- Demo -
-
-
- -
-
- mt-xs md:mt-md lg:mt-lg - lg -
-
-
- Demo -
-
-
- -
-
- mt-xs md:mt-md lg:mt-xl - xl -
-
-
- Demo -
-
-
- -
-
- mt-xs md:mt-md lg:mt-2xl - 2xl -
-
-
- Demo -
-
-
+
+
+ +
+
+ mt-xs md:mt-sm +
+
+
+ sm +
+
+
+ +
+
+ mt-xs md:mt-md +
+
+
+ md +
+
+
+ +
+
+ mt-xs md:mt-md lg:mt-lg +
+
+
+ lg +
+
+
+ +
+
+ mt-xs md:mt-md lg:mt-xl +
+
+
+ xl +
+
+
+ +
+
+ mt-xs md:mt-md lg:mt-2xl +
+
+
+ 2xl +
+
+
` @@ -254,91 +235,83 @@ export const margin_bottom = { parameters: { docs: { description: { - story: `Utility classes for margin bottom` + story: `Margin bottom for different screen sizes` } } }, render: () => { return ` -
-
-

Margin bottom

- +
+

Margin bottom

+

Margin bottom for different screen sizes

+ +
+
+
+ mb-xs +
+
+
+ xs
-
- -
-
- mb-xs - xs -
-
-
- Demo -
-
-
- -
-
- mb-xs md:mb-sm - sm -
-
-
- Demo -
-
-
- -
-
- mb-xs md:mb-md - md -
-
-
- Demo -
-
-
- -
-
- mb-xs md:mb-md lg:mb-lg - lg -
-
-
- Demo -
-
-
- -
-
- mb-xs md:mb-md lg:mb-xl - xl -
-
-
- Demo -
-
-
- -
-
- mb-xs md:mb-md lg:mb-2xl - 2xl -
-
-
- Demo -
-
-
+
+
+ +
+
+ mb-xs md:mb-sm +
+
+
+ sm +
+
+
+ +
+
+ mb-xs md:mb-md +
+
+
+ md +
+
+
+ +
+
+ mb-xs md:mb-md lg:mb-lg +
+
+
+ lg +
+
+
+ +
+
+ mb-xs md:mb-md lg:mb-xl +
+
+
+ xl +
+
+
+ +
+
+ mb-xs md:mb-md lg:mb-2xl +
+
+
+ 2xl +
+
+
` @@ -354,91 +327,83 @@ export const gap = { parameters: { docs: { description: { - story: `Utility classes for gap` + story: `Gap for different screen sizes` } } }, render: () => { return ` -
-
-

Gap

- +
+

Gap

+

Gap for different screen sizes

+ +
+
+
+ gap-between-xs +
+
+
+ xs
-
- -
-
- gap-between-xs - xs -
-
-
- Demo -
-
-
- -
-
- gap-between-xs md:gap-between-sm - sm -
-
-
- Demo -
-
-
- -
-
- gap-between-xs md:gap-between-md - md -
-
-
- Demo -
-
-
- -
-
- gap-between-xs md:gap-between-md gap-between-lg - lg -
-
-
- Demo -
-
-
- -
-
- gap-between-xs md:gap-between-md gap-between-xl - xl -
-
-
- Demo -
-
-
- -
-
- gap-between-xs md:gap-between-md lg:gap-between-2xl - 2xl -
-
-
- Demo -
-
-
+
+
+ +
+
+ gap-between-xs md:gap-between-sm +
+
+
+ sm +
+
+
+ +
+
+ gap-between-xs md:gap-between-md +
+
+
+ md +
+
+
+ +
+
+ gap-between-xs md:gap-between-md gap-between-lg +
+
+
+ lg +
+
+
+ +
+
+ gap-between-xs md:gap-between-md gap-between-xl +
+
+
+ xl +
+
+
+ +
+
+ gap-between-xs md:gap-between-md lg:gap-between-2xl +
+
+
+ 2xl +
+
+
` @@ -460,73 +425,66 @@ export const border_width = { }, render: () => { return ` -
-
-

Border width

- +
+

Border width

+ + +
+
+
+ border-0 +
+
+
+ none
-
- -
-
- border-0 - none -
-
-
- Demo -
-
-
- -
-
- border - 1px -
-
-
- Demo -
-
-
- -
-
- border-2 - 2px -
-
-
- Demo -
-
-
- -
-
- border-4 - 4px -
-
-
- Demo -
-
-
- -
-
- border-8 - 8px -
-
-
- Demo -
-
-
+
+
+ +
+
+ border +
+
+
+ 1px +
+
+
+ +
+
+ border-2 +
+
+
+ 2px +
+
+
+ +
+
+ border-4 +
+
+
+ 4px +
+
+
+ +
+
+ border-8 +
+
+
+ 8px +
+
+
` @@ -548,61 +506,55 @@ export const border_style = { }, render: () => { return ` -
-
-

Border style

- +
+

Border style

+ + +
+
+
+ border-solid +
+
+
+ solid
-
- -
-
- border-solid - solid -
-
-
- Demo -
-
-
- -
-
- border-dashed - dashed -
-
-
- Demo -
-
-
- -
-
- border-dotted - dotted -
-
-
- Demo -
-
-
- -
-
- border-double - double -
-
-
- Demo -
-
-
+
+
+ +
+
+ border-dashed +
+
+
+ dashed +
+
+
+ +
+
+ border-dotted +
+
+
+ dotted +
+
+
+ +
+
+ border-double +
+
+
+ double +
+
+
` From b1c29f88e341adec22fc38a983ac0f9d526fd293 Mon Sep 17 00:00:00 2001 From: GitFred Date: Tue, 14 Oct 2025 16:17:48 +0200 Subject: [PATCH 10/12] add docs feature --- sdc-addon.ui_styles.yml | 77 +--- src/utilityClassesGenerator.ts | 80 ++++ src/vite-plugin-utility-classes.ts | 55 ++- stories/utility-classes.sdc-addon.stories.js | 364 ++----------------- 4 files changed, 139 insertions(+), 437 deletions(-) diff --git a/sdc-addon.ui_styles.yml b/sdc-addon.ui_styles.yml index fdede7e..c36b199 100644 --- a/sdc-addon.ui_styles.yml +++ b/sdc-addon.ui_styles.yml @@ -18,81 +18,6 @@ rounded: previewed_with: - inline-flex items-center justify-center text-center w-[200px] h-[200px] bg-secondary text-white m-4 -margin_top: - category: "Spacing" - label: "Margin top" - description: "Margin top for different screen sizes" - options: - "mt-xs": xs - "mt-xs md:mt-sm": sm - "mt-xs md:mt-md": md - "mt-xs md:mt-md lg:mt-lg": lg - "mt-xs md:mt-md lg:mt-xl": xl - "mt-xs md:mt-md lg:mt-2xl": 2xl - - previewed_with: - - bg-neutral-content - -margin_bottom: - category: "Spacing" - label: "Margin bottom" - description: "Margin bottom for different screen sizes" - options: - "mb-xs": xs - "mb-xs md:mb-sm": sm - "mb-xs md:mb-md": md - "mb-xs md:mb-md lg:mb-lg": lg - "mb-xs md:mb-md lg:mb-xl": xl - "mb-xs md:mb-md lg:mb-2xl": 2xl - - previewed_with: - - bg-neutral-content - -margin_start: - enabled: false - category: "Spacing" - label: "Margin start" - description: "Margin start for different screen sizes" - options: - "ms-xs": xs - "ms-xs md:ms-sm": sm - "ms-xs md:ms-md": md - "ms-xs md:ms-md lg:ms-lg": lg - "ms-xs md:ms-md lg:ms-xl": xl - "ms-xs md:ms-md lg:ms-2xl": 2xl - - previewed_with: - - bg-neutral-content - -margin_end: - enabled: false - category: "Spacing" - label: "Margin end" - description: "Margin end for different screen sizes" - options: - "me-xs": xs - "me-xs md:me-sm": sm - "me-xs md:me-md": md - "me-xs md:me-md lg:me-lg": lg - "me-xs md:me-md lg:me-xl": xl - "me-xs md:me-md lg:me-2xl": 2xl - previewed_with: - - bg-neutral-content - -gap: - category: "Spacing" - label: "Gap" - description: "Gap for different screen sizes" - options: - "gap-between-xs": xs - "gap-between-xs md:gap-between-sm": sm - "gap-between-xs md:gap-between-md": md - "gap-between-xs md:gap-between-md gap-between-lg": lg - "gap-between-xs md:gap-between-md gap-between-xl": xl - "gap-between-xs md:gap-between-md lg:gap-between-2xl": 2xl - previewed_with: - - bg-neutral-content - border_width: category: "Borders" label: "Border width" @@ -116,4 +41,6 @@ border_style: thirdPartySettings: sdcStorybook: + tags: + - autodocs disableUtilityClasses: false \ No newline at end of file diff --git a/src/utilityClassesGenerator.ts b/src/utilityClassesGenerator.ts index 41f310a..d462500 100644 --- a/src/utilityClassesGenerator.ts +++ b/src/utilityClassesGenerator.ts @@ -152,6 +152,86 @@ export default { }` } +/** + * Generate base story structure (common to both autodocs and individual stories) + */ +export function generateBaseStoryStructure(comment: string, additionalContent: string, yamlPath: string, namespace: string): string { + return [ + comment, + `// Generated from ${yamlPath}`, + '', + 'export default {', + ` title: '${namespace}/Utility Classes',`, + ' parameters: {', + ' docs: {', + ' description: {', + ` story: 'Utility classes documentation generated from ${yamlPath}'`, + ' }', + ' }', + ' }', + '}', + '', + additionalContent, + ].join('\n') +} + +/** + * Generate autodocs content for all utilities + */ +export function generateAutodocsContent(utilities: [string, any][]): string { + const allUtilityPreviews = utilities + .map(([groupKey, definition]) => { + const { category, label, description, options, previewed_with = [], links = [] } = definition + + if (definition.enabled === false) return '' + + const optionPreviews = Object.entries(options) + .map(([className, optionLabel]) => generatePreviewHtml(definition, className as string, optionLabel as string, previewed_with)) + .join('\n') + + const linksSection = links.length > 0 + ? ` +

Related Links:

+
    + ${links.map((link: string) => `
  • ${link}
  • `).join('')} +
` + : '' + + return ` +
+

${label}

+ ${description ? `

${description}

` : ''} + ${linksSection} +
+ ${optionPreviews} +
+
` + }) + .filter(Boolean) + .join('\n\n') + + return [ + 'export const Docs = {', + ` tags: ['autodocs'],`, + ' render: () => {', + ' return `', + allUtilityPreviews, + ' `', + ' }', + '}', + ].join('\n') +} + +/** + * Generate individual stories content + */ +export function generateIndividualStoriesContent(utilities: [string, any][], namespace: string): string { + return utilities + .map(([groupKey, definition]) => generateStory(namespace, groupKey as string, definition)) + .filter(Boolean) + .join('\n\n') +} + /** * Generates CSS styles for the utility classes demo */ diff --git a/src/vite-plugin-utility-classes.ts b/src/vite-plugin-utility-classes.ts index f42a04b..b32a0b0 100644 --- a/src/vite-plugin-utility-classes.ts +++ b/src/vite-plugin-utility-classes.ts @@ -3,7 +3,14 @@ import { join, dirname, extname } from 'path' import { parse as parseYaml } from 'yaml' import { globSync } from 'glob' import { logger } from './logger.ts' -import { generateUtilityClassesCSS, generateStory } from './utilityClassesGenerator.ts' +import { + generateUtilityClassesCSS, + generateStory, + generatePreviewHtml, + generateBaseStoryStructure, + generateAutodocsContent, + generateIndividualStoriesContent +} from './utilityClassesGenerator.ts' // Helper function to capitalize strings const capitalize = (str: string) => str[0].toUpperCase() + str.slice(1) @@ -125,26 +132,34 @@ export default (options: { yamlPath?: string; outputDir?: string } = {}) => ({ } const namespace = deriveNamespaceFromPath(rootDir, yamlPath) - const stories = utilities - .map(([groupKey, definition]) => generateStory(namespace, groupKey as string, definition)) - .filter(Boolean) - .join('\n\n') - - const storiesContent = `// Auto-generated utility classes stories -// Generated from ${yamlPath} - -export default { - title: '${namespace}/Utility Classes', - parameters: { - docs: { - description: { - story: 'Utility classes documentation generated from ${yamlPath}' - } - } - } -} -${stories}` + // Global autodocs tags from thirdPartySettings per YAML file + const tps = (config as any)?.thirdPartySettings || {} + const sdcStorybook = tps?.sdcStorybook || {} + const globalTags: string[] = Array.isArray(sdcStorybook?.tags) ? sdcStorybook.tags : [] + const hasAutodocs = globalTags.includes('autodocs') + + let storiesContent: string + + if (hasAutodocs) { + // Generate simple docs definition without individual stories + const docsContent = generateAutodocsContent(utilities) + storiesContent = generateBaseStoryStructure( + '// Auto-generated utility classes documentation', + docsContent, + yamlPath, + namespace + ) + } else { + // Generate individual stories (original behavior) + const stories = generateIndividualStoriesContent(utilities, namespace) + storiesContent = generateBaseStoryStructure( + '// Auto-generated utility classes stories', + stories, + yamlPath, + namespace + ) + } const storiesPath = join(outputDir, `utility-classes.${namespace}.stories.js`) writeFileSync(storiesPath, storiesContent, 'utf8') diff --git a/stories/utility-classes.sdc-addon.stories.js b/stories/utility-classes.sdc-addon.stories.js index 829ad9e..df935a3 100644 --- a/stories/utility-classes.sdc-addon.stories.js +++ b/stories/utility-classes.sdc-addon.stories.js @@ -1,4 +1,4 @@ -// Auto-generated utility classes stories +// Auto-generated utility classes documentation // Generated from /Users/fred/Local/sdc-addon/sdc-addon.ui_styles.yml export default { @@ -12,24 +12,17 @@ export default { } } - -export const rounded = { - title: 'sdc-addon/Utility Classes/Rounded/Rounded', - parameters: { - docs: { - description: { - story: `Utility classes for rounded` - } - } - }, +export const Docs = { + tags: ['autodocs'], render: () => { return ` -
-

Rounded

- - -
+ +
+

Rounded

+ +
+
rounded-sm @@ -128,309 +121,16 @@ export const rounded = {
+
-
- ` - }, - play: async ({ canvasElement }) => { - Drupal.attachBehaviors(canvasElement, window.drupalSettings) - }, -} - - -export const margin_top = { - title: 'sdc-addon/Utility Classes/Spacing/Margin top', - parameters: { - docs: { - description: { - story: `Margin top for different screen sizes` - } - } - }, - render: () => { - return ` -
-

Margin top

-

Margin top for different screen sizes

- -
- -
-
- mt-xs -
-
-
- xs -
-
-
- -
-
- mt-xs md:mt-sm -
-
-
- sm -
-
-
- -
-
- mt-xs md:mt-md -
-
-
- md -
-
-
- -
-
- mt-xs md:mt-md lg:mt-lg -
-
-
- lg -
-
-
- -
-
- mt-xs md:mt-md lg:mt-xl -
-
-
- xl -
-
-
- -
-
- mt-xs md:mt-md lg:mt-2xl -
-
-
- 2xl -
-
-
-
-
- ` - }, - play: async ({ canvasElement }) => { - Drupal.attachBehaviors(canvasElement, window.drupalSettings) - }, -} - - -export const margin_bottom = { - title: 'sdc-addon/Utility Classes/Spacing/Margin bottom', - parameters: { - docs: { - description: { - story: `Margin bottom for different screen sizes` - } - } - }, - render: () => { - return ` -
-

Margin bottom

-

Margin bottom for different screen sizes

- -
- -
-
- mb-xs -
-
-
- xs -
-
-
- -
-
- mb-xs md:mb-sm -
-
-
- sm -
-
-
- -
-
- mb-xs md:mb-md -
-
-
- md -
-
-
- -
-
- mb-xs md:mb-md lg:mb-lg -
-
-
- lg -
-
-
- -
-
- mb-xs md:mb-md lg:mb-xl -
-
-
- xl -
-
-
- -
-
- mb-xs md:mb-md lg:mb-2xl -
-
-
- 2xl -
-
-
-
-
- ` - }, - play: async ({ canvasElement }) => { - Drupal.attachBehaviors(canvasElement, window.drupalSettings) - }, -} -export const gap = { - title: 'sdc-addon/Utility Classes/Spacing/Gap', - parameters: { - docs: { - description: { - story: `Gap for different screen sizes` - } - } - }, - render: () => { - return ` -
-

Gap

-

Gap for different screen sizes

- -
+
+

Border width

-
-
- gap-between-xs -
-
-
- xs -
-
-
- -
-
- gap-between-xs md:gap-between-sm -
-
-
- sm -
-
-
- -
-
- gap-between-xs md:gap-between-md -
-
-
- md -
-
-
- -
-
- gap-between-xs md:gap-between-md gap-between-lg -
-
-
- lg -
-
-
- -
-
- gap-between-xs md:gap-between-md gap-between-xl -
-
-
- xl -
-
-
- -
-
- gap-between-xs md:gap-between-md lg:gap-between-2xl -
-
-
- 2xl -
-
-
-
-
- ` - }, - play: async ({ canvasElement }) => { - Drupal.attachBehaviors(canvasElement, window.drupalSettings) - }, -} - - -export const border_width = { - title: 'sdc-addon/Utility Classes/Borders/Border width', - parameters: { - docs: { - description: { - story: `Utility classes for border width` - } - } - }, - render: () => { - return ` -
-

Border width

- - -
+
+
border-0 @@ -485,33 +185,16 @@ export const border_width = {
+
-
- ` - }, - play: async ({ canvasElement }) => { - Drupal.attachBehaviors(canvasElement, window.drupalSettings) - }, -} -export const border_style = { - title: 'sdc-addon/Utility Classes/Borders/Border style', - parameters: { - docs: { - description: { - story: `Utility classes for border style` - } - } - }, - render: () => { - return ` -
-

Border style

- - -
+
+

Border style

+ +
+
border-solid @@ -555,11 +238,8 @@ export const border_style = {
+
-
` - }, - play: async ({ canvasElement }) => { - Drupal.attachBehaviors(canvasElement, window.drupalSettings) - }, + } } \ No newline at end of file From 420fe6245652c318bd13d30fc4bf15226f2f90e3 Mon Sep 17 00:00:00 2001 From: GitFred Date: Fri, 28 Nov 2025 16:59:54 +0100 Subject: [PATCH 11/12] Rename vitePluginUtilityClasses to vitePluginUiStyles and utilityClassesIndexer to uiStylesIndexer --- src/preset.ts | 6 +- ui-skins-storybook/.gitignore | 12 + ui-skins-storybook/README.md | 86 + ui-skins-storybook/package-lock.json | 3133 +++++++++++++++++ ui-skins-storybook/package.json | 55 + ui-skins-storybook/src/dataThemesGenerator.ts | 142 + ui-skins-storybook/src/index.ts | 6 + ui-skins-storybook/src/logger.ts | 15 + .../src/vite-plugin-theme-generator.ts | 68 + ui-skins-storybook/tsconfig.json | 20 + ui-skins-storybook/tsup.config.ts | 22 + ui-styles-storybook | 1 + 12 files changed, 3563 insertions(+), 3 deletions(-) create mode 100644 ui-skins-storybook/.gitignore create mode 100644 ui-skins-storybook/README.md create mode 100644 ui-skins-storybook/package-lock.json create mode 100644 ui-skins-storybook/package.json create mode 100644 ui-skins-storybook/src/dataThemesGenerator.ts create mode 100644 ui-skins-storybook/src/index.ts create mode 100644 ui-skins-storybook/src/logger.ts create mode 100644 ui-skins-storybook/src/vite-plugin-theme-generator.ts create mode 100644 ui-skins-storybook/tsconfig.json create mode 100644 ui-skins-storybook/tsup.config.ts create mode 160000 ui-styles-storybook diff --git a/src/preset.ts b/src/preset.ts index 615f321..863a316 100644 --- a/src/preset.ts +++ b/src/preset.ts @@ -1,7 +1,7 @@ import YamlStoriesPlugin, { yamlStoriesIndexer, } from './vite-plugin-storybook-yaml-stories.ts' -import UtilityClassesPlugin from './vite-plugin-utility-classes.ts' +import { vitePluginUiStyles, uiStylesIndexer } from '@fredboulanger/ui-styles-storybook/main' import { mergeConfig } from 'vite' import type { UserConfig } from 'vite' import type { Indexer } from 'storybook/internal/types' @@ -54,7 +54,7 @@ export async function viteFinal(config: UserConfig, options: SDCAddonOptions) { }), ...(twigPlugin ? [twigPlugin] : []), YamlStoriesPlugin({ ...options, globalDefs, namespaces }), - UtilityClassesPlugin(), + vitePluginUiStyles({ namespaces: sdcStorybookOptions.namespaces }), ], optimizeDeps: { exclude: ['vite-plugin-twig-drupal', 'vite-plugin-twing-drupal'], @@ -70,6 +70,7 @@ export const experimental_indexers: StorybookConfig['experimental_indexers'] = async (existingIndexers: Indexer[] | undefined) => [ ...(existingIndexers || []), yamlStoriesIndexer, + uiStylesIndexer, ] // Optional: Add the previewHead @@ -84,7 +85,6 @@ export const previewHead: StorybookConfig['previewHead'] = (head: string) => ` word-wrap: normal; } - diff --git a/ui-skins-storybook/.gitignore b/ui-skins-storybook/.gitignore new file mode 100644 index 0000000..05b024e --- /dev/null +++ b/ui-skins-storybook/.gitignore @@ -0,0 +1,12 @@ +node_modules/ +dist/ +*.log +.DS_Store +.env +.env.local +.env.development.local +.env.test.local +.env.production.local +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/ui-skins-storybook/README.md b/ui-skins-storybook/README.md new file mode 100644 index 0000000..6a5e5cd --- /dev/null +++ b/ui-skins-storybook/README.md @@ -0,0 +1,86 @@ +# UI Skins Storybook + +Generate Storybook ui-skins configuration from `*.ui_skins.themes.yml` files. + +## Features + +- Automatically generates Storybook theme configuration from YAML theme files +- Provides Vite plugin for seamless integration +- Supports theme targeting (html, body, or custom selectors) +- Hot reload support for theme file changes + +## Installation + +```bash +npm install @fredboulanger/ui-skins-storybook +``` + +## Usage + +### Vite Plugin + +Add the plugin to your Storybook configuration: + +```typescript +// .storybook/main.ts +import { viteFinal } from '@storybook/html-vite' +import { vitePluginThemeGenerator } from '@fredboulanger/ui-skins-storybook' + +export default { + // ... other config + viteFinal: async (config) => { + return viteFinal(config, { + plugins: [ + vitePluginThemeGenerator(), + // ... other plugins + ] + }) + } +} +``` + +### Manual Generation + +```typescript +import { generateThemes } from '@fredboulanger/ui-skins-storybook' + +// Generate themes from *.ui_skins.themes.yml files +await generateThemes() +``` + +## Theme File Format + +Create `*.ui_skins.themes.yml` files in your project: + +```yaml +# Example: themes.ui_skins.themes.yml +cyberpunk: + label: "Cyberpunk" + label_context: "color" + key: "data-theme" + target: body + +forest: + label: "Forest" + label_context: "color" + key: "data-theme" + target: html + +garden: + label: "Garden" + label_context: "color" + key: "data-theme" + target: html +``` + +## Generated Output + +The plugin generates a `.storybook/data-themes.ts` file with: + +- Theme decorators for applying data-theme attributes +- Global types for Storybook toolbar +- Theme target mapping + +## License + +MIT diff --git a/ui-skins-storybook/package-lock.json b/ui-skins-storybook/package-lock.json new file mode 100644 index 0000000..2560f5a --- /dev/null +++ b/ui-skins-storybook/package-lock.json @@ -0,0 +1,3133 @@ +{ + "name": "@fredboulanger/data-themes-storybook", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@fredboulanger/data-themes-storybook", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "yaml": "^2.0.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsup": "^8.0.0", + "typescript": "^5.0.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "storybook": "^8.0.0 || ^9.0.0", + "vite": "^5.0.0" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "license": "MIT", + "peer": true + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", + "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", + "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", + "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", + "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", + "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", + "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", + "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", + "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", + "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", + "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", + "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", + "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", + "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", + "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", + "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", + "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", + "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", + "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", + "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", + "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", + "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", + "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", + "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", + "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", + "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", + "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", + "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", + "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", + "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", + "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", + "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", + "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", + "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", + "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", + "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", + "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", + "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", + "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", + "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", + "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", + "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", + "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", + "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", + "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", + "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", + "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", + "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", + "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@storybook/global": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", + "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "license": "MIT", + "peer": true + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.23.tgz", + "integrity": "sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "license": "MIT", + "peer": true, + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "license": "MIT", + "peer": true, + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/better-opn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", + "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "open": "^8.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "license": "MIT", + "peer": true, + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "license": "MIT", + "peer": true + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "license": "MIT", + "peer": true + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", + "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.11", + "@esbuild/android-arm": "0.25.11", + "@esbuild/android-arm64": "0.25.11", + "@esbuild/android-x64": "0.25.11", + "@esbuild/darwin-arm64": "0.25.11", + "@esbuild/darwin-x64": "0.25.11", + "@esbuild/freebsd-arm64": "0.25.11", + "@esbuild/freebsd-x64": "0.25.11", + "@esbuild/linux-arm": "0.25.11", + "@esbuild/linux-arm64": "0.25.11", + "@esbuild/linux-ia32": "0.25.11", + "@esbuild/linux-loong64": "0.25.11", + "@esbuild/linux-mips64el": "0.25.11", + "@esbuild/linux-ppc64": "0.25.11", + "@esbuild/linux-riscv64": "0.25.11", + "@esbuild/linux-s390x": "0.25.11", + "@esbuild/linux-x64": "0.25.11", + "@esbuild/netbsd-arm64": "0.25.11", + "@esbuild/netbsd-x64": "0.25.11", + "@esbuild/openbsd-arm64": "0.25.11", + "@esbuild/openbsd-x64": "0.25.11", + "@esbuild/openharmony-arm64": "0.25.11", + "@esbuild/sunos-x64": "0.25.11", + "@esbuild/win32-arm64": "0.25.11", + "@esbuild/win32-ia32": "0.25.11", + "@esbuild/win32-x64": "0.25.11" + } + }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "peer": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT", + "peer": true + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "license": "MIT", + "peer": true + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT", + "peer": true + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/recast": { + "version": "0.23.11", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", + "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "license": "MIT", + "peer": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", + "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.5", + "@rollup/rollup-android-arm64": "4.52.5", + "@rollup/rollup-darwin-arm64": "4.52.5", + "@rollup/rollup-darwin-x64": "4.52.5", + "@rollup/rollup-freebsd-arm64": "4.52.5", + "@rollup/rollup-freebsd-x64": "4.52.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", + "@rollup/rollup-linux-arm64-gnu": "4.52.5", + "@rollup/rollup-linux-arm64-musl": "4.52.5", + "@rollup/rollup-linux-loong64-gnu": "4.52.5", + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-musl": "4.52.5", + "@rollup/rollup-linux-s390x-gnu": "4.52.5", + "@rollup/rollup-linux-x64-gnu": "4.52.5", + "@rollup/rollup-linux-x64-musl": "4.52.5", + "@rollup/rollup-openharmony-arm64": "4.52.5", + "@rollup/rollup-win32-arm64-msvc": "4.52.5", + "@rollup/rollup-win32-ia32-msvc": "4.52.5", + "@rollup/rollup-win32-x64-gnu": "4.52.5", + "@rollup/rollup-win32-x64-msvc": "4.52.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/storybook": { + "version": "9.1.15", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-9.1.15.tgz", + "integrity": "sha512-es7uDdEwRVVUAt7XLAZZ1hicOq9r4ov5NFeFPpa2YEyAsyHYOCr0CTlHBfslWG6D5EVNWK3kVIIuW8GHB6hEig==", + "license": "MIT", + "peer": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/user-event": "^14.6.1", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/spy": "3.2.4", + "better-opn": "^3.0.2", + "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0", + "esbuild-register": "^3.5.0", + "recast": "^0.23.5", + "semver": "^7.6.2", + "ws": "^8.18.0" + }, + "bin": { + "storybook": "bin/index.cjs" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "prettier": "^2 || ^3" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT", + "peer": true + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "peer": true + }, + "node_modules/tsup": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.0.tgz", + "integrity": "sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.25.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "0.8.0-beta.0", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tsup/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "deprecated": "The work that was done in this beta branch won't be included in future versions", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + } + } +} diff --git a/ui-skins-storybook/package.json b/ui-skins-storybook/package.json new file mode 100644 index 0000000..13f1b7e --- /dev/null +++ b/ui-skins-storybook/package.json @@ -0,0 +1,55 @@ +{ + "name": "@fredboulanger/ui-skins-storybook", + "version": "0.1.0", + "description": "Generate Storybook ui-skins configuration from *.ui_skins.themes.yml files", + "main": "dist/index.mjs", + "module": "dist/index.mjs", + "types": "dist/index.d.mts", + "files": [ + "dist", + "README.md" + ], + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "prepublishOnly": "npm run build" + }, + "keywords": [ + "storybook", + "ui-skins", + "themes", + "yaml", + "components", + "drupal", + "sdc" + ], + "author": "fredboulanger", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Fred-Boulanger/sdc-addon" + }, + "bugs": { + "url": "https://github.com/Fred-Boulanger/sdc-addon/issues" + }, + "homepage": "https://github.com/Fred-Boulanger/sdc-addon#readme", + "dependencies": { + "glob": "^10.0.0", + "yaml": "^2.0.0" + }, + "peerDependencies": { + "storybook": "^8.0.0 || ^9.0.0", + "vite": "^5.0.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsup": "^8.0.0", + "typescript": "^5.0.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/ui-skins-storybook/src/dataThemesGenerator.ts b/ui-skins-storybook/src/dataThemesGenerator.ts new file mode 100644 index 0000000..f49ac79 --- /dev/null +++ b/ui-skins-storybook/src/dataThemesGenerator.ts @@ -0,0 +1,142 @@ +import { readFile, writeFile } from 'node:fs/promises' +import { glob } from 'glob' +import { parse } from 'yaml' +import { join } from 'node:path' + +export interface ThemeConfig { + key: string + label: string + value: string + target: string +} + +/** + * Generate Storybook data-themes.ts configuration from *.ui_skins.themes.yml files + */ +export async function generateThemes(): Promise { + console.log('🎨 Generating theme configuration from *.ui_skins.themes.yml files...') + + try { + // Find all theme files + const themeFiles = await glob('**/*.ui_skins.themes.yml', { cwd: process.cwd() }) + + if (themeFiles.length === 0) { + console.log('âš ī¸ No *.ui_skins.themes.yml files found') + return + } + + console.log(`📁 Found ${themeFiles.length} theme file(s):`, themeFiles) + + const themes: ThemeConfig[] = [] + + // Parse each theme file + for (const themeFile of themeFiles) { + try { + const content = await readFile(themeFile, 'utf8') + const themeData = parse(content) + + // Extract theme information + for (const [themeKey, themeConfig] of Object.entries(themeData)) { + if (typeof themeConfig === 'object' && themeConfig !== null) { + themes.push({ + key: themeKey, + label: (themeConfig as any).label || themeKey, + value: themeKey, + target: (themeConfig as any).target || 'body' + }) + } + } + + console.log(`✅ Parsed theme file: ${themeFile}`) + } catch (error) { + console.error(`❌ Error parsing ${themeFile}:`, (error as Error).message) + } + } + + if (themes.length === 0) { + console.log('âš ī¸ No themes found in the files') + return + } + + console.log(`đŸŽ¯ Found ${themes.length} theme(s):`, themes.map(t => t.key)) + + // Generate the themes configuration file + const themesConfigContent = generateThemesConfig(themes) + const themesConfigPath = join(process.cwd(), '.storybook', 'data-themes.ts') + await writeFile(themesConfigPath, themesConfigContent, 'utf8') + + console.log(`✅ Generated data-themes.ts with ${themes.length} theme(s)`) + console.log(`📝 Updated: ${themesConfigPath}`) + console.log(`💡 To use themes, add to your preview.ts:`) + console.log(` import { themeDecorators, themeGlobalTypes } from './data-themes'`) + console.log(` Then add ...themeDecorators to decorators and ...themeGlobalTypes to globalTypes`) + + } catch (error) { + console.error('❌ Error generating themes:', error) + throw error + } +} + +/** + * Generate the themes configuration file + */ +export function generateThemesConfig(themes: ThemeConfig[]): string { + const themeItems = themes.map(theme => + ` { value: '${theme.value}', title: '${theme.label}' }` + ).join(',\n') + + const defaultTheme = themes.length > 0 ? themes[0].value : 'default' + const defaultTarget = themes.length > 0 ? themes[0].target : 'body' + + // Create theme target mapping + const themeTargets = themes.map(theme => + ` '${theme.value}': '${theme.target}'` + ).join(',\n') + + return `// Auto-generated theme configuration from *.ui_skins.themes.yml files +// This file is automatically generated - do not edit manually + +// Theme target mapping +const themeTargets = { +${themeTargets} +}; + +// Add a decorator to set data-theme on the correct target element +export const themeDecorators = [ + (storyFn, context) => { + // Wait for the target element to be available + setTimeout(() => { + const selectedTheme = context.globals.theme || '${defaultTheme}'; + const targetSelector = themeTargets[selectedTheme] || '${defaultTarget}'; + + // Remove data-theme from both html and body to clean previous themes + const html = document.querySelector('html'); + const body = document.querySelector('body'); + if (html) html.removeAttribute('data-theme'); + if (body) body.removeAttribute('data-theme'); + + // Apply the new theme to the correct target + const target = document.querySelector(targetSelector); + if (target) { + target.setAttribute('data-theme', selectedTheme); + } + }, 0); + return storyFn(); + }, +]; + +export const themeGlobalTypes = { + theme: { + name: 'Theme', + description: 'Global theme for components', + defaultValue: '${defaultTheme}', + toolbar: { + icon: 'paintbrush', + items: [ +${themeItems} + ], + }, + }, +}; +` +} diff --git a/ui-skins-storybook/src/index.ts b/ui-skins-storybook/src/index.ts new file mode 100644 index 0000000..a42153b --- /dev/null +++ b/ui-skins-storybook/src/index.ts @@ -0,0 +1,6 @@ +// Main exports for data-themes-storybook package +export { generateThemes, generateThemesConfig } from './dataThemesGenerator.js' +export type { ThemeConfig } from './dataThemesGenerator.js' +export { default as vitePluginThemeGenerator } from './vite-plugin-theme-generator.js' +export type { ThemeGeneratorOptions } from './vite-plugin-theme-generator.js' +export { logger } from './logger.js' diff --git a/ui-skins-storybook/src/logger.ts b/ui-skins-storybook/src/logger.ts new file mode 100644 index 0000000..86500c1 --- /dev/null +++ b/ui-skins-storybook/src/logger.ts @@ -0,0 +1,15 @@ +// Simple logger implementation for data-themes-storybook package +export const logger = { + info: (message: string, ...args: any[]) => { + console.log(`â„šī¸ ${message}`, ...args) + }, + warn: (message: string, ...args: any[]) => { + console.warn(`âš ī¸ ${message}`, ...args) + }, + error: (message: string, ...args: any[]) => { + console.error(`❌ ${message}`, ...args) + }, + debug: (message: string, ...args: any[]) => { + console.debug(`🐛 ${message}`, ...args) + } +} diff --git a/ui-skins-storybook/src/vite-plugin-theme-generator.ts b/ui-skins-storybook/src/vite-plugin-theme-generator.ts new file mode 100644 index 0000000..5a35668 --- /dev/null +++ b/ui-skins-storybook/src/vite-plugin-theme-generator.ts @@ -0,0 +1,68 @@ +import type { Plugin } from 'vite' +import { generateThemes } from './dataThemesGenerator.js' +import { logger } from './logger.js' + +export interface ThemeGeneratorOptions { + /** + * Whether to generate themes on build start + * @default true + */ + generateOnStart?: boolean + + /** + * Whether to watch theme files for changes + * @default true + */ + watch?: boolean +} + +/** + * Vite plugin for automatic theme generation from *.ui_skins.themes.yml files + */ +export default function vitePluginThemeGenerator(options: ThemeGeneratorOptions = {}): Plugin { + const { + generateOnStart = true, + watch = true + } = options + + let hasGenerated = false + + return { + name: 'vite-plugin-theme-generator', + + async buildStart() { + if (generateOnStart && !hasGenerated) { + try { + await generateThemes() + hasGenerated = true + } catch (error) { + logger.warn('Failed to generate themes on build start:', error) + } + } + }, + + async load(id: string) { + // Generate themes when loading any component.yml file + if (id.endsWith('component.yml') && !hasGenerated) { + try { + await generateThemes() + hasGenerated = true + } catch (error) { + logger.warn('Failed to generate themes:', error) + } + } + }, + + async handleHotUpdate({ file }) { + // Regenerate themes when theme files change + if (watch && file.endsWith('.ui_skins.themes.yml')) { + try { + await generateThemes() + logger.info('🎨 Themes regenerated due to file change:', file) + } catch (error) { + logger.warn('Failed to regenerate themes:', error) + } + } + } + } +} diff --git a/ui-skins-storybook/tsconfig.json b/ui-skins-storybook/tsconfig.json new file mode 100644 index 0000000..4d7ee95 --- /dev/null +++ b/ui-skins-storybook/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "allowJs": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/ui-skins-storybook/tsup.config.ts b/ui-skins-storybook/tsup.config.ts new file mode 100644 index 0000000..5d24e21 --- /dev/null +++ b/ui-skins-storybook/tsup.config.ts @@ -0,0 +1,22 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm'], + dts: true, + clean: true, + sourcemap: true, + external: [ + 'storybook', + 'vite', + 'glob', + 'yaml', + 'node:fs/promises', + 'node:path' + ], + esbuildOptions(options) { + options.banner = { + js: '"use client"' + } + } +}) diff --git a/ui-styles-storybook b/ui-styles-storybook new file mode 160000 index 0000000..4e81f67 --- /dev/null +++ b/ui-styles-storybook @@ -0,0 +1 @@ +Subproject commit 4e81f6787a59466d55cfa7efdb0d9ed904280762 From 06021e42d7bbf82cc6c084958a0ab907aa237d04 Mon Sep 17 00:00:00 2001 From: GitFred Date: Mon, 22 Dec 2025 11:59:27 +0100 Subject: [PATCH 12/12] Replace malakoff_humanis and ui_suite_cbd references with theme_1 and theme_2 --- parent-namespace/theme_1.ui_skins.themes.yml | 140 +++++++++++++++++ ui-skins-storybook/README.md | 157 ++++++++++++++++--- 2 files changed, 279 insertions(+), 18 deletions(-) create mode 100644 parent-namespace/theme_1.ui_skins.themes.yml diff --git a/parent-namespace/theme_1.ui_skins.themes.yml b/parent-namespace/theme_1.ui_skins.themes.yml new file mode 100644 index 0000000..d0a2354 --- /dev/null +++ b/parent-namespace/theme_1.ui_skins.themes.yml @@ -0,0 +1,140 @@ +# No value because same as plugin ID. +theme_1: + label: "Theme 1" + label_context: "color" + key: "data-theme" + target: html + + + +# No value because same as plugin ID. +cupcake: + enabled: false + +# No value because same as plugin ID. +aqua: + enabled: false + +# No value because same as plugin ID. +black: + enabled: false + +# No value because same as plugin ID. +bumblebee: + enabled: false + +# No value because same as plugin ID. +cmyk: + enabled: false + +# No value because same as plugin ID. +corporate: + enabled: false + +# No value because same as plugin ID. +cyberpunk: + enabled: false + +# No value because same as plugin ID. +dracula: + enabled: false + +# No value because same as plugin ID. +emerald: + enabled: false + +# No value because same as plugin ID. +fantasy: + enabled: false + +# No value because same as plugin ID. +forest: + enabled: false + +# No value because same as plugin ID. +garden: + enabled: false + +# No value because same as plugin ID. +halloween: + enabled: false + +# No value because same as plugin ID. +lofi: + enabled: false + +# No value because same as plugin ID. +luxury: + enabled: false + +# No value because same as plugin ID. +pastel: + enabled: false + +# No value because same as plugin ID. +retro: + enabled: false + +# No value because same as plugin ID. +synthwave: + enabled: false + +# No value because same as plugin ID. +valentine: + enabled: false + +# No value because same as plugin ID. +wireframe: + enabled: false + +# No value because same as plugin ID. +autumn: + enabled: false + +# No value because same as plugin ID. +business: + enabled: false + +# No value because same as plugin ID. +acid: + enabled: false + +# No value because same as plugin ID. +lemonade: + enabled: false + +# No value because same as plugin ID. +night: + enabled: false + +# No value because same as plugin ID. +coffee: + enabled: false + +# No value because same as plugin ID. +winter: + enabled: false + +# No value because same as plugin ID. +dim: + enabled: false + +# No value because same as plugin ID. +nord: + enabled: false + +# No value because same as plugin ID. +sunset: + enabled: false + +# No value because same as plugin ID. +caramellatte: + enabled: false + +# No value because same as plugin ID. +abyss: + enabled: false + +# No value because same as plugin ID. +silk: + enabled: false diff --git a/ui-skins-storybook/README.md b/ui-skins-storybook/README.md index 6a5e5cd..ec23344 100644 --- a/ui-skins-storybook/README.md +++ b/ui-skins-storybook/README.md @@ -23,56 +23,87 @@ Add the plugin to your Storybook configuration: ```typescript // .storybook/main.ts -import { viteFinal } from '@storybook/html-vite' -import { vitePluginThemeGenerator } from '@fredboulanger/ui-skins-storybook' +import { vitePluginThemeGenerator } from '@fredboulanger/ui-skins-storybook/main' export default { // ... other config - viteFinal: async (config) => { - return viteFinal(config, { - plugins: [ - vitePluginThemeGenerator(), - // ... other plugins - ] - }) - } + viteFinal: (config) => ({ + ...config, + plugins: [...(config.plugins || []), vitePluginThemeGenerator()], + }), } ``` -### Manual Generation +Or for JavaScript configuration: + +```javascript +// .storybook/main.js +import { vitePluginThemeGenerator } from '@fredboulanger/ui-skins-storybook/main' + +export default { + // ... other config + viteFinal: (config) => ({ + ...config, + plugins: [...(config.plugins || []), vitePluginThemeGenerator()], + }), +} +``` + +### Using with Namespaces + +If you're using namespaces in your Storybook configuration, you can pass them to the plugin to search for theme files in those directories: ```typescript -import { generateThemes } from '@fredboulanger/ui-skins-storybook' +// .storybook/main.ts +import { vitePluginThemeGenerator } from '@fredboulanger/ui-skins-storybook/main' +import { resolve } from 'path' -// Generate themes from *.ui_skins.themes.yml files -await generateThemes() +export default { + // ... other config + viteFinal: (config) => ({ + ...config, + plugins: [...(config.plugins || []), vitePluginThemeGenerator({ + namespaces: { + 'theme_1': resolve('../../../themes/custom/theme_1'), + 'theme_2': resolve('../../../themes/custom/theme_2'), + } + })], + }), +} ``` +The plugin will automatically search for `*.ui_skins.themes.yml` files in: +- The current working directory +- All namespace directories you specify + ## Theme File Format -Create `*.ui_skins.themes.yml` files in your project: +Create `*.ui_skins.themes.yml` files in your project. Each theme **must** have the following required properties: + +- `label`: Display name for the theme in Storybook toolbar +- `key`: The data-theme attribute value (usually matches the theme name) +- `target`: CSS selector where the data-theme attribute will be applied ```yaml # Example: themes.ui_skins.themes.yml cyberpunk: label: "Cyberpunk" - label_context: "color" key: "data-theme" target: body forest: label: "Forest" - label_context: "color" key: "data-theme" target: html garden: label: "Garden" - label_context: "color" key: "data-theme" target: html ``` +**Note**: Themes without all three required properties (`label`, `key`, `target`) will be skipped during generation. + ## Generated Output The plugin generates a `.storybook/data-themes.ts` file with: @@ -81,6 +112,96 @@ The plugin generates a `.storybook/data-themes.ts` file with: - Global types for Storybook toolbar - Theme target mapping +## Integration with Storybook Preview + +To use the generated themes in your Storybook, you need to integrate the `data-themes.ts` file into your `.storybook/preview.ts` configuration: + +### 1. Import the Generated Theme Configuration + +```typescript +// .storybook/preview.ts +import type { Preview } from '@storybook/html' +import { themeDecorators, themeGlobalTypes } from './data-themes' + +export const decorators = [ + ...themeDecorators, // Theme decorators + // Your other decorators +] + +export const globalTypes = { + ...themeGlobalTypes, // Theme global types + // Your other global types +} + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + }, +} + +export default preview +``` + +### 2. What the Integration Provides + +- **Theme Toolbar**: A paintbrush icon in the Storybook toolbar to switch between themes +- **Automatic Theme Application**: Themes are automatically applied to the correct target element (html, body, or custom selector) +- **Theme Cleanup**: Previous themes are properly removed before applying new ones +- **Global Theme State**: Theme selection persists across story navigation + +### 3. Generated File Structure + +The `data-themes.ts` file exports: + +- `themeDecorators`: Array of decorators that apply theme attributes +- `themeGlobalTypes`: Global types configuration for the Storybook toolbar + +### 4. Target Elements + +Themes can be applied to different target elements: + +- `body`: Applied to the `` element +- `html`: Applied to the `` element +- Custom selector: Any valid CSS selector (e.g., `.theme-container`, `#app`) + +### 5. Complete Example + +Here's a complete example of a `.storybook/preview.ts` file: + +```typescript +import type { Preview } from '@storybook/html' +import { themeDecorators, themeGlobalTypes } from './data-themes' + +export const decorators = [ + ...themeDecorators, + // Add other decorators as needed +] + +export const globalTypes = { + ...themeGlobalTypes, + // Add other global types as needed +} + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + // Add other parameters as needed + }, +} + +export default preview +``` + ## License MIT