@figma-vars/hooks 2.0.0-beta.1 → 2.0.0-beta.2
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/README.md +66 -1
- package/dist/hooks/useVariables.d.ts +3 -1
- package/dist/hooks/useVariables.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +39 -39
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,8 @@ Built for the modern web, this library provides a suite of hooks to fetch, manag
|
|
|
26
26
|
- **✍️ Ergonomic Mutations**: A `useMutation`-style API for creating, updating, and deleting variables, providing clear loading and error states.
|
|
27
27
|
- **🔒 TypeScript-first**: Strictly typed for an ergonomic and safe developer experience. Get autocompletion for all API responses.
|
|
28
28
|
- **📖 Storybook & Next.js Ready**: Perfect for building live design token dashboards or style guides.
|
|
29
|
+
- **🔄 Local JSON Support**: Use local JSON files exported from Figma Dev Mode plugins when you don't have a Figma Enterprise account, enabling offline development and static deployments.
|
|
30
|
+
- **🚧 Style Dictionary Integration**: Coming soon in future beta releases - seamless integration with Amazon's Style Dictionary for multi-platform design token distribution.
|
|
29
31
|
|
|
30
32
|
---
|
|
31
33
|
|
|
@@ -162,6 +164,61 @@ function CreateVariableForm({ collectionId }: { collectionId: string }) {
|
|
|
162
164
|
}
|
|
163
165
|
```
|
|
164
166
|
|
|
167
|
+
### Using Local JSON Files (No Enterprise Account Required)
|
|
168
|
+
|
|
169
|
+
If you don't have a Figma Enterprise account (required for the Variables API), you can still use this library with local JSON files exported from Figma Dev Mode plugins. This is perfect for:
|
|
170
|
+
|
|
171
|
+
- **Offline Development**: Work on your design system without an internet connection
|
|
172
|
+
- **Static Deployments**: Deploy design token dashboards to static hosting
|
|
173
|
+
- **CI/CD Pipelines**: Use exported JSON files in automated workflows
|
|
174
|
+
- **Team Collaboration**: Share design tokens without API access
|
|
175
|
+
|
|
176
|
+
#### Getting Your JSON File
|
|
177
|
+
|
|
178
|
+
We recommend using the [Variables Exporter for Dev Mode](https://www.figma.com/community/plugin/1491572182178544621) plugin:
|
|
179
|
+
|
|
180
|
+
1. Install the plugin in Figma
|
|
181
|
+
2. Open your Figma file in Dev Mode
|
|
182
|
+
3. Run the plugin and export your variables as JSON
|
|
183
|
+
4. Save the JSON file to your project (e.g., `src/assets/figma-variables.json`)
|
|
184
|
+
|
|
185
|
+
This plugin exports the exact same format that the Figma Variables API returns, ensuring perfect compatibility with this library.
|
|
186
|
+
|
|
187
|
+
#### Using the Fallback File
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
// src/main.tsx or App.tsx
|
|
191
|
+
import React from 'react'
|
|
192
|
+
import ReactDOM from 'react-dom/client'
|
|
193
|
+
import { FigmaVarsProvider } from '@figma-vars/hooks'
|
|
194
|
+
import App from './App'
|
|
195
|
+
import variablesData from './assets/figma-variables.json'
|
|
196
|
+
|
|
197
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
198
|
+
<React.StrictMode>
|
|
199
|
+
<FigmaVarsProvider
|
|
200
|
+
token={null} // No token needed when using fallbackFile
|
|
201
|
+
fileKey="your-figma-file-key"
|
|
202
|
+
fallbackFile={variablesData}>
|
|
203
|
+
<App />
|
|
204
|
+
</FigmaVarsProvider>
|
|
205
|
+
</React.StrictMode>
|
|
206
|
+
)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
You can also pass the JSON as a string if you prefer:
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
import variablesJson from './assets/figma-variables.json?raw'
|
|
213
|
+
|
|
214
|
+
<FigmaVarsProvider
|
|
215
|
+
token={null}
|
|
216
|
+
fileKey="your-figma-file-key"
|
|
217
|
+
fallbackFile={variablesJson}>
|
|
218
|
+
<App />
|
|
219
|
+
</FigmaVarsProvider>
|
|
220
|
+
```
|
|
221
|
+
|
|
165
222
|
### Figma PAT Security
|
|
166
223
|
|
|
167
224
|
When using the Figma API, it's essential to keep your Personal Access Token (PAT) secure. Here are some best practices:
|
|
@@ -231,11 +288,19 @@ function ErrorHandling() {
|
|
|
231
288
|
|
|
232
289
|
### Core Hooks
|
|
233
290
|
|
|
234
|
-
- `useVariables()`: Fetches all local variables for the file key provided to the `FigmaVarsProvider`. Returns a SWR hook state with `data`, `isLoading`, and `error` properties. The actual Figma response is in `data.data`.
|
|
291
|
+
- `useVariables()`: Fetches all local variables for the file key provided to the `FigmaVarsProvider`. Returns a SWR hook state with `data`, `isLoading`, and `error` properties. The actual Figma response is in `data.data`. When `fallbackFile` is provided, it uses the local JSON data instead of making an API request.
|
|
235
292
|
- `useVariableCollections()`: A convenience hook that returns just the variable collections from the main `useVariables` data.
|
|
236
293
|
- `useVariableModes()`: A convenience hook that returns just the variable modes from the main `useVariables` data.
|
|
237
294
|
- `useFigmaToken()`: A simple hook to access the token and file key from the context.
|
|
238
295
|
|
|
296
|
+
### Provider Props
|
|
297
|
+
|
|
298
|
+
The `FigmaVarsProvider` accepts the following props:
|
|
299
|
+
|
|
300
|
+
- `token`: Figma Personal Access Token (PAT) for API authentication. Can be `null` when using `fallbackFile`.
|
|
301
|
+
- `fileKey`: Figma file key for the target file. Required for API requests but can be any string when using `fallbackFile`.
|
|
302
|
+
- `fallbackFile`: Optional local JSON file (as object or string) to use instead of API requests. Perfect for users without Figma Enterprise accounts.
|
|
303
|
+
|
|
239
304
|
### Mutation Hooks
|
|
240
305
|
|
|
241
306
|
All mutation hooks return an object with the following shape: `{ mutate, data, isLoading, error }`.
|
|
@@ -4,7 +4,9 @@ import { LocalVariablesResponse } from '../types/figma';
|
|
|
4
4
|
*
|
|
5
5
|
* @remarks
|
|
6
6
|
* This hook uses SWR for caching and revalidation. It fetches the variables for the
|
|
7
|
-
* file key provided via the FigmaVarsProvider context.
|
|
7
|
+
* file key provided via the FigmaVarsProvider context. If a fallbackFile is provided,
|
|
8
|
+
* it will use that instead of making an API request, which is useful for users without
|
|
9
|
+
* Figma Enterprise accounts or for offline development.
|
|
8
10
|
*
|
|
9
11
|
* @returns SWR response object with `data`, `error`, `isLoading`, and `isValidating`.
|
|
10
12
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVariables.d.ts","sourceRoot":"","sources":["../../src/hooks/useVariables.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAG1D
|
|
1
|
+
{"version":3,"file":"useVariables.d.ts","sourceRoot":"","sources":["../../src/hooks/useVariables.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAG1D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,0LAoBxB,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=require("react/jsx-runtime"),i=require("react"),_=require("swr"),p=i.createContext(void 0),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=require("react/jsx-runtime"),i=require("react"),_=require("swr"),p=i.createContext(void 0),g=({children:e,token:r,fileKey:t,fallbackFile:o})=>{const s=o===void 0?{token:r,fileKey:t}:{token:r,fileKey:t,fallbackFile:o};return b.jsx(p.Provider,{value:s,children:e})},l=()=>{const e=i.useContext(p);if(e===void 0)throw new Error("useFigmaTokenContext must be used within a FigmaVarsProvider");return e},h="https://api.figma.com",w=`${h}/v1/variables`,T=w,y=e=>`${w}/${e}`,v="application/json",A="X-FIGMA-TOKEN",u="A Figma API token is required.",I="An error occurred while fetching data from the Figma API.";async function V(e,r){if(!r)throw new Error(u);const t=await fetch(e,{method:"GET",headers:{[A]:r,"Content-Type":v}});if(!t.ok){let o=I;try{const s=await t.json();s!=null&&s.message&&(o=s.message)}catch{}throw new Error(o)}return t.json()}const E=()=>{const{token:e,fileKey:r,fallbackFile:t}=l(),o=async(n,a)=>t?typeof t=="string"?JSON.parse(t):t:V(n,a),s=e&&r?`https://api.figma.com/v1/files/${r}/variables/local`:null;return _(s&&e?[s,e]:null,s&&e?([n,a])=>o(n,a):()=>Promise.resolve(void 0))},R=()=>{const{data:e}=E(),r=i.useMemo(()=>e!=null&&e.meta?Object.values(e.meta.variableCollections):[],[e]),t=i.useMemo(()=>e!=null&&e.meta?e.meta.variableCollections:{},[e]);return{collections:r,collectionsById:t}},C=()=>{const{data:e}=E();return i.useMemo(()=>{const r=[],t={},o={};if(e!=null&&e.meta)for(const s of Object.values(e.meta.variableCollections)){r.push(...s.modes),t[s.id]=s.modes;for(const c of s.modes)o[c.modeId]=c}return{modes:r,modesByCollectionId:t,modesById:o}},[e])};function M(e,r){switch(r.type){case"loading":return{...e,status:"loading",error:null};case"success":return{...e,status:"success",data:r.payload};case"error":return{...e,status:"error",error:r.payload};default:return e}}const d=e=>{const r={status:"idle",data:null,error:null},[t,o]=i.useReducer(M,r);return{mutate:i.useCallback(async c=>{o({type:"loading"});try{const n=await e(c);return o({type:"success",payload:n}),n}catch(n){o({type:"error",payload:n});return}},[e]),...t,isLoading:t.status==="loading",isSuccess:t.status==="success",isError:t.status==="error"}};async function m(e,r,t,o){if(!r)throw new Error(u);const n={method:{CREATE:"POST",UPDATE:"PUT",DELETE:"DELETE"}[t],headers:{"Content-Type":"application/json",[A]:r}};o&&(n.body=JSON.stringify(o));const a=await fetch(`${h}${e}`,n);if(!a.ok){const f=await a.json().catch(()=>({}));throw new Error(f.err||f.message||"An API error occurred")}return a.status===204||!a.body?{}:a.json()}const P=()=>{const{token:e}=l();return d(async t=>{if(!e)throw new Error(u);return await m(T,e,"CREATE",t)})},F=()=>{const{token:e}=l();return d(async({variableId:t,payload:o})=>{if(!e)throw new Error(u);return await m(y(t),e,"UPDATE",o)})},O=()=>{const{token:e}=l();return d(async t=>{if(!e)throw new Error(u);return await m(y(t),e,"DELETE",void 0)})},S=()=>{const{token:e}=l();return d(async t=>{if(!e)throw new Error(u);return await m(T,e,"CREATE",t)})};function D(e,r){return e.filter(t=>{let o=!0;return r.resolvedType&&(o=o&&t.resolvedType===r.resolvedType),r.name&&(o=o&&t.name.includes(r.name)),o})}exports.FigmaVarsProvider=g;exports.filterVariables=D;exports.useBulkUpdateVariables=S;exports.useCreateVariable=P;exports.useDeleteVariable=O;exports.useUpdateVariable=F;exports.useVariableCollections=R;exports.useVariableModes=C;exports.useVariables=E;
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _ } from "react/jsx-runtime";
|
|
2
|
-
import { createContext as I, useContext as v, useMemo as d, useReducer as
|
|
2
|
+
import { createContext as I, useContext as v, useMemo as d, useReducer as g, useCallback as R } from "react";
|
|
3
3
|
import C from "swr";
|
|
4
|
-
const f = I(void 0),
|
|
4
|
+
const f = I(void 0), S = ({
|
|
5
5
|
children: t,
|
|
6
6
|
token: o,
|
|
7
7
|
fileKey: e,
|
|
@@ -14,10 +14,10 @@ const f = I(void 0), N = ({
|
|
|
14
14
|
if (t === void 0)
|
|
15
15
|
throw new Error("useFigmaTokenContext must be used within a FigmaVarsProvider");
|
|
16
16
|
return t;
|
|
17
|
-
}, p = "https://api.figma.com", h = `${p}/v1/variables`, w = h, A = (t) => `${h}/${t}`, b = "application/json", T = "X-FIGMA-TOKEN",
|
|
18
|
-
async function
|
|
17
|
+
}, p = "https://api.figma.com", h = `${p}/v1/variables`, w = h, A = (t) => `${h}/${t}`, b = "application/json", T = "X-FIGMA-TOKEN", i = "A Figma API token is required.", P = "An error occurred while fetching data from the Figma API.";
|
|
18
|
+
async function F(t, o) {
|
|
19
19
|
if (!o)
|
|
20
|
-
throw new Error(
|
|
20
|
+
throw new Error(i);
|
|
21
21
|
const e = await fetch(t, {
|
|
22
22
|
method: "GET",
|
|
23
23
|
headers: {
|
|
@@ -37,12 +37,12 @@ async function M(t, o) {
|
|
|
37
37
|
return e.json();
|
|
38
38
|
}
|
|
39
39
|
const y = () => {
|
|
40
|
-
const { token: t, fileKey: o } = u(), e = t && o ? `https://api.figma.com/v1/files/${o}/variables/local` : null;
|
|
40
|
+
const { token: t, fileKey: o, fallbackFile: e } = u(), r = async (n, a) => e ? typeof e == "string" ? JSON.parse(e) : e : F(n, a), s = t && o ? `https://api.figma.com/v1/files/${o}/variables/local` : null;
|
|
41
41
|
return C(
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
s && t ? [s, t] : null,
|
|
43
|
+
s && t ? ([n, a]) => r(n, a) : () => Promise.resolve(void 0)
|
|
44
44
|
);
|
|
45
|
-
},
|
|
45
|
+
}, V = () => {
|
|
46
46
|
const { data: t } = y(), o = d(
|
|
47
47
|
() => t != null && t.meta ? Object.values(t.meta.variableCollections) : [],
|
|
48
48
|
[t]
|
|
@@ -54,15 +54,15 @@ const y = () => {
|
|
|
54
54
|
collections: o,
|
|
55
55
|
collectionsById: e
|
|
56
56
|
};
|
|
57
|
-
},
|
|
57
|
+
}, k = () => {
|
|
58
58
|
const { data: t } = y();
|
|
59
59
|
return d(() => {
|
|
60
60
|
const o = [], e = {}, r = {};
|
|
61
61
|
if (t != null && t.meta)
|
|
62
62
|
for (const s of Object.values(t.meta.variableCollections)) {
|
|
63
63
|
o.push(...s.modes), e[s.id] = s.modes;
|
|
64
|
-
for (const
|
|
65
|
-
r[
|
|
64
|
+
for (const c of s.modes)
|
|
65
|
+
r[c.modeId] = c;
|
|
66
66
|
}
|
|
67
67
|
return {
|
|
68
68
|
modes: o,
|
|
@@ -88,16 +88,16 @@ const l = (t) => {
|
|
|
88
88
|
status: "idle",
|
|
89
89
|
data: null,
|
|
90
90
|
error: null
|
|
91
|
-
}, [e, r] =
|
|
91
|
+
}, [e, r] = g(O, o);
|
|
92
92
|
return {
|
|
93
|
-
mutate:
|
|
94
|
-
async (
|
|
93
|
+
mutate: R(
|
|
94
|
+
async (c) => {
|
|
95
95
|
r({ type: "loading" });
|
|
96
96
|
try {
|
|
97
|
-
const
|
|
98
|
-
return r({ type: "success", payload:
|
|
99
|
-
} catch (
|
|
100
|
-
r({ type: "error", payload:
|
|
97
|
+
const n = await t(c);
|
|
98
|
+
return r({ type: "success", payload: n }), n;
|
|
99
|
+
} catch (n) {
|
|
100
|
+
r({ type: "error", payload: n });
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
103
|
},
|
|
@@ -111,8 +111,8 @@ const l = (t) => {
|
|
|
111
111
|
};
|
|
112
112
|
async function m(t, o, e, r) {
|
|
113
113
|
if (!o)
|
|
114
|
-
throw new Error(
|
|
115
|
-
const
|
|
114
|
+
throw new Error(i);
|
|
115
|
+
const n = {
|
|
116
116
|
method: {
|
|
117
117
|
CREATE: "POST",
|
|
118
118
|
UPDATE: "PUT",
|
|
@@ -123,19 +123,19 @@ async function m(t, o, e, r) {
|
|
|
123
123
|
[T]: o
|
|
124
124
|
}
|
|
125
125
|
};
|
|
126
|
-
r && (
|
|
127
|
-
const
|
|
128
|
-
if (!
|
|
129
|
-
const E = await
|
|
126
|
+
r && (n.body = JSON.stringify(r));
|
|
127
|
+
const a = await fetch(`${p}${t}`, n);
|
|
128
|
+
if (!a.ok) {
|
|
129
|
+
const E = await a.json().catch(() => ({}));
|
|
130
130
|
throw new Error(E.err || E.message || "An API error occurred");
|
|
131
131
|
}
|
|
132
|
-
return
|
|
132
|
+
return a.status === 204 || !a.body ? {} : a.json();
|
|
133
133
|
}
|
|
134
|
-
const
|
|
134
|
+
const B = () => {
|
|
135
135
|
const { token: t } = u();
|
|
136
136
|
return l(async (e) => {
|
|
137
137
|
if (!t)
|
|
138
|
-
throw new Error(
|
|
138
|
+
throw new Error(i);
|
|
139
139
|
return await m(
|
|
140
140
|
w,
|
|
141
141
|
t,
|
|
@@ -143,12 +143,12 @@ const G = () => {
|
|
|
143
143
|
e
|
|
144
144
|
);
|
|
145
145
|
});
|
|
146
|
-
},
|
|
146
|
+
}, G = () => {
|
|
147
147
|
const { token: t } = u();
|
|
148
148
|
return l(
|
|
149
149
|
async ({ variableId: e, payload: r }) => {
|
|
150
150
|
if (!t)
|
|
151
|
-
throw new Error(
|
|
151
|
+
throw new Error(i);
|
|
152
152
|
return await m(
|
|
153
153
|
A(e),
|
|
154
154
|
t,
|
|
@@ -157,11 +157,11 @@ const G = () => {
|
|
|
157
157
|
);
|
|
158
158
|
}
|
|
159
159
|
);
|
|
160
|
-
},
|
|
160
|
+
}, j = () => {
|
|
161
161
|
const { token: t } = u();
|
|
162
162
|
return l(async (e) => {
|
|
163
163
|
if (!t)
|
|
164
|
-
throw new Error(
|
|
164
|
+
throw new Error(i);
|
|
165
165
|
return await m(
|
|
166
166
|
A(e),
|
|
167
167
|
t,
|
|
@@ -173,7 +173,7 @@ const G = () => {
|
|
|
173
173
|
const { token: t } = u();
|
|
174
174
|
return l(async (e) => {
|
|
175
175
|
if (!t)
|
|
176
|
-
throw new Error(
|
|
176
|
+
throw new Error(i);
|
|
177
177
|
return await m(
|
|
178
178
|
w,
|
|
179
179
|
t,
|
|
@@ -189,13 +189,13 @@ function x(t, o) {
|
|
|
189
189
|
});
|
|
190
190
|
}
|
|
191
191
|
export {
|
|
192
|
-
|
|
192
|
+
S as FigmaVarsProvider,
|
|
193
193
|
x as filterVariables,
|
|
194
194
|
L as useBulkUpdateVariables,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
195
|
+
B as useCreateVariable,
|
|
196
|
+
j as useDeleteVariable,
|
|
197
|
+
G as useUpdateVariable,
|
|
198
|
+
V as useVariableCollections,
|
|
199
|
+
k as useVariableModes,
|
|
200
200
|
y as useVariables
|
|
201
201
|
};
|
package/package.json
CHANGED