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 +10 -0
- package/package.json +21 -4
- package/src/components/EmptyState.svelte +6 -2
- package/src/components/FieldGroup.svelte +1 -15
- package/src/components/Footer.svelte +4 -0
- package/src/components/ListItem.svelte +8 -2
- package/src/components/LoadingState.svelte +3 -1
- package/src/components/StatusBar.svelte +2 -1
- package/src/lib/colors.js +2 -1
- package/src/lib/figma-helpers.ts +5 -8
- package/src/lib/index.js +1 -4
- package/src/lib/resize.js +2 -2
- package/src/lib/validation.js +2 -1
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.
|
|
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
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
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"}>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { createEventDispatcher } from "svelte";
|
|
3
|
-
import { IconButton,
|
|
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) => {
|
|
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"
|
|
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
|
|
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) =>
|
|
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
|
|
package/src/lib/figma-helpers.ts
CHANGED
|
@@ -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
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)
|
package/src/lib/validation.js
CHANGED
|
@@ -58,7 +58,7 @@ export function validateUrl(url, options = { required: true }) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
return { valid: true };
|
|
61
|
-
} catch
|
|
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
|