@cyberalien/svg-utils 1.1.4 → 1.1.5

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 (64) hide show
  1. package/lib/css/rules.d.ts +10 -0
  2. package/lib/css/rules.js +26 -0
  3. package/lib/css/stringify.d.ts +8 -2
  4. package/lib/css/stringify.js +16 -8
  5. package/lib/css/stylesheet.d.ts +0 -2
  6. package/lib/css/stylesheet.js +3 -3
  7. package/lib/css/types.d.ts +1 -1
  8. package/lib/helpers/reduce-motion.d.ts +2 -0
  9. package/lib/helpers/reduce-motion.js +3 -0
  10. package/lib/index.d.ts +10 -2
  11. package/lib/index.js +7 -2
  12. package/lib/svg-css/icon/css/basic.d.ts +13 -0
  13. package/lib/svg-css/icon/css/basic.js +26 -0
  14. package/lib/svg-css/icon/css/stateful.d.ts +14 -0
  15. package/lib/svg-css/icon/css/stateful.js +45 -0
  16. package/lib/svg-css/icon/types.d.ts +45 -0
  17. package/lib/svg-css/icon/types.js +1 -0
  18. package/lib/svg-css/states/cleanup-values.d.ts +6 -0
  19. package/lib/svg-css/states/cleanup-values.js +15 -0
  20. package/lib/svg-css/states/fallback/parse.d.ts +7 -0
  21. package/lib/svg-css/states/fallback/parse.js +46 -0
  22. package/lib/svg-css/states/fallback/stringify.d.ts +6 -0
  23. package/lib/svg-css/states/fallback/stringify.js +8 -0
  24. package/lib/svg-css/states/fallback/test.d.ts +9 -0
  25. package/lib/svg-css/states/fallback/test.js +21 -0
  26. package/lib/svg-css/states/fallback/types.d.ts +20 -0
  27. package/lib/svg-css/states/fallback/types.js +1 -0
  28. package/lib/svg-css/states/focus.d.ts +10 -0
  29. package/lib/svg-css/states/focus.js +14 -0
  30. package/lib/svg-css/states/generator.d.ts +19 -0
  31. package/lib/svg-css/states/generator.js +31 -0
  32. package/lib/svg-css/states/key.d.ts +6 -0
  33. package/lib/svg-css/states/key.js +22 -0
  34. package/lib/svg-css/states/object.d.ts +6 -0
  35. package/lib/svg-css/states/object.js +13 -0
  36. package/lib/svg-css/states/selector/helpers/iterate.d.ts +10 -0
  37. package/lib/svg-css/states/selector/helpers/iterate.js +71 -0
  38. package/lib/svg-css/states/selector/merge.d.ts +6 -0
  39. package/lib/svg-css/states/selector/merge.js +29 -0
  40. package/lib/svg-css/states/selector/parse.d.ts +13 -0
  41. package/lib/svg-css/states/selector/parse.js +74 -0
  42. package/lib/svg-css/states/selector/part/merge.d.ts +10 -0
  43. package/lib/svg-css/states/selector/part/merge.js +37 -0
  44. package/lib/svg-css/states/selector/part/split.d.ts +13 -0
  45. package/lib/svg-css/states/selector/part/split.js +60 -0
  46. package/lib/svg-css/states/selector/part/stringify.d.ts +9 -0
  47. package/lib/svg-css/states/selector/part/stringify.js +32 -0
  48. package/lib/svg-css/states/selector/split.d.ts +6 -0
  49. package/lib/svg-css/states/selector/split.js +29 -0
  50. package/lib/svg-css/states/selector/stringify.d.ts +8 -0
  51. package/lib/svg-css/states/selector/stringify.js +143 -0
  52. package/lib/svg-css/states/selector/sub/merge.d.ts +6 -0
  53. package/lib/svg-css/states/selector/sub/merge.js +36 -0
  54. package/lib/svg-css/states/selector/sub/split.d.ts +10 -0
  55. package/lib/svg-css/states/selector/sub/split.js +61 -0
  56. package/lib/svg-css/states/selector/sub/stringify.d.ts +6 -0
  57. package/lib/svg-css/states/selector/sub/stringify.js +17 -0
  58. package/lib/svg-css/states/selector/types.d.ts +37 -0
  59. package/lib/svg-css/states/selector/types.js +1 -0
  60. package/lib/svg-css/states/types.d.ts +9 -0
  61. package/lib/svg-css/states/types.js +1 -0
  62. package/lib/svg-css/states/value.d.ts +10 -0
  63. package/lib/svg-css/states/value.js +15 -0
  64. package/package.json +1 -1
