figma-plugin-utilities 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.1] - 2026-05-13
11
+
12
+ ### Added
13
+ - ESLint configuration with TypeScript and Svelte support for code linting
14
+ - Prettier setup with Svelte plugin for consistent code formatting
15
+ - `lint` and `prettier` npm scripts for development workflow
16
+
17
+ ### Changed
18
+ - Updated **ListItem** and **StatusBar** to use icon imports from `figma-ui3-kit-svelte/icons` after the UI kit icon export restructure.
19
+
10
20
  ## [0.3.0] - 2026-05-06
11
21
 
12
22
  WCAG 2.2 AA accessibility audit and remediation across all components.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figma-plugin-utilities",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Shared Svelte components and utilities for Figma plugins",
5
5
  "type": "module",
6
6
  "svelte": "./src/index.js",
@@ -54,8 +54,25 @@
54
54
  ],
55
55
  "author": "Marius Roosendaal",
56
56
  "license": "MIT",
57
- "dependencies": {
58
- "figma-ui3-kit-svelte": "^0.5.0",
59
- "svelte": "^4.2.20"
57
+ "devDependencies": {
58
+ "@eslint/js": "^9.39.1",
59
+ "@figma/plugin-typings": "^1.51.0",
60
+ "@sveltejs/vite-plugin-svelte": "^3.0.2",
61
+ "@types/node": "^22.13.4",
62
+ "eslint": "^9.39.1",
63
+ "eslint-plugin-svelte": "^2.46.0",
64
+ "figma-ui3-kit-svelte": "file:..//_packages/figma-ui3-kit-svelte",
65
+ "globals": "^16.5.0",
66
+ "prettier": "3.7.3",
67
+ "prettier-plugin-svelte": "^3.4.0",
68
+ "svelte": "^4.2.20",
69
+ "svelte-eslint-parser": "^0.39.0",
70
+ "typescript": "^5.9.3",
71
+ "typescript-eslint": "^8.48.0",
72
+ "vite": "^5.2.0"
73
+ },
74
+ "scripts": {
75
+ "lint": "tsc --noEmit && eslint . --ext .ts,.js,.svelte && npx prettier --check \"src/**/*.{js,ts,svelte,html,css}\"",
76
+ "prettier": "npx prettier --write \"src/**/*.{js,ts,svelte,html,css}\""
60
77
  }
61
78
  }
@@ -30,9 +30,12 @@
30
30
  /** Whether to center vertically */
31
31
  export let centered = true;
32
32
 
33
- let className = '';
33
+ /** ARIA role: "status" for info messages, "alert" for errors */
34
+ export let role = "status";
35
+
36
+ let className = "";
34
37
  export { className as class };
35
- // Normalize actions
38
+
36
39
  $: normalizedActions = actions ? actions : action ? [action] : null;
37
40
  </script>
38
41
 
@@ -41,6 +44,7 @@
41
44
  class:centered
42
45
  class:small={size === "small"}
43
46
  class:large={size === "large"}
47
+ {role}
44
48
  >
45
49
  {#if icon}
46
50
  <div class="empty-state__icon" aria-hidden="true">
@@ -1,28 +1,14 @@
1
1
  <script>
2
2
  import { Label } from "figma-ui3-kit-svelte";
3
3
 
4
- /**
5
- * Field group wrapper
6
- * Wraps a form field with an optional label
7
- *
8
- * @example
9
- * <FieldGroup label="Collection">
10
- * <Dropdown menuItems={options} bind:value={selected} />
11
- * </FieldGroup>
12
- */
13
-
14
4
  /** Label text (optional) */
15
5
  export let label = "";
16
6
 
17
- /** For attribute for the label (optional) */
7
+ /** id of the associated control (optional) */
18
8
  export let labelFor = "";
19
9
 
20
10
  /** Size of the label (optional) */
21
11
  export let size = undefined;
22
-
23
- $: if (typeof window !== "undefined" && label && !labelFor) {
24
- console.warn("[FieldGroup] A label is rendered but no labelFor is set. Associate the label with its control using the labelFor prop.");
25
- }
26
12
  </script>
27
13
 
28
14
  <div class="field-group" class:small={size === "small"}>
@@ -103,6 +103,10 @@
103
103
  width: 100%;
104
104
  }
105
105
 
