@cyberalien/svg-utils 1.1.4 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/lib/components/export/merge.js +3 -1
  2. package/lib/components/helpers/content/stringify.d.ts +2 -2
  3. package/lib/components/helpers/content/stringify.js +4 -10
  4. package/lib/components/helpers/css/generate.d.ts +2 -1
  5. package/lib/components/helpers/css/generate.js +43 -46
  6. package/lib/components/helpers/filenames/css.d.ts +1 -1
  7. package/lib/components/helpers/filenames/css.js +2 -3
  8. package/lib/components/helpers/functions/custom.d.ts +14 -0
  9. package/lib/components/helpers/functions/custom.js +17 -0
  10. package/lib/components/helpers/functions/fallback.d.ts +8 -0
  11. package/lib/components/helpers/functions/fallback.js +53 -0
  12. package/lib/components/helpers/functions/innerhtml.d.ts +8 -0
  13. package/lib/components/helpers/functions/innerhtml.js +35 -0
  14. package/lib/components/helpers/functions/size.js +1 -1
  15. package/lib/components/helpers/imports/create.js +1 -2
  16. package/lib/components/helpers/imports/stringify.js +0 -2
  17. package/lib/components/helpers/imports/types.d.ts +0 -1
  18. package/lib/components/jsx.js +79 -8
  19. package/lib/components/prepare/iconify.d.ts +1 -1
  20. package/lib/components/prepare/iconify.js +6 -4
  21. package/lib/components/prepare/states.d.ts +8 -0
  22. package/lib/components/prepare/states.js +73 -0
  23. package/lib/components/raw.js +10 -7
  24. package/lib/components/svelte.js +73 -14
  25. package/lib/components/types/component.d.ts +2 -1
  26. package/lib/components/types/css.d.ts +1 -2
  27. package/lib/components/types/data.d.ts +0 -3
  28. package/lib/components/types/options.d.ts +9 -2
  29. package/lib/components/types/source.d.ts +21 -6
  30. package/lib/components/vue-func.js +71 -12
  31. package/lib/components/vue.js +70 -13
  32. package/lib/css/find/animations.d.ts +10 -0
  33. package/lib/css/find/animations.js +35 -0
  34. package/lib/css/find/classname.d.ts +5 -0
  35. package/lib/css/find/classname.js +14 -0
  36. package/lib/css/find/prop.d.ts +5 -0
  37. package/lib/css/find/prop.js +12 -0
  38. package/lib/css/minify.d.ts +2 -0
  39. package/lib/css/minify.js +5 -0
  40. package/lib/css/rules.d.ts +10 -0
  41. package/lib/css/rules.js +26 -0
  42. package/lib/css/stringify.d.ts +8 -2
  43. package/lib/css/stringify.js +16 -8
  44. package/lib/css/stylesheet.d.ts +0 -2
  45. package/lib/css/stylesheet.js +3 -3
  46. package/lib/css/types.d.ts +1 -1
  47. package/lib/helpers/data/compact.d.ts +20 -0
  48. package/lib/helpers/data/compact.js +38 -0
  49. package/lib/helpers/data/expand.d.ts +5 -0
  50. package/lib/helpers/data/expand.js +9 -0
  51. package/lib/helpers/reduce-motion.d.ts +2 -0
  52. package/lib/helpers/reduce-motion.js +3 -0
  53. package/lib/index.d.ts +15 -2
  54. package/lib/index.js +11 -2
  55. package/lib/svg-css/icon/css.d.ts +25 -0
  56. package/lib/svg-css/icon/css.js +57 -0
  57. package/lib/svg-css/icon/types.d.ts +45 -0
  58. package/lib/svg-css/icon/types.js +1 -0
  59. package/lib/svg-css/icon-set/add.d.ts +7 -0
  60. package/lib/svg-css/icon-set/add.js +52 -0
  61. package/lib/svg-css/icon-set/create.d.ts +6 -0
  62. package/lib/svg-css/icon-set/create.js +8 -0
  63. package/lib/svg-css/icon-set/get.d.ts +4 -0
  64. package/lib/svg-css/icon-set/get.js +92 -0
  65. package/lib/svg-css/icon-set/minify/expand.d.ts +10 -0
  66. package/lib/svg-css/icon-set/minify/expand.js +35 -0
  67. package/lib/svg-css/icon-set/minify/keys.d.ts +5 -0
  68. package/lib/svg-css/icon-set/minify/keys.js +9 -0
  69. package/lib/svg-css/icon-set/minify/minify.d.ts +19 -0
  70. package/lib/svg-css/icon-set/minify/minify.js +74 -0
  71. package/lib/svg-css/icon-set/types.d.ts +43 -0
  72. package/lib/svg-css/icon-set/types.js +1 -0
  73. package/lib/svg-css/states/cleanup-values.d.ts +6 -0
  74. package/lib/svg-css/states/cleanup-values.js +15 -0
  75. package/lib/svg-css/states/fallback/parse.d.ts +7 -0
  76. package/lib/svg-css/states/fallback/parse.js +46 -0
  77. package/lib/svg-css/states/fallback/stringify.d.ts +6 -0
  78. package/lib/svg-css/states/fallback/stringify.js +9 -0
  79. package/lib/svg-css/states/fallback/test.d.ts +9 -0
  80. package/lib/svg-css/states/fallback/test.js +21 -0
  81. package/lib/svg-css/states/fallback/types.d.ts +20 -0
  82. package/lib/svg-css/states/fallback/types.js +1 -0
  83. package/lib/svg-css/states/focus.d.ts +10 -0
  84. package/lib/svg-css/states/focus.js +14 -0
  85. package/lib/svg-css/states/generator.d.ts +19 -0
  86. package/lib/svg-css/states/generator.js +31 -0
  87. package/lib/svg-css/states/key.d.ts +6 -0
  88. package/lib/svg-css/states/key.js +22 -0
  89. package/lib/svg-css/states/object.d.ts +6 -0
  90. package/lib/svg-css/states/object.js +13 -0
  91. package/lib/svg-css/states/selector/helpers/iterate.d.ts +10 -0
  92. package/lib/svg-css/states/selector/helpers/iterate.js +71 -0
  93. package/lib/svg-css/states/selector/merge.d.ts +6 -0
  94. package/lib/svg-css/states/selector/merge.js +29 -0
  95. package/lib/svg-css/states/selector/parse.d.ts +13 -0
  96. package/lib/svg-css/states/selector/parse.js +74 -0
  97. package/lib/svg-css/states/selector/part/merge.d.ts +10 -0
  98. package/lib/svg-css/states/selector/part/merge.js +37 -0
  99. package/lib/svg-css/states/selector/part/split.d.ts +13 -0
  100. package/lib/svg-css/states/selector/part/split.js +60 -0
  101. package/lib/svg-css/states/selector/part/stringify.d.ts +9 -0
  102. package/lib/svg-css/states/selector/part/stringify.js +32 -0
  103. package/lib/svg-css/states/selector/split.d.ts +6 -0
  104. package/lib/svg-css/states/selector/split.js +29 -0
  105. package/lib/svg-css/states/selector/stringify.d.ts +8 -0
  106. package/lib/svg-css/states/selector/stringify.js +143 -0
  107. package/lib/svg-css/states/selector/sub/merge.d.ts +6 -0
  108. package/lib/svg-css/states/selector/sub/merge.js +36 -0
  109. package/lib/svg-css/states/selector/sub/split.d.ts +10 -0
  110. package/lib/svg-css/states/selector/sub/split.js +61 -0
  111. package/lib/svg-css/states/selector/sub/stringify.d.ts +6 -0
  112. package/lib/svg-css/states/selector/sub/stringify.js +17 -0
  113. package/lib/svg-css/states/selector/types.d.ts +37 -0
  114. package/lib/svg-css/states/selector/types.js +1 -0
  115. package/lib/svg-css/states/types.d.ts +9 -0
  116. package/lib/svg-css/states/types.js +1 -0
  117. package/lib/svg-css/states/validate.d.ts +6 -0
  118. package/lib/svg-css/states/validate.js +55 -0
  119. package/lib/svg-css/states/value.d.ts +10 -0
  120. package/lib/svg-css/states/value.js +15 -0
  121. package/package.json +8 -8
  122. package/lib/components/helpers/css/name.d.ts +0 -7
  123. package/lib/components/helpers/css/name.js +0 -12