@@ -0,0 +1,10 @@
1
+ import { CSSRules } from "./types.js";
2
+ /**
3
+ * Split rules string into object
4
+ */
5
+ declare function splitCSSRules(rules: CSSRules | string): CSSRules;
6
+ /**
7
+ * Merge rules
8
+ */
9
+ declare function mergeCSSRules(rules: CSSRules | string, oldRules?: CSSRules | string): CSSRules;
10
+ export { mergeCSSRules, splitCSSRules };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Split rules string into object
3
+ */
4
+ function splitCSSRules(rules) {
5
+ if (typeof rules === "string") {
6
+ const result = Object.create(null);
7
+ const parts = rules.split(";");
8
+ for (const part of parts) {
9
+ const [key, value] = part.split(":").map((s) => s.trim());
10
+ if (key && value) result[key] = value;
11
+ }
12
+ return result;
13
+ }
14
+ return rules;
15
+ }
16
+ /**
17
+ * Merge rules
18
+ */
19
+ function mergeCSSRules(rules, oldRules) {
20
+ return {
21
+ ...splitCSSRules(oldRules || {}),
22
+ ...splitCSSRules(rules)
23
+ };
24
+ }
25
+
26
+ export { mergeCSSRules, splitCSSRules };
@@ -7,8 +7,14 @@ declare function stringifyCSSRules(rules: CSSRules, depth?: number): string;
7
7
  * Stringify CSS selector with rules
8
8
  */
9
9
  declare function stringifyCSSSelector(selector: string, rules: string | CSSRules, depth?: number): string;
10
+ /**
11
+ * Convert animation frames to CSS string
12
+ *
13
+ * Does not include @keyframes block, only the content
14
+ */
15
+ declare function stringifyCSSAnimationFrames(keyframes: CSSKeyframes, depth?: number): string;
10
16
  /**
11
17
  * Stringify CSS keyframes
12
18
  */