106
+ .footer--full :global(> *) {
107
+ flex: 1;
108
+ }
109
+
106
110
  .footer--full :global(button) {
107
111
  flex: 1;
108
112
  width: 100%;
@@ -1,6 +1,7 @@
1
1
  <script>
2
2
  import { createEventDispatcher } from "svelte";
3
- import { IconButton, IconMore, Menu } from "figma-ui3-kit-svelte";
3
+ import { IconButton, Menu } from "figma-ui3-kit-svelte";
4
+ import { IconMore } from "figma-ui3-kit-svelte/icons";
4
5
 
5
6
  /**
6
7
  * List item with optional action menu
@@ -73,7 +74,12 @@
73
74
  class="list-item"
74
75
  class:active
75
76
  on:click={handleClick}
76
- on:keydown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); handleClick(); } }}
77
+ on:keydown={(e) => {
78
+ if (e.key === "Enter" || e.key === " ") {
79
+ e.preventDefault();
80
+ handleClick();
81
+ }
82
+ }}
77
83
  role="button"
78
84
  tabindex="0"
79
85
  aria-pressed={active}
@@ -21,7 +21,9 @@
21
21
  </script>
22
22
 
23
23
  <div class="loading-state {className}" role="status">
24
- <Text variant="body-medium" color="--figma-color-text-secondary">{message}</Text>
24
+ <Text variant="body-medium" color="--figma-color-text-secondary"
25
+ >{message}</Text
26
+ >
25
27
  </div>
26
28
 
27
29
  <style>
@@ -1,6 +1,7 @@
1
1
  <script>
2
2
  import { onDestroy, createEventDispatcher } from "svelte";
3
- import { IconButton, IconClose } from "figma-ui3-kit-svelte";
3
+ import { IconButton } from "figma-ui3-kit-svelte";
4
+ import { IconClose } from "figma-ui3-kit-svelte/icons";
4
5
 
5
6
  // Status bar for notifications with auto-dismiss
6
7
  // Supports types: 'info', 'success', 'error', 'warning'
package/src/lib/colors.js CHANGED
@@ -39,7 +39,8 @@ export function hexToRgb(hex) {
39
39
  * @returns {number} Relative luminance (0-1)
40
40
  */
41
41
  export function getLuminance({ r, g, b }) {
42
- const adjust = (c) => (c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
42
+ const adjust = (c) =>
43
+ c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
43
44
  return 0.2126 * adjust(r) + 0.7152 * adjust(g) + 0.0722 * adjust(b);
44
45
  }
45
46
 
@@ -9,7 +9,7 @@
9
9
  */
10
10
  export function sendToUI<T extends Record<string, unknown>>(
11
11
  type: string,
12
- data?: T
12
+ data?: T,
13
13
  ): void {
14
14
  if (data) {
15
15
  figma.ui.postMessage({ type, ...data });
@@ -32,7 +32,7 @@ export async function getCollections(): Promise<VariableCollection[]> {
32
32
  * @returns Promise resolving to array of variables
33
33
  */
34
34
  export async function getVariables(
35
- type?: VariableResolvedDataType
35
+ type?: VariableResolvedDataType,
36
36
  ): Promise<Variable[]> {
37
37
  return figma.variables.getLocalVariablesAsync(type);
38
38
  }
@@ -61,7 +61,7 @@ export function showSuccess(message: string, timeout = 3000): void {
61
61
  * @returns Array of selected nodes
62
62
  */
63
63
  export function getSelection<T extends SceneNode>(
64
- nodeType?: NodeType
64
+ nodeType?: NodeType,
65
65
  ): readonly T[] {
66
66
  const selection = figma.currentPage.selection;
67
67
  if (nodeType) {
@@ -85,10 +85,7 @@ export function focusNodes(nodes: readonly SceneNode[]): void {
85
85
  * @param family - Font family name
86
86
  * @param style - Font style (e.g., "Regular", "Bold")
87
87
  */
88
- export async function loadFont(
89
- family: string,
90
- style: string
91
- ): Promise<void> {
88
+ export async function loadFont(family: string, style: string): Promise<void> {
92
89
  await figma.loadFontAsync({ family, style });
93
90
  }
94
91
 
@@ -109,7 +106,7 @@ export async function saveToStorage<T>(key: string, value: T): Promise<void> {
109
106
  */
110
107
  export async function loadFromStorage<T>(
111
108
  key: string,
112
- defaultValue?: T
109
+ defaultValue?: T,
113
110
  ): Promise<T | undefined> {
114
111
  try {
115
112
  const value = await figma.clientStorage.getAsync(key);
package/src/lib/index.js CHANGED
@@ -1,8 +1,5 @@
1
1
  // Message utilities
2
- export {
3
- sendToPlugin,
4
- createMessageHandler,
5
- } from "./messages.js";
2
+ export { sendToPlugin, createMessageHandler } from "./messages.js";
6
3
 
7
4
  // Color utilities
8
5
  export {
package/src/lib/resize.js CHANGED
@@ -73,10 +73,10 @@ export function resizeToFit(options = {}) {
73
73
  /**
74
74
  * Set up automatic resizing when content changes
75
75
  * Uses ResizeObserver to watch for size changes
76
- *
76
+ *
77
77
  * IMPORTANT: The container element must NOT have height: 100% or fixed height.
78
78
  * Use bind:this on a wrapper element that flows naturally with content.
79
- *
79
+ *
80
80
  * @param {object} options - Auto-resize options
81
81
  * @param {HTMLElement} options.container - Container element to observe (required, must not have fixed height)
82
82
  * @param {number} [options.width] - Width in pixels (uses default if not specified)
@@ -58,7 +58,7 @@ export function validateUrl(url, options = { required: true }) {
58
58
  }
59
59
 
60
60
  return { valid: true };
61
- } catch (err) {
61
+ } catch {
62
62
  return {
63
63
  valid: false,
64
64
  error: "Invalid URL format",
@@ -148,6 +148,7 @@ export function sanitizeInput(input, maxLength) {
148
148
  }
149
149
 
150
150
  // Remove null bytes and control characters (except newlines and tabs)
151
+ // eslint-disable-next-line no-control-regex
151
152
  str = str.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, "");
152
153
 
153
154
  // Trim whitespace