@@ -1,3 +1,5 @@
1
+ import { stringifyStylesheet } from "../../css/stylesheet.js";
2
+
1
3
  /**
2
4
  * Merge exported component files into single array
3
5
  */
@@ -18,7 +20,7 @@ function mergeExportedComponentFiles(items, files) {
18
20
  add(item);
19
21
  if (item.css && item.style) add({
20
22
  filename: item.css,
21
- content: item.style
23
+ content: stringifyStylesheet(item.style)
22
24
  });
23
25
  }
24
26
  return files;
@@ -1,7 +1,7 @@
1
+ import { CSSGeneratedStylesheet } from "../../../css/types.js";
1
2
  import { ComponentFactorySource } from "../../types/source.js";
2
- import { ComponentFactoryRenderingOptions } from "../../types/options.js";
3
3
  /**
4
4
  * Convert icon content to a string literal
5
5
  */
6
- declare function stringifyFactoryIconContent(icon: ComponentFactorySource, options: Pick<ComponentFactoryRenderingOptions, 'cssMode' | 'mergeCSS'>, embedCSS?: string): string;
6
+ declare function stringifyFactoryIconContent(icon: Omit<ComponentFactorySource, 'viewBox' | 'fallback' | 'states'>, embedCSS?: CSSGeneratedStylesheet): string;
7
7
  export { stringifyFactoryIconContent };
@@ -1,17 +1,11 @@
1
- import { generateCSSDefaultImportName } from "../css/name.js";
1
+ import { stringifyStylesheet } from "../../../css/stylesheet.js";
2
2
 
3
3
  /**
4
4
  * Convert icon content to a string literal
5
5
  */
6
- function stringifyFactoryIconContent(icon, options, embedCSS) {
7
- const { cssMode, mergeCSS } = options;
8
- let content = "`" + (embedCSS ? `<style>${embedCSS}</style>${icon.content}` : icon.content).replace(/`/g, "\\`") + "`";
9
- switch (cssMode) {
10
- case "module":
11
- for (const className in icon.classes) content = content.replace(new RegExp("([\" ])(" + className + ")([\" ])", "g"), `$1\${${mergeCSS ? "css" : generateCSSDefaultImportName(className)}['${className}']}$3`);
12
- return content;
13
- default: return content;
14
- }
6
+ function stringifyFactoryIconContent(icon, embedCSS) {
7
+ const style = embedCSS ? stringifyStylesheet(embedCSS) : "";
8
+ return "`" + (style ? `<style>${style}</style>${icon.content}` : icon.content).replace(/`/g, "\\`") + "`";
15
9
  }
16
10
 
17
11
  export { stringifyFactoryIconContent };
@@ -1,3 +1,4 @@
1
+ import { CSSGeneratedStylesheet } from "../../../css/types.js";
1
2
  import { GeneratedAssetFile } from "../../types/component.js";
2
3
  import { ComponentFactorySource } from "../../types/source.js";
3
4
  import { ComponentFactoryOptions } from "../../types/options.js";
@@ -10,5 +11,5 @@ interface Options extends Pick<ComponentFactoryOptions, 'cssMode' | 'cssPath' |
10
11
  *
11
12
  * Adds imports to imports object, adds assets
12
13
  */
13
- declare function generateCSSFilesForComponent(content: ComponentFactorySource, imports: FactoryComponentImports, assets: GeneratedAssetFile[], options: Options): string | undefined;
14
+ declare function generateCSSFilesForComponent(content: Omit<ComponentFactorySource, 'viewBox'>, imports: FactoryComponentImports, assets: GeneratedAssetFile[], options: Options): CSSGeneratedStylesheet | undefined;
14
15
  export { generateCSSFilesForComponent };
@@ -1,6 +1,6 @@
1
- import { stringifyCSSKeyframes, stringifyCSSSelector } from "../../../css/stringify.js";
2
- import { generateCSSDefaultImportName } from "./name.js";
1
+ import { createEmptyStylesheet, stringifyStylesheet } from "../../../css/stylesheet.js";
3
2
  import { getGeneratedCSSFilename } from "../filenames/css.js";
3
+ import { renderStatefulSVGCSSIconStyle } from "../../../svg-css/icon/css.js";
4
4
 
5
5
  /**
6
6
  * Generate CSS files for component
@@ -8,65 +8,62 @@ import { getGeneratedCSSFilename } from "../filenames/css.js";
8
8
  * Adds imports to imports object, adds assets
9
9
  */
10
10
  function generateCSSFilesForComponent(content, imports, assets, options) {
11
- const { classes, keyframes } = content;
12
- if (!classes) return;
11
+ if (!content.classes) return;
13
12
  const { cssMode, componentType } = options;
14
13
  const isComponent = cssMode === "embed";
15
14
  const returnCSS = isComponent || cssMode === "prop";
16
- const isModule = cssMode === "module";
17
15
  const mergeCSS = (returnCSS || options.mergeCSS) ?? false;
18
- const embedAnimations = isModule && !mergeCSS;
19
- const classNamePrefix = isComponent && componentType === "svelte" ? ":global " : "";
20
- const keyframesPrefix = isComponent && componentType === "svelte" ? "-global-" : "";
21
- const mergedContent = [];
22
- for (const className in classes) {
23
- const baseContent = stringifyCSSSelector(`${classNamePrefix}.${className}`, classes[className]);
24
- let content = baseContent;
25
- if (embedAnimations && keyframes) {
26
- for (const animationName in keyframes) if (baseContent.includes(animationName)) {
27
- const value = keyframes[animationName];
28
- content += "\n" + (typeof value === "string" ? value : stringifyCSSKeyframes(keyframesPrefix + animationName, value));
16
+ const commonStylesheet = mergeCSS ? createEmptyStylesheet() : void 0;
17
+ const statefulData = content.statefulData;
18
+ const stylesheets = renderStatefulSVGCSSIconStyle(content, statefulData?.context ?? null, commonStylesheet);
19
+ if (isComponent && componentType === "svelte") {
20
+ const list = commonStylesheet ? [commonStylesheet] : Object.values(stylesheets);
21
+ const wrapSelectors = (selectors) => {
22
+ const keys = Object.keys(selectors);
23
+ for (const selector of keys) {
24
+ const value = selectors[selector];
25
+ if (!selector.startsWith("@")) {
26
+ delete selectors[selector];
27
+ const newSelector = selector.split(",").map((item) => `:global(${item.trim()})`).join(", ");
28
+ selectors[newSelector] = value;
29
+ }
30
+ if (value.nested) wrapSelectors(value.nested);
31
+ }
32
+ };
33
+ for (const stylesheet of list) {
34
+ wrapSelectors(stylesheet.selectors);
35
+ const keyframes = stylesheet.keyframes;
36
+ const animations = Object.keys(keyframes);
37
+ for (const animationName of animations) {
38
+ const animation = keyframes[animationName];
39
+ delete keyframes[animationName];
40
+ keyframes["-global-" + animationName] = animation;
29
41
  }
30
42
  }
31
- if (mergeCSS) {
32
- mergedContent.push(content);
33
- continue;
34
- }
35
- const filename = getGeneratedCSSFilename(className, options);
36
- assets.push({
37
- ...filename,
38
- content
39
- });
40
- if (isModule) imports.modules[filename.import] = generateCSSDefaultImportName(className);
41
- else imports.css.add(filename.import);
42
43
  }
43
- if (!embedAnimations && keyframes) for (const animationName in keyframes) {
44
- const value = keyframes[animationName];
45
- const content = typeof value === "string" ? value : stringifyCSSKeyframes(keyframesPrefix + animationName, value);
46
- if (mergeCSS) {
47
- mergedContent.push(content);
48
- continue;
44
+ if (!mergeCSS) for (const className in stylesheets) {
45
+ const stylesheet = stylesheets[className];
46
+ const content = stringifyStylesheet(stylesheet);
47
+ if (content) {
48
+ const filename = getGeneratedCSSFilename(className, options);
49
+ assets.push({
50
+ ...filename,
51
+ content
52
+ });
53
+ imports.css.add(filename.import);
49
54
  }
50
- const filename = getGeneratedCSSFilename(animationName, options);
51
- assets.push({
52
- ...filename,
53
- content
54
- });
55
- if (isModule) imports.modules[filename.import] = generateCSSDefaultImportName(animationName);
56
- else imports.css.add(filename.import);
57
55
  }
58
- if (mergeCSS && mergedContent.length) {
59
- const content = mergedContent.join("\n");
60
- if (typeof mergeCSS == "object") {
56
+ else if (typeof mergeCSS == "object") {
57
+ const content = stringifyStylesheet(commonStylesheet);
58
+ if (content) {
61
59
  assets.push({
62
60
  ...mergeCSS,
63
61
  content
64
62
  });
65
- if (isModule) imports.modules[mergeCSS.import] = "css";
66
- else if (!returnCSS) imports.css.add(mergeCSS.import);
63
+ imports.css.add(mergeCSS.import);
67
64
  }
68
- return returnCSS ? content : void 0;
69
65
  }
66
+ return returnCSS ? commonStylesheet : void 0;
70
67
  }
71
68
 
72
69
  export { generateCSSFilesForComponent };
@@ -2,5 +2,5 @@ import { ComponentFactoryOptions, GeneratedAssetPath } from "../../types/options
2
2
  /**
3
3
  * Generate CSS filename based on options
4
4
  */
5
- declare function getGeneratedCSSFilename(name: string, options: Pick<ComponentFactoryOptions, 'cssMode' | 'cssPath' | 'doubleDirsForCSS'>): GeneratedAssetPath;
5
+ declare function getGeneratedCSSFilename(name: string, options: Pick<ComponentFactoryOptions, 'cssPath' | 'doubleDirsForCSS'>): GeneratedAssetPath;
6
6
  export { getGeneratedCSSFilename };
@@ -4,9 +4,8 @@ import { getGeneratedAssetFilename } from "./asset.js";
4
4
  * Generate CSS filename based on options
5
5
  */
6
6
  function getGeneratedCSSFilename(name, options) {
7
- const { cssPath, doubleDirsForCSS, cssMode } = options;
8
- const baseName = doubleDirsForCSS ? `${name.slice(0, 1).toLowerCase()}/${name}` : name;
9
- return getGeneratedAssetFilename(cssMode === "module" ? `${baseName}.module.css` : `${baseName}.css`, cssPath);
7
+ const { cssPath, doubleDirsForCSS } = options;
8
+ return getGeneratedAssetFilename(`${doubleDirsForCSS ? `${name.slice(0, 1).toLowerCase()}/${name}` : name}.css`, cssPath);
10
9
  }
11
10
 
12
11
  export { getGeneratedCSSFilename };
@@ -0,0 +1,14 @@
1
+ import { GeneratedAssetFile } from "../../types/component.js";
2
+ import { ComponentFactoryOptions } from "../../types/options.js";
3
+ import { FactoryComponentImports } from "../imports/types.js";
4
+ interface Data {
5
+ functionName: string;
6
+ content: string;
7
+ exportNames?: Set<string>;
8
+ jsName?: string;
9
+ }
10
+ /**
11
+ * Adds a custom function to assets
12
+ */
13
+ declare function addCustomFunctionAsset(imports: FactoryComponentImports, assets: GeneratedAssetFile[], options: Pick<ComponentFactoryOptions, 'rootPath' | 'helpersDirectory'>, data: Data): void;
14
+ export { addCustomFunctionAsset };
@@ -0,0 +1,17 @@
1
+ import { defaultHelpersDirectory, getGeneratedAssetFilename } from "../filenames/asset.js";
2
+ import { camelToKebab } from "../../../helpers/misc/strings.js";
3
+
4
+ /**
5
+ * Adds a custom function to assets
6
+ */
7
+ function addCustomFunctionAsset(imports, assets, options, data) {
8
+ const { functionName, content, exportNames } = data;
9
+ const filename = getGeneratedAssetFilename(`${options.helpersDirectory ?? defaultHelpersDirectory}/${data.jsName ?? camelToKebab(functionName)}.js`, options.rootPath);
10
+ assets.push({
11
+ ...filename,
12
+ content
13
+ });
14
+ imports.named[filename.import] = exportNames ?? new Set([functionName]);
15
+ }
16
+
17
+ export { addCustomFunctionAsset };
@@ -0,0 +1,8 @@
1
+ import { GeneratedAssetFile } from "../../types/component.js";
2
+ import { ComponentFactoryOptions } from "../../types/options.js";
3
+ import { FactoryComponentImports } from "../imports/types.js";
4
+ /**
5
+ * Adds getFallback() function to assets
6
+ */
7
+ declare function addFallbackFunctionAsset(imports: FactoryComponentImports, assets: GeneratedAssetFile[], options: Pick<ComponentFactoryOptions, 'rootPath' | 'helpersDirectory'>, defaultValues: Record<string, boolean | string>): string;
8
+ export { addFallbackFunctionAsset };
@@ -0,0 +1,53 @@
1
+ import { createUniqueHashContext } from "../../../helpers/hash/context.js";
2
+ import { getUniqueHash } from "../../../helpers/hash/unique.js";
3
+ import { defaultHelpersDirectory, getGeneratedAssetFilename } from "../filenames/asset.js";
4
+
5
+ const sharedFunctionName = "getIconFallback";
6
+ const hashedFunctionName = "getFallback";
7
+ const functionContent = `
8
+ export function ${sharedFunctionName}(
9
+ defaultValues,
10
+ template,
11
+ values,
12
+ ) {
13
+ const stateValue = (state) =>
14
+ values[state] ?? defaultValues?.[state];
15
+ return template
16
+ .map((chunk) =>
17
+ typeof chunk === 'string'
18
+ ? chunk
19
+ : 'values' in chunk
20
+ ? chunk.values[+!!stateValue(chunk.state)]
21
+ : stateValue(chunk.state)
22
+ )
23
+ .join('');
24
+ }
25
+ `;
26
+ /**
27
+ * Adds getFallback() function to assets
28
+ */
29
+ function addFallbackFunctionAsset(imports, assets, options, defaultValues) {
30
+ const hash = getUniqueHash(defaultValues, {
31
+ context: createUniqueHashContext(),
32
+ css: true,
33
+ length: 10
34
+ });
35
+ const assetDirectory = options.helpersDirectory ?? defaultHelpersDirectory;
36
+ const sharedFilename = getGeneratedAssetFilename(`${assetDirectory}/fallback.js`, options.rootPath);
37
+ assets.push({
38
+ ...sharedFilename,
39
+ content: functionContent
40
+ });
41
+ const hashedFilename = getGeneratedAssetFilename(`${assetDirectory}/fallback-${hash}.js`, options.rootPath);
42
+ assets.push({
43
+ ...hashedFilename,
44
+ content: `import { ${sharedFunctionName} } from './fallback.js';
45
+
46
+ export const ${hashedFunctionName} = ${sharedFunctionName}.bind(null, ${JSON.stringify(defaultValues)});
47
+ `
48
+ });
49
+ imports.named[hashedFilename.import] = new Set([hashedFunctionName]);
50
+ return hashedFunctionName;
51
+ }
52
+
53
+ export { addFallbackFunctionAsset };
@@ -0,0 +1,8 @@
1
+ import { GeneratedAssetFile } from "../../types/component.js";
2
+ import { ComponentFactoryOptions } from "../../types/options.js";
3
+ import { FactoryComponentImports } from "../imports/types.js";
4
+ /**
5
+ * Adds cleanUpInnerHTML() function to assets
6
+ */
7
+ declare function addInnerHTMLFunctionAsset(imports: FactoryComponentImports, assets: GeneratedAssetFile[], options: Pick<ComponentFactoryOptions, 'rootPath' | 'helpersDirectory'>): string;
8
+ export { addInnerHTMLFunctionAsset };
@@ -0,0 +1,35 @@
1
+ import { addCustomFunctionAsset } from "./custom.js";
2
+
3
+ const functionName = "cleanupHTML";
4
+ /**
5
+ * Adds cleanUpInnerHTML() function to assets
6
+ */
7
+ function addInnerHTMLFunctionAsset(imports, assets, options) {
8
+ addCustomFunctionAsset(imports, assets, options, {
9
+ functionName,
10
+ content: `let policy;
11
+
12
+ function createPolicy() {
13
+ try {
14
+ policy = window.trustedTypes.createPolicy('iconify', {
15
+ createHTML: (s) => s,
16
+ });
17
+ } catch (err) {
18
+ policy = null;
19
+ }
20
+ }
21
+
22
+ export function ${functionName}(html) {
23
+ if (policy === undefined) {
24
+ createPolicy();
25
+ }
26
+
27
+ return policy ? policy.createHTML(html) : html;
28
+ }
29
+ `,
30
+ jsName: "innerhtml"
31
+ });
32
+ return functionName;
33
+ }
34
+
35
+ export { addInnerHTMLFunctionAsset };
@@ -68,11 +68,11 @@ export { ${functionName} };
68
68
  */
69
69
  function addSizeFunctionAsset(imports, assets, options) {
70
70
  const filename = getGeneratedAssetFilename(`${options.helpersDirectory ?? defaultHelpersDirectory}/size.js`, options.rootPath);
71
- imports.named[filename.import] = new Set([functionName]);
72
71
  assets.push({
73
72
  ...filename,
74
73
  content: functionContent
75
74
  });
75
+ imports.named[filename.import] = new Set([functionName]);
76
76
  return functionName;
77
77
  }
78
78
 
@@ -7,8 +7,7 @@ function createFactoryImports() {
7
7
  named: Object.create(null),
8
8
  types: Object.create(null),
9
9
  full: /* @__PURE__ */ new Set(),
10
- css: /* @__PURE__ */ new Set(),
11
- modules: Object.create(null)
10
+ css: /* @__PURE__ */ new Set()
12
11
  };
13
12
  }
14
13
 
@@ -15,7 +15,6 @@ function split(imports, relative) {
15
15
  default: filterObject(imports.default),
16
16
  named: filterObject(imports.named),
17
17
  types: filterObject(imports.types),
18
- modules: filterObject(imports.modules),
19
18
  full: new Set([...imports.full].filter(shouldInclude)),
20
19
  css: new Set([...imports.css].filter(shouldInclude))
21
20
  };
@@ -38,7 +37,6 @@ function stringifyFactoryImports(imports, includeTypes = true) {
38
37
  if (items) lines.push(`import type { ${items} } from '${source}';`);
39
38
  }
40
39
  for (const source of data.css) lines.push(`import '${source}';`);
41
- for (const source in data.modules) lines.push(`import ${data.modules[source]} from '${source}';`);
42
40
  }
43
41
  if (lines.length) lines.push("");
44
42
  return lines.join("\n");
@@ -7,6 +7,5 @@ interface FactoryComponentImports {
7
7
  types: Record<string, Set<string>>;
8
8
  full: Set<string>;
9
9
  css: Set<string>;
10
- modules: Record<string, string>;
11
10
  }
12
11
  export { FactoryComponentImports };
@@ -11,11 +11,18 @@ import { getUsedFactoryProps, stringifyFactoryPropTypes } from "./helpers/props/
11
11
  import { minifyViewBox } from "../svg/viewbox/minify.js";
12
12
  import { getViewBoxRatio } from "./helpers/content/ratio.js";
13
13
  import { addJSXComponentTypes } from "./helpers/ts/jsx.js";
14
+ import { addCustomFunctionAsset } from "./helpers/functions/custom.js";
15
+ import { addFallbackFunctionAsset } from "./helpers/functions/fallback.js";
16
+ import { addInnerHTMLFunctionAsset } from "./helpers/functions/innerhtml.js";
14
17
 
15
18
  /**
16
19
  * Create functional Vue component code
17
20
  */
18
21
  function createJSXComponent(data, options) {
22
+ const icon = data.icon;
23
+ const viewBox = icon.viewBox;
24
+ const defaultFallback = icon.defaultFallback;
25
+ const statefulData = icon.statefulData;
19
26
  const useTS = options.ts ?? false;
20
27
  const assets = [];
21
28
  const imports = createFactoryImports();
@@ -29,17 +36,16 @@ function createJSXComponent(data, options) {
29
36
  break;
30
37
  }
31
38
  const fallbackPackage = options.fallbackPackage || null;
32
- const hasFallback = !!(fallbackPackage && data.fallback);
39
+ const hasFallback = !!(fallbackPackage && defaultFallback);
33
40
  if (hasFallback) {
34
41
  imports.named[fallbackPackage] = new Set(["Icon"]);
35
42
  dependencies.add(fallbackPackage);
36
43
  }
37
44
  const reactNamedImports = new Set([createElement]);
38
45
  imports.named[importPackage] = reactNamedImports;
39
- const style = generateCSSFilesForComponent(data.icon, imports, assets, options);
46
+ const style = generateCSSFilesForComponent(icon, imports, assets, options);
40
47
  const isEmbeddedCSS = options.cssMode === "embed";
41
48
  let hasFixedSize = !!options.width && !!options.height;
42
- const viewBox = data.viewBox;
43
49
  const hasComputedViewbox = options.square && !hasFixedSize && viewBox.width !== viewBox.height;
44
50
  const isStringViewBox = !hasFallback;
45
51
  const hasComputedRatio = hasComputedViewbox && isStringViewBox;
@@ -52,6 +58,61 @@ function createJSXComponent(data, options) {
52
58
  value: "props",
53
59
  template: "...props,"
54
60
  };
61
+ let computedFallback = false;
62
+ if (statefulData) {
63
+ const { supportedStates, allStates } = statefulData;
64
+ if (supportedStates.size) {
65
+ const computedStates = [];
66
+ const computedStateNames = [];
67
+ let addedStateFunc = false;
68
+ for (const state of allStates) if (typeof state === "string") {
69
+ if (supportedStates.has(state)) {
70
+ props[state] = {
71
+ type: "boolean",
72
+ value: state,
73
+ template: ""
74
+ };
75
+ computedStates.push(`'${state}': ${state}`);
76
+ computedStateNames.push(state);
77
+ }
78
+ } else {
79
+ const stateName = state[0];
80
+ if (supportedStates.has(stateName)) {
81
+ const stateValues = state[1];
82
+ const defaultStateValue = state[2] ?? stateValues[0];
83
+ props[stateName] = {
84
+ type: stateValues.map((value) => `'${value}'`).join(" | "),
85
+ value: stateName,
86
+ template: ""
87
+ };
88
+ computedStates.push(`'${stateName}': namedStateValue(${stateName}, '${defaultStateValue}')`);
89
+ computedStateNames.push(stateName);
90
+ if (!addedStateFunc) {
91
+ addedStateFunc = true;
92
+ addCustomFunctionAsset(imports, assets, options, {
93
+ functionName: "namedStateValue",
94
+ content: `export function namedStateValue(value, defaultValue) {
95
+ return value && value !== defaultValue ? value : undefined;
96
+ }`
97
+ });
98
+ }
99
+ }
100
+ }
101
+ if (computedStates.length) {
102
+ componentInternalCode.push(`const states = useMemo(() => ({ ${computedStates.join(", ")} }), [${computedStateNames.join(", ")}]);`);
103
+ if (hasFallback && statefulData.fallback) {
104
+ computedFallback = true;
105
+ const func = addFallbackFunctionAsset(imports, assets, options, statefulData.defaultStateValues);
106
+ componentInternalCode.push(`const fallback = useMemo(() => ${func}(${JSON.stringify(statefulData.fallback)}, states), [states]);`);
107
+ }
108
+ componentInternalCode.push(`const className = useMemo(() => Object.entries(states).map(([key, value]) => value ? \`state-\${value === true ? key : value}\` : '').join(' ').trim() || undefined, [states]);`);
109
+ props["className"] = {
110
+ value: "className",
111
+ template: `className,`
112
+ };
113
+ }
114
+ }
115
+ }
55
116
  const getViewBox = (viewBox) => isStringViewBox ? `'${stringifyIconViewBox(viewBox)}'` : JSON.stringify(minifyViewBox(viewBox));
56
117
  if (hasComputedViewbox) {
57
118
  componentExternalCode.push(`const baseViewBox = ${getViewBox(viewBox)};`, `const squareViewBox = ${getViewBox(makeSquareViewBox(viewBox))};`);
@@ -60,7 +121,7 @@ function createJSXComponent(data, options) {
60
121
  const ratioValue = getViewBoxRatio(viewBox);
61
122
  if (hasComputedRatio) componentInternalCode.push(`const ratio = useMemo(() => square ? 1 : ${ratioValue}, [square]);`);
62
123
  if (hasFixedSize) {
63
- const sizeProps = getComponentSizeValues(options, data.viewBox);
124
+ const sizeProps = getComponentSizeValues(options, viewBox);
64
125
  if (!sizeProps) throw new Error("Fixed size expected, but could not be determined");
65
126
  props.width = sizeProps.width;
66
127
  props.height = sizeProps.height;
@@ -78,7 +139,6 @@ function createJSXComponent(data, options) {
78
139
  } else {
79
140
  const getSizeProps = addSizeFunctionAsset(imports, assets, options);
80
141
  componentInternalCode.push(`const size = useMemo(() => ${getSizeProps}(width, height, ${hasComputedRatio ? "ratio" : ratioValue}), [width, height${hasComputedRatio ? ", ratio" : ""}]);`);
81
- reactNamedImports.add("useMemo");
82
142
  props.width = {
83
143
  type: "string",
84
144
  value: "width",
@@ -90,16 +150,27 @@ function createJSXComponent(data, options) {
90
150
  template: ""
91
151
  };
92
152
  }
153
+ if (componentInternalCode.some((line) => line.includes("useMemo"))) reactNamedImports.add("useMemo");
93
154
  if (options.square) props.square = { type: "boolean" };
94
155
  props.viewBox = {
95
156
  value: "viewBox",
96
157
  template: "viewBox,"
97
158
  };
159
+ let contentTemplate;
160
+ const contentValue = stringifyFactoryIconContent(data.icon, isEmbeddedCSS ? style : void 0);
161
+ if (!hasFallback) {
162
+ const funcName = addInnerHTMLFunctionAsset(imports, assets, options);
163
+ componentExternalCode.push(`const content = {__html: ${funcName}(${contentValue})};`);
164
+ contentTemplate = `dangerouslySetInnerHTML: content,`;
165
+ }
98
166
  props.content = {
99
- value: stringifyFactoryIconContent(data.icon, options, isEmbeddedCSS ? style : void 0),
100
- template: hasFallback ? void 0 : "dangerouslySetInnerHTML: {__html: {value}},"
167
+ value: contentValue,
168
+ template: contentTemplate
101
169
  };
102
- if (hasFallback && data.fallback) props.fallback = data.fallback;
170
+ if (hasFallback && defaultFallback) props.fallback = computedFallback ? {
171
+ value: "fallback",
172
+ template: "fallback,"
173
+ } : defaultFallback;
103
174
  componentInternalCode.push(`return ${createElement}(${hasFallback ? "Icon" : "'svg'"}, {
104
175
  \t\t${stringifyFactoryPropsAsJSON(props, "\n ")}
105
176
  \t});`);
@@ -1,7 +1,7 @@
1
1
  import { GeneratedAssetFile } from "../types/component.js";
2
- import { ConvertSVGContentOptions } from "../../svg-css/types.js";
3
2
  import { FactoryIconData } from "../types/data.js";
4
3
  import { GeneratedAssetPath } from "../types/options.js";
4
+ import { ConvertSVGContentOptions } from "../../svg-css/types.js";
5
5
  import { IconifyIcon, IconifyJSON } from "@iconify/types";
6
6
  interface Options extends ConvertSVGContentOptions {
7
7
  fallback?: string | boolean;
@@ -8,13 +8,15 @@ import { normaliseIconifyIcon } from "../../iconify/icon/nornalise.js";
8
8
  function convertIconifyIconToFactoryContent(icon, prefix, name, options) {
9
9
  const { body, viewBox } = normaliseIconifyIcon(icon);
10
10
  const fallbackOption = options?.fallback ?? true;
11
- const fallback = typeof fallbackOption === "string" ? fallbackOption : fallbackOption ? `${prefix}:${name}` : void 0;
11
+ const defaultFallback = typeof fallbackOption === "string" ? fallbackOption : fallbackOption ? `${prefix}:${name}` : void 0;
12
12
  return {
13
13
  prefix,
14
14
  name,
15
- viewBox,
16
- icon: convertSVGContentToCSSRules(body, options),
17
- fallback
15
+ icon: {
16
+ ...convertSVGContentToCSSRules(body, options),
17
+ viewBox,
18
+ defaultFallback
19
+ }
18
20
  };
19
21
  }
20
22
  /**
@@ -0,0 +1,8 @@
1
+ import { SVGCSSStatefulIcon } from "../../svg-css/icon/types.js";
2
+ import { ComponentFactorySource } from "../types/source.js";
3
+ import { ComponentFactoryStatefulIconRenderingOptions } from "../types/options.js";
4
+ /**
5
+ * Check states for stateful icon
6
+ */
7
+ declare function prepareComponentFactoryStatefulIcon(icon: SVGCSSStatefulIcon, options?: ComponentFactoryStatefulIconRenderingOptions): ComponentFactorySource | undefined;
8
+ export { prepareComponentFactoryStatefulIcon };
@@ -0,0 +1,73 @@
1
+ import { getStateValue } from "../../svg-css/states/value.js";
2
+ import { createStatefulIconSelectorsContext } from "../../svg-css/states/selector/parse.js";
3
+ import { parseIconFallbackTemplate } from "../../svg-css/states/fallback/parse.js";
4
+ import { parseViewBox } from "../../svg/viewbox/parse.js";
5
+
6
+ /**
7
+ * Check states for stateful icon
8
+ */
9
+ function prepareComponentFactoryStatefulIcon(icon, options) {
10
+ const viewBox = typeof icon.viewBox === "string" ? parseViewBox(icon.viewBox) : icon.viewBox;
11
+ if (!viewBox) return;
12
+ const newIcon = {
13
+ ...icon,
14
+ viewBox
15
+ };
16
+ const fallback = icon.fallback;
17
+ const isStatefulFallback = fallback?.includes("{");
18
+ if (!isStatefulFallback && fallback) newIcon.defaultFallback = fallback;
19
+ const allStates = icon.states;
20
+ if (!allStates) return newIcon;
21
+ const config = Object.create(null);
22
+ const customConfig = options?.stateSelectors;
23
+ const supportedStates = /* @__PURE__ */ new Set();
24
+ const defaultStateValues = Object.create(null);
25
+ const supportedStateValues = Object.create(null);
26
+ for (const state of allStates) {
27
+ const stateName = typeof state === "string" ? state : state[0];
28
+ const defaultValue = getStateValue(state);
29
+ defaultStateValues[stateName] = defaultValue;
30
+ if (customConfig?.[stateName]) config[stateName] = customConfig[stateName];
31
+ else {
32
+ if (state === "focus") config[stateName] = ".svg-focus-anchor:focus-within, .svg-hover-anchor:hover, &.state-focus";
33
+ else config[stateName] = `&.state-{state}`;
34
+ supportedStates.add(stateName);
35
+ supportedStateValues[stateName] = defaultValue;
36
+ }
37
+ }
38
+ const statefulData = {
39
+ allStates,
40
+ supportedStates,
41
+ defaultStateValues,
42
+ supportedStateValues,
43
+ context: createStatefulIconSelectorsContext(config, allStates)
44
+ };
45
+ if (isStatefulFallback) {
46
+ let fallbackTemplate = parseIconFallbackTemplate(fallback, allStates);
47
+ if (!fallbackTemplate) return newIcon;
48
+ let defaultFallback = "";
49
+ let hasStatefulFallback = false;
50
+ fallbackTemplate = fallbackTemplate.map((item) => {
51
+ if (typeof item === "string") {
52
+ defaultFallback += item;
53
+ return item;
54
+ }
55
+ const stateName = item.state;
56
+ const defaultValue = defaultStateValues[stateName] || stateName;
57
+ defaultFallback += defaultValue;
58
+ if (supportedStates.has(stateName)) {
59
+ hasStatefulFallback = true;
60
+ return item;
61
+ }
62
+ return defaultValue;
63
+ });
64
+ if (hasStatefulFallback) statefulData.fallback = fallbackTemplate;
65
+ newIcon.defaultFallback = defaultFallback;
66
+ }
67
+ return {
68
+ ...newIcon,
69
+ statefulData
70
+ };
71
+ }
72
+
73
+ export { prepareComponentFactoryStatefulIcon };