13
- declare function stringifyCSSKeyframes(animationName: string, keyframes: CSSKeyframes, depth?: number): string;
14
- export { stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector };
19
+ declare function stringifyCSSKeyframes(animationName: string, keyframes: CSSKeyframes | string, depth?: number): string;
20
+ export { stringifyCSSAnimationFrames, stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector };
@@ -17,15 +17,18 @@ function stringifyCSSRules(rules, depth = 1) {
17
17
  * Stringify CSS selector with rules
18
18
  */
19
19
  function stringifyCSSSelector(selector, rules, depth = 0) {
20
- const content = typeof rules === "string" ? rules : stringifyCSSRules(rules, depth + 1);
20
+ const content = typeof rules === "string" ? indent(depth + 1) + rules + "\n" : stringifyCSSRules(rules, depth + 1);
21
21
  if (!content.length) return "";
22
22
  const tab = indent(depth);
23
23
  return `${tab}${selector} {\n${content}${tab}}\n`;
24
24
  }
25
25
  /**
26
- * Stringify CSS keyframes
26
+ * Convert animation frames to CSS string
27
+ *
28
+ * Does not include @keyframes block, only the content
27
29
  */
28
- function stringifyCSSKeyframes(animationName, keyframes, depth = 0) {
30
+ function stringifyCSSAnimationFrames(keyframes, depth = 0) {
31
+ const lines = [];
29
32
  const prop = keyframes.prop;
30
33
  const values = /* @__PURE__ */ new Map();
31
34
  keyframes.frames.forEach((frame) => {
@@ -34,13 +37,18 @@ function stringifyCSSKeyframes(animationName, keyframes, depth = 0) {
34
37
  if (item) item.push(frame.time);
35
38
  else values.set(css, [frame.time]);
36
39
  });
37
- const lines = [];
38
- lines.push(`${indent(depth)}@keyframes ${animationName} {\n`);
39
40
  values.forEach((times, css) => {
40
41
  lines.push(`${indent(depth + 1)}${times.map((time) => `${(time * 100).toFixed(2).replace(/\.?0+$/, "")}%`).join(", ")} {\n${css}${indent(depth + 1)}}\n`);
41
42
  });
42
- lines.push("}\n");
43
- return lines.join("");
43
+ return lines.join("").trim();
44
+ }
45
+ /**
46
+ * Stringify CSS keyframes
47
+ */
48
+ function stringifyCSSKeyframes(animationName, keyframes, depth = 0) {
49
+ const content = typeof keyframes === "string" ? keyframes : stringifyCSSAnimationFrames(keyframes, depth);
50
+ if (content.includes("@keyframes")) return content;
51
+ return `${indent(depth)}@keyframes ${animationName} {\n${indent(depth + 1)}${content}\n}\n`;
44
52
  }
45
53
 
46
- export { stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector };
54
+ export { stringifyCSSAnimationFrames, stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector };
@@ -5,8 +5,6 @@ import { CSSGeneratedStylesheet, CSSRules } from "./types.js";
5
5
  declare function createEmptyStylesheet(): CSSGeneratedStylesheet;
6
6
  /**
7
7
  * Add generated selector to stylesheet
8
- *
9
- * If item exists, it will be overwritten. Class names should be hashed to avoid conflicts, so this should not cause issues.
10
8
  */
11
9
  declare function addGeneratedSelector(stylesheet: CSSGeneratedStylesheet, tree: string[], rules: CSSRules | string): void;
12
10
  /**
@@ -1,4 +1,5 @@
1
1
  import { stringifyCSSKeyframes, stringifyCSSSelector } from "./stringify.js";
2
+ import { mergeCSSRules } from "./rules.js";
2
3
 
3
4
  /**
4
5
  * Create empty stylesheet
@@ -11,17 +12,16 @@ function createEmptyStylesheet() {
11
12
  }
12
13
  /**
13
14
  * Add generated selector to stylesheet
14
- *
15
- * If item exists, it will be overwritten. Class names should be hashed to avoid conflicts, so this should not cause issues.
16
15
  */
17
16
  function addGeneratedSelector(stylesheet, tree, rules) {
17
+ tree = tree.filter((item, index) => item.startsWith("@") ? tree.indexOf(item) === index : true);
18
18
  let parent = stylesheet.selectors;
19
19
  for (let i = 0; i < tree.length; i++) {
20
20
  const selector = tree[i];
21
21
  if (!parent[selector]) parent[selector] = {};
22
22
  const parentItem = parent[selector];
23
23
  if (i === tree.length - 1) {
24
- parentItem.rules = rules;
24
+ parentItem.rules = mergeCSSRules(rules, parentItem.rules);
25
25
  return;
26
26
  }
27
27
  if (!parentItem.nested) parentItem.nested = Object.create(null);
@@ -28,6 +28,6 @@ interface CSSGeneratedSelector {
28
28
  type CSSGeneratedSelectors = Record<string, CSSGeneratedSelector>;
29
29
  interface CSSGeneratedStylesheet {
30
30
  selectors: CSSGeneratedSelectors;
31
- keyframes: Record<string, CSSKeyframes>;
31
+ keyframes: Record<string, CSSKeyframes | string>;
32
32
  }
33
33
  export { CSSGeneratedSelector, CSSGeneratedSelectors, CSSGeneratedStylesheet, CSSHashOptions, CSSKeyframe, CSSKeyframes, CSSRules };
@@ -0,0 +1,2 @@
1
+ declare const prefersReduceMotion = "@media not (prefers-reduced-motion)";
2
+ export { prefersReduceMotion };
@@ -0,0 +1,3 @@
1
+ const prefersReduceMotion = "@media not (prefers-reduced-motion)";
2
+
3
+ export { prefersReduceMotion };
package/lib/index.d.ts CHANGED
@@ -5,7 +5,8 @@ import { CSSGeneratedSelector, CSSGeneratedSelectors, CSSGeneratedStylesheet, CS
5
5
  import { ParsedXMLNode, ParsedXMLTagElement, ParsedXMLTextElement, StringifyXMLOptions } from "./xml/types.js";
6
6
  import { BaseConvertSVGContentOptions, ConvertSVGContentOptions, ConvertedSVGContent } from "./svg-css/types.js";
7
7
  import { createCSSClassName } from "./css/hash.js";
8
- import { stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector } from "./css/stringify.js";
8
+ import { mergeCSSRules, splitCSSRules } from "./css/rules.js";
9
+ import { stringifyCSSAnimationFrames, stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector } from "./css/stringify.js";
9
10
  import { addGeneratedSelector, createEmptyStylesheet, stringifyStylesheet } from "./css/stylesheet.js";
10
11
  import { createUniqueHashContext } from "./helpers/hash/context.js";
11
12
  import { hashString } from "./helpers/hash/hash.js";
@@ -27,4 +28,11 @@ import { changeSVGIDs } from "./svg/ids/change.js";
27
28
  import { createUniqueIDs } from "./svg/ids/unique.js";
28
29
  import { convertSVGRootToCSS } from "./svg-css/root.js";
29
30
  import { convertSVGContentToCSSRules } from "./svg-css/content.js";
30
- export { BaseConvertSVGContentOptions, CSSGeneratedSelector, CSSGeneratedSelectors, CSSGeneratedStylesheet, CSSHashOptions, CSSKeyframe, CSSKeyframes, CSSRules, ChangeIDResult, ClassProp, ComparisonKey, ConvertSVGContentOptions, ConvertedSVGContent, HashContext, ParsedXMLNode, ParsedXMLTagElement, ParsedXMLTextElement, StringifyXMLOptions, UniqueHashOptions, UniqueHashPartialOptions, UniqueIDOptions, addGeneratedSelector, changeIDInString, changeSVGIDs, classProps, cloneObject, compareKeys, compareSets, compareValues, convertSVGContentToCSSRules, convertSVGRootToCSS, createCSSClassName, createEmptyStylesheet, createUniqueHashContext, createUniqueIDs, defaultClassProp, getUniqueHash, hashString, hashToString, iterateXMLContent, parseXMLContent, removeDuplicateIDs, removeUnusedIDs, sortObject, splitClassName, stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector, stringifyStylesheet, stringifyXMLContent, toggleClassName, uniquePromise };
31
+ import { IconFallbackAdvancedState, IconFallbackBooleanState, IconFallbackTemplate } from "./svg-css/states/fallback/types.js";
32
+ import { getIconFallback } from "./svg-css/states/fallback/stringify.js";
33
+ import { IconStatesAdvancedState, IconStatesAdvancedStateValues, IconStatesList, IconStatesSimpleState, IconStatesState } from "./svg-css/states/types.js";
34
+ import { getStatesFromKey } from "./svg-css/states/key.js";
35
+ import { getObjectFromStates } from "./svg-css/states/object.js";
36
+ import { getAdvancedStateDefaultValue, getStateValue } from "./svg-css/states/value.js";
37
+ import { SVGCSSIcon, SVGCSSIconRules, SVGCSSStatefulIcon, SVGCSSStatefulIconRules } from "./svg-css/icon/types.js";
38
+ export { BaseConvertSVGContentOptions, CSSGeneratedSelector, CSSGeneratedSelectors, CSSGeneratedStylesheet, CSSHashOptions, CSSKeyframe, CSSKeyframes, CSSRules, ChangeIDResult, ClassProp, ComparisonKey, ConvertSVGContentOptions, ConvertedSVGContent, HashContext, IconFallbackAdvancedState, IconFallbackBooleanState, IconFallbackTemplate, IconStatesAdvancedState, IconStatesAdvancedStateValues, IconStatesList, IconStatesSimpleState, IconStatesState, ParsedXMLNode, ParsedXMLTagElement, ParsedXMLTextElement, SVGCSSIcon, SVGCSSIconRules, SVGCSSStatefulIcon, SVGCSSStatefulIconRules, StringifyXMLOptions, UniqueHashOptions, UniqueHashPartialOptions, UniqueIDOptions, addGeneratedSelector, changeIDInString, changeSVGIDs, classProps, cloneObject, compareKeys, compareSets, compareValues, convertSVGContentToCSSRules, convertSVGRootToCSS, createCSSClassName, createEmptyStylesheet, createUniqueHashContext, createUniqueIDs, defaultClassProp, getAdvancedStateDefaultValue, getIconFallback, getObjectFromStates, getStateValue, getStatesFromKey, getUniqueHash, hashString, hashToString, iterateXMLContent, mergeCSSRules, parseXMLContent, removeDuplicateIDs, removeUnusedIDs, sortObject, splitCSSRules, splitClassName, stringifyCSSAnimationFrames, stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector, stringifyStylesheet, stringifyXMLContent, toggleClassName, uniquePromise };
package/lib/index.js CHANGED
@@ -11,7 +11,8 @@ import { iterateXMLContent } from "./xml/iterate.js";
11
11
  import { parseXMLContent } from "./xml/parse.js";
12
12
  import { stringifyXMLContent } from "./xml/stringify.js";
13
13
  import { createCSSClassName } from "./css/hash.js";
14
- import { stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector } from "./css/stringify.js";
14
+ import { stringifyCSSAnimationFrames, stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector } from "./css/stringify.js";
15
+ import { mergeCSSRules, splitCSSRules } from "./css/rules.js";
15
16
  import { addGeneratedSelector, createEmptyStylesheet, stringifyStylesheet } from "./css/stylesheet.js";
16
17
  import { classProps, defaultClassProp } from "./classname/const.js";
17
18
  import { splitClassName, toggleClassName } from "./classname/toggle.js";
@@ -22,5 +23,9 @@ import { changeSVGIDs } from "./svg/ids/change.js";
22
23
  import { createUniqueIDs } from "./svg/ids/unique.js";
23
24
  import { convertSVGRootToCSS } from "./svg-css/root.js";
24
25
  import { convertSVGContentToCSSRules } from "./svg-css/content.js";
26
+ import { getIconFallback } from "./svg-css/states/fallback/stringify.js";
27
+ import { getStatesFromKey } from "./svg-css/states/key.js";
28
+ import { getAdvancedStateDefaultValue, getStateValue } from "./svg-css/states/value.js";
29
+ import { getObjectFromStates } from "./svg-css/states/object.js";
25
30
 
26
- export { addGeneratedSelector, changeIDInString, changeSVGIDs, classProps, cloneObject, compareKeys, compareSets, compareValues, convertSVGContentToCSSRules, convertSVGRootToCSS, createCSSClassName, createEmptyStylesheet, createUniqueHashContext, createUniqueIDs, defaultClassProp, getUniqueHash, hashString, hashToString, iterateXMLContent, parseXMLContent, removeDuplicateIDs, removeUnusedIDs, sortObject, splitClassName, stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector, stringifyStylesheet, stringifyXMLContent, toggleClassName, uniquePromise };
31
+ export { addGeneratedSelector, changeIDInString, changeSVGIDs, classProps, cloneObject, compareKeys, compareSets, compareValues, convertSVGContentToCSSRules, convertSVGRootToCSS, createCSSClassName, createEmptyStylesheet, createUniqueHashContext, createUniqueIDs, defaultClassProp, getAdvancedStateDefaultValue, getIconFallback, getObjectFromStates, getStateValue, getStatesFromKey, getUniqueHash, hashString, hashToString, iterateXMLContent, mergeCSSRules, parseXMLContent, removeDuplicateIDs, removeUnusedIDs, sortObject, splitCSSRules, splitClassName, stringifyCSSAnimationFrames, stringifyCSSKeyframes, stringifyCSSRules, stringifyCSSSelector, stringifyStylesheet, stringifyXMLContent, toggleClassName, uniquePromise };
@@ -0,0 +1,13 @@
1
+ import { CSSGeneratedStylesheet } from "../../../css/types.js";
2
+ import { SVGCSSIcon } from "../types.js";
3
+ /**
4
+ * Add styles for icon to stylesheet
5
+ *
6
+ * If commonStylesheet is an object, all styles will be added to it and result
7
+ * will contain the same stylesheet for all classes.
8
+ *
9
+ * If commonStylesheet is not an object, styles will be separated into different
10
+ * stylesheets for classes and keyframes, which can be reused across icons.
11
+ */
12
+ declare function renderSVGCSSIconStyle(icon: SVGCSSIcon, commonStylesheet?: CSSGeneratedStylesheet | ((selector: string) => CSSGeneratedStylesheet)): Record<string, CSSGeneratedStylesheet>;
13
+ export { renderSVGCSSIconStyle };
@@ -0,0 +1,26 @@
1
+ import { addGeneratedSelector, createEmptyStylesheet } from "../../../css/stylesheet.js";
2
+ import { prefersReduceMotion } from "../../../helpers/reduce-motion.js";
3
+
4
+ /**
5
+ * Add styles for icon to stylesheet
6
+ *
7
+ * If commonStylesheet is an object, all styles will be added to it and result
8
+ * will contain the same stylesheet for all classes.
9
+ *
10
+ * If commonStylesheet is not an object, styles will be separated into different
11
+ * stylesheets for classes and keyframes, which can be reused across icons.
12
+ */
13
+ function renderSVGCSSIconStyle(icon, commonStylesheet = createEmptyStylesheet) {
14
+ const stylesheets = Object.create(null);
15
+ const getStylesheet = (className) => {
16
+ if (typeof commonStylesheet === "object") return commonStylesheet;
17
+ if (!stylesheets[className]) stylesheets[className] = commonStylesheet(className);
18
+ return stylesheets[className];
19
+ };
20
+ for (const className in icon.classes) addGeneratedSelector(getStylesheet(className), [`.${className}`], icon.classes[className]);
21
+ for (const className in icon.animations) addGeneratedSelector(getStylesheet(className), [prefersReduceMotion, `.${className}`], icon.animations[className]);
22
+ for (const keyframeName in icon.keyframes) getStylesheet(keyframeName).keyframes[keyframeName] = icon.keyframes[keyframeName];
23
+ return stylesheets;
24
+ }
25
+
26
+ export { renderSVGCSSIconStyle };
@@ -0,0 +1,14 @@
1
+ import { CSSGeneratedStylesheet } from "../../../css/types.js";
2
+ import { SVGCSSStatefulIcon } from "../types.js";
3
+ import { StatefulIconSelectorsContext } from "../../states/selector/types.js";
4
+ /**
5
+ * Add styles for stateful icon to stylesheet
6
+ *
7
+ * If commonStylesheet is an object, all styles will be added to it and result
8
+ * will contain the same stylesheet for all classes.
9
+ *
10
+ * If commonStylesheet is not an object, styles will be separated into different
11
+ * stylesheets for classes and keyframes, which can be reused across icons.
12
+ */
13
+ declare function renderStatefulSVGCSSIconStyle(icon: SVGCSSStatefulIcon, context: StatefulIconSelectorsContext, commonStylesheet?: CSSGeneratedStylesheet | ((selector: string) => CSSGeneratedStylesheet)): Record<string, CSSGeneratedStylesheet>;
14
+ export { renderStatefulSVGCSSIconStyle };
@@ -0,0 +1,45 @@
1
+ import { addGeneratedSelector, createEmptyStylesheet } from "../../../css/stylesheet.js";
2
+ import { prefersReduceMotion } from "../../../helpers/reduce-motion.js";
3
+ import { getSelectorsForStateValues } from "../../states/selector/parse.js";
4
+
5
+ /**
6
+ * Add styles for stateful icon to stylesheet
7
+ *
8
+ * If commonStylesheet is an object, all styles will be added to it and result
9
+ * will contain the same stylesheet for all classes.
10
+ *
11
+ * If commonStylesheet is not an object, styles will be separated into different
12
+ * stylesheets for classes and keyframes, which can be reused across icons.
13
+ */
14
+ function renderStatefulSVGCSSIconStyle(icon, context, commonStylesheet = createEmptyStylesheet) {
15
+ const stylesheets = Object.create(null);
16
+ const getStylesheet = (className) => {
17
+ if (typeof commonStylesheet === "object") return commonStylesheet;
18
+ if (!stylesheets[className]) stylesheets[className] = commonStylesheet(className);
19
+ return stylesheets[className];
20
+ };
21
+ for (const className in icon.classes) addGeneratedSelector(getStylesheet(className), [`.${className}`], icon.classes[className]);
22
+ for (const className in icon.animations) addGeneratedSelector(getStylesheet(className), [prefersReduceMotion, `.${className}`], icon.animations[className]);
23
+ for (const keyframeName in icon.keyframes) getStylesheet(keyframeName).keyframes[keyframeName] = icon.keyframes[keyframeName];
24
+ for (const className in icon.statefulClasses) {
25
+ const baseClassName = `.${className}`;
26
+ const stylesheet = getStylesheet(className);
27
+ const classData = icon.statefulClasses[className];
28
+ if (classData.transition) addGeneratedSelector(getStylesheet(className), [prefersReduceMotion, baseClassName], classData.transition);
29
+ for (const stateKey in classData.stateRules) {
30
+ const selectors = getSelectorsForStateValues(context, stateKey);
31
+ if (selectors) for (const tree of selectors) addGeneratedSelector(stylesheet, [...tree, baseClassName], classData.stateRules[stateKey]);
32
+ }
33
+ for (const stateKey in classData.stateTransition) {
34
+ const selectors = getSelectorsForStateValues(context, stateKey);
35
+ if (selectors) for (const tree of selectors) addGeneratedSelector(stylesheet, [
36
+ prefersReduceMotion,
37
+ ...tree,
38
+ baseClassName
39
+ ], classData.stateTransition[stateKey]);
40
+ }
41
+ }
42
+ return stylesheets;
43
+ }
44
+
45
+ export { renderStatefulSVGCSSIconStyle };
@@ -0,0 +1,45 @@
1
+ import { IconViewBox } from "../../svg/viewbox/types.js";
2
+ import { CSSKeyframes, CSSRules } from "../../css/types.js";
3
+ import { IconStatesList } from "../states/types.js";
4
+ /**
5
+ * Main properties
6
+ */
7
+ interface MainProps {
8
+ viewBox: IconViewBox | string;
9
+ fallback?: string;
10
+ content: string;
11
+ }
12
+ interface StatefulMainProps extends MainProps {
13
+ states: IconStatesList;
14
+ }
15
+ /**
16
+ * CSS data
17
+ */
18
+ interface SVGCSSIconRules {
19
+ classes?: Record<string, CSSRules | string>;
20
+ animations?: Record<string, CSSRules | string>;
21
+ keyframes?: Record<string, CSSKeyframes | string>;
22
+ }
23
+ /**
24
+ * Extended class interface for stateful icons
25
+ */
26
+ interface ExtendedSVGCSSIconClass {
27
+ stateRules?: Record<string, CSSRules | string>;
28
+ transition?: CSSRules | string;
29
+ stateTransition?: Record<string, CSSRules | string>;
30
+ }
31
+ /**
32
+ * Stateful CSS data
33
+ */
34
+ interface SVGCSSStatefulIconRules extends SVGCSSIconRules {
35
+ statefulClasses?: Record<string, ExtendedSVGCSSIconClass>;
36
+ }
37
+ /**
38
+ * Icon data
39
+ */
40
+ interface SVGCSSIcon extends MainProps, SVGCSSIconRules {}
41
+ /**
42
+ * Icon with states
43
+ */
44
+ interface SVGCSSStatefulIcon extends StatefulMainProps, SVGCSSStatefulIconRules {}
45
+ export { SVGCSSIcon, SVGCSSIconRules, SVGCSSStatefulIcon, SVGCSSStatefulIconRules };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,6 @@
1
+ import { IconStatesList } from "./types.js";
2
+ /**
3
+ * Clean up state values object
4
+ */
5
+ declare function cleanupStateValues(states: IconStatesList, values: Record<string, boolean | string>, clone?: boolean): Record<string, string | boolean>;
6
+ export { cleanupStateValues };
@@ -0,0 +1,15 @@
1
+ import { getStateValue } from "./value.js";
2
+
3
+ /**
4
+ * Clean up state values object
5
+ */
6
+ function cleanupStateValues(states, values, clone = false) {
7
+ const result = clone ? { ...values } : values;
8
+ for (const state of states) {
9
+ const stateName = typeof state === "string" ? state : state[0];
10
+ result[stateName] = getStateValue(state, result[stateName]);
11
+ }
12
+ return result;
13
+ }
14
+
15
+ export { cleanupStateValues };
@@ -0,0 +1,7 @@
1
+ import { IconFallbackTemplate } from "./types.js";
2
+ import { IconStatesList } from "../types.js";
3
+ /**
4
+ * Parse fallback template string into array of strings and states
5
+ */
6
+ declare function parseIconFallbackTemplate(fallback: string, states: IconStatesList): IconFallbackTemplate | undefined;
7
+ export { parseIconFallbackTemplate };
@@ -0,0 +1,46 @@
1
+ function unwrap(value) {
2
+ if (value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
3
+ return value;
4
+ }
5
+ /**
6
+ * Parse fallback template string into array of strings and states
7
+ */
8
+ function parseIconFallbackTemplate(fallback, states) {
9
+ const chunks = [];
10
+ let startIndex = 0;
11
+ for (const match of fallback.matchAll(/{[^}]+}/g)) {
12
+ const matchText = match[0];
13
+ const matchIndex = match.index || 0;
14
+ if (matchIndex > startIndex) {
15
+ const prev = fallback.slice(startIndex, matchIndex);
16
+ if (prev) chunks.push(prev);
17
+ }
18
+ startIndex = matchIndex + matchText.length;
19
+ const textMatches = Array.from(matchText.matchAll(/[a-z0-9:'-]+/g));
20
+ if (!textMatches.length) return;
21
+ const separators = Array.from(matchText.matchAll(/[^a-z0-9:'-]/g));
22
+ if (separators.length !== textMatches.length + 1) return;
23
+ const stateName = textMatches[0][0].trim();
24
+ const state = states.find((s) => typeof s === "string" ? s === stateName : s[0] === stateName);
25
+ if (!state) return;
26
+ if (typeof state === "string") {
27
+ if (textMatches.length !== 3) return;
28
+ const trueValue = textMatches[1][0];
29
+ const falseValue = textMatches[2][0];
30
+ const firstSeparator = separators[1][0];
31
+ const secondSeparator = separators[2][0];
32
+ if (firstSeparator !== "?" || secondSeparator !== "|") return;
33
+ chunks.push({
34
+ state,
35
+ values: [unwrap(falseValue), unwrap(trueValue)]
36
+ });
37
+ } else {
38
+ if (textMatches.length > 1) return;
39
+ chunks.push({ state: stateName });
40
+ }
41
+ }
42
+ if (startIndex < fallback.length) chunks.push(fallback.slice(startIndex));
43
+ return chunks;
44
+ }
45
+
46
+ export { parseIconFallbackTemplate };
@@ -0,0 +1,6 @@
1
+ import { IconFallbackTemplate } from "./types.js";
2
+ /**
3
+ * Generate fallback string from template and states
4
+ */
5
+ declare function getIconFallback(template: IconFallbackTemplate, values: Record<string, boolean | string>): string;
6
+ export { getIconFallback };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Generate fallback string from template and states
3
+ */
4
+ function getIconFallback(template, values) {
5
+ return template.map((chunk) => typeof chunk === "string" ? chunk : "values" in chunk ? chunk.values[+!!values[chunk.state]] : values[chunk.state]).join("");
6
+ }
7
+
8
+ export { getIconFallback };
@@ -0,0 +1,9 @@
1
+ import { IconFallbackTemplate } from "./types.js";
2
+ import { IconStatesList } from "../types.js";
3
+ /**
4
+ * Parse and test fallback template string
5
+ *
6
+ * This will make sure template is valid and does not contain invalid characters
7
+ */
8
+ declare function parseAndTestIconFallbackTemplate(fallback: string, states: IconStatesList): IconFallbackTemplate | undefined;
9
+ export { parseAndTestIconFallbackTemplate };
@@ -0,0 +1,21 @@
1
+ import { parseIconFallbackTemplate } from "./parse.js";
2
+
3
+ const match = /^[a-z0-9:-]*$/;
4
+ /**
5
+ * Parse and test fallback template string
6
+ *
7
+ * This will make sure template is valid and does not contain invalid characters
8
+ */
9
+ function parseAndTestIconFallbackTemplate(fallback, states) {
10
+ const template = parseIconFallbackTemplate(fallback, states);
11
+ if (template) {
12
+ for (const chunk of template) if (typeof chunk === "string") {
13
+ if (!chunk.match(match)) return;
14
+ } else if ("values" in chunk) {
15
+ if (!chunk.values.every((v) => v.match(match))) return;
16
+ }
17
+ }
18
+ return template;
19
+ }
20
+
21
+ export { parseAndTestIconFallbackTemplate };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Fallback replacement for boolean state
3
+ */
4
+ interface IconFallbackBooleanState {
5
+ state: string;
6
+ values: [string, string];
7
+ }
8
+ /**
9
+ * Fallback for named state
10
+ *
11
+ * Value should match state values
12
+ */
13
+ interface IconFallbackAdvancedState {
14
+ state: string;
15
+ }
16
+ /**
17
+ * Template for fallback: mix of strings and states
18
+ */
19
+ type IconFallbackTemplate = (string | IconFallbackBooleanState | IconFallbackAdvancedState)[];
20
+ export { IconFallbackAdvancedState, IconFallbackBooleanState, IconFallbackTemplate };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,10 @@
1
+ import { IconStatesList, IconStatesState } from "./types.js";
2
+ /**
3
+ * Check for focus state
4
+ */
5
+ declare function isFocusState(state: IconStatesState | string): boolean;
6
+ /**
7
+ * Filter out focus state from states list
8
+ */
9
+ declare function filterFocusState(states: IconStatesList): IconStatesList;
10
+ export { filterFocusState, isFocusState };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Check for focus state
3
+ */
4
+ function isFocusState(state) {
5
+ return state === "focus";
6
+ }
7
+ /**
8
+ * Filter out focus state from states list
9
+ */
10
+ function filterFocusState(states) {
11
+ return states.filter((state) => !isFocusState(state));
12
+ }
13
+
14
+ export { filterFocusState, isFocusState };
@@ -0,0 +1,19 @@
1
+ import { IconStatesList, IconStatesState } from "./types.js";
2
+ /**
3
+ * Generate code for object from states
4
+ *
5
+ * Does not include starting { and ending }
6
+ * Last entry does not have a comma at the end
7
+ */
8
+ declare function generateObjectFromStates(states: IconStatesList): string;
9
+ /**
10
+ * Generate code for TypeScript interface from state
11
+ */
12
+ declare function getStateInterface(state: IconStatesState, optional?: boolean): string;
13
+ /**
14
+ * Generate code for TypeScript interface from states
15
+ *
16
+ * Does not include starting { and ending }
17
+ */
18
+ declare function generateInterfaceFromStates(states: IconStatesList, optional?: boolean): string;
19
+ export { generateInterfaceFromStates, generateObjectFromStates, getStateInterface };
@@ -0,0 +1,31 @@
1
+ import { getAdvancedStateDefaultValue } from "./value.js";
2
+
3
+ /**
4
+ * Generate code for object from states
5
+ *
6
+ * Does not include starting { and ending }
7
+ * Last entry does not have a comma at the end
8
+ */
9
+ function generateObjectFromStates(states) {
10
+ const lines = [];
11
+ for (const state of states) if (typeof state === "string") lines.push(`'${state}': false`);
12
+ else lines.push(`'${state[0]}': '${getAdvancedStateDefaultValue(state)}'`);
13
+ return lines.join(",\n ");
14
+ }
15
+ /**
16
+ * Generate code for TypeScript interface from state
17
+ */
18
+ function getStateInterface(state, optional = true) {
19
+ const q = optional ? "?" : "";
20
+ return typeof state === "string" ? `'${state}'${q}: boolean;` : `'${state[0]}'${q}: ${state[1].map((value) => `'${value}'`).join(" | ")};`;
21
+ }
22
+ /**
23
+ * Generate code for TypeScript interface from states
24
+ *
25
+ * Does not include starting { and ending }
26
+ */
27
+ function generateInterfaceFromStates(states, optional = true) {
28
+ return states.map((state) => getStateInterface(state, optional)).join("\n ");
29
+ }
30
+
31
+ export { generateInterfaceFromStates, generateObjectFromStates, getStateInterface };
@@ -0,0 +1,6 @@
1
+ import { IconStatesList } from "./types.js";
2
+ /**
3
+ * Get state values from an underscore separated list of state values
4
+ */
5
+ declare function getStatesFromKey(key: string, states: IconStatesList): Record<string, string | boolean>;
6
+ export { getStatesFromKey };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Get state values from an underscore separated list of state values
3
+ */
4
+ function getStatesFromKey(key, states) {
5
+ const obj = Object.create(null);
6
+ const parts = key.split("_");
7
+ for (const state of states) {
8
+ if (typeof state === "string") {
9
+ if (parts.includes(state)) obj[state] = true;
10
+ continue;
11
+ }
12
+ const values = state[1];
13
+ const defaultValue = state[2] || values[0];
14
+ for (const value of values) if (value !== defaultValue && parts.includes(value)) {
15
+ obj[state[0]] = value;
16
+ break;
17
+ }
18
+ }
19
+ return obj;
20
+ }
21
+
22
+ export { getStatesFromKey };
@@ -0,0 +1,6 @@
1
+ import { IconStatesList } from "./types.js";
2
+ /**
3
+ * Create reactive object from states
4
+ */
5
+ declare function getObjectFromStates(states: IconStatesList): Record<string, boolean | string>;
6
+ export { getObjectFromStates };