Skip to content

Commit c513d80

Browse files
Merge branch 'main' into akshay/update-free-trial-link
2 parents ac976b3 + c3f4d24 commit c513d80

18 files changed

Lines changed: 461 additions & 68 deletions

.changeset/four-ducks-do.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@tokens-studio/figma-plugin": patch
3+
---
4+
5+
Variable scoping enhancements -
6+
7+
- Add support for hiddenFromPublishing
8+
- Add support for selecting no scopes in a token
9+
- Fix font weight token scoping bug when weight is set to a string

packages/tokens-studio-for-figma/src/app/components/EditTokenForm.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ function EditTokenForm({ resolvedTokens }: Props) {
352352
const newExtensions = { ...internalEditToken.$extensions };
353353
delete newExtensions['com.figma.scopes'];
354354
delete newExtensions['com.figma.codeSyntax'];
355+
delete newExtensions['com.figma.hiddenFromPublishing'];
355356
// Keep variableId and isOverride if they exist
356357
setInternalEditToken({
357358
...internalEditToken,
@@ -360,7 +361,7 @@ function EditTokenForm({ resolvedTokens }: Props) {
360361
}, [internalEditToken]);
361362

362363
const handleFigmaVariableChange = React.useCallback(
363-
(scopes: string[], codeSyntax: Partial<Record<string, string>>) => {
364+
(scopes: string[], codeSyntax: Partial<Record<string, string>>, hiddenFromPublishing?: boolean) => {
364365
const newExtensions = { ...internalEditToken.$extensions };
365366

366367
// Set or remove scopes in flat format
@@ -377,6 +378,12 @@ function EditTokenForm({ resolvedTokens }: Props) {
377378
delete newExtensions['com.figma.codeSyntax'];
378379
}
379380

381+
if (typeof hiddenFromPublishing === 'boolean') {
382+
newExtensions['com.figma.hiddenFromPublishing'] = hiddenFromPublishing;
383+
} else {
384+
delete newExtensions['com.figma.hiddenFromPublishing'];
385+
}
386+
380387
setInternalEditToken({
381388
...internalEditToken,
382389
$extensions: newExtensions as SingleToken['$extensions'],

packages/tokens-studio-for-figma/src/app/components/FigmaVariableForm.tsx

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import React, { useCallback, useMemo } from 'react';
2+
import { useTranslation } from 'react-i18next';
23
import {
34
IconButton, Heading, Checkbox, Text, Stack, Label,
45
} from '@tokens-studio/ui';
6+
import Tooltip from './Tooltip';
57
import IconPlus from '@/icons/plus.svg';
68
import IconMinus from '@/icons/minus.svg';
79
import { EditTokenObject } from '@/types/tokens';
@@ -13,22 +15,26 @@ import { VariableScope, CodeSyntaxPlatform } from '@/types/tokens';
1315
import {
1416
VARIABLE_SCOPE_OPTIONS, TOKEN_TYPE_TO_SCOPES_MAP, CODE_SYNTAX_PLATFORM_OPTIONS,
1517
} from '@/constants/FigmaVariableMetaData';
18+
import { track } from '@/utils/analytics';
1619

1720
export default function FigmaVariableForm({
1821
internalEditToken,
1922
handleFigmaVariableChange,
2023
handleRemoveFigmaVariable,
2124
}: {
2225
internalEditToken: EditTokenObject;
23-
handleFigmaVariableChange: (scopes: VariableScope[], codeSyntax: Partial<Record<CodeSyntaxPlatform, string>>) => void;
26+
handleFigmaVariableChange: (scopes: VariableScope[], codeSyntax: Partial<Record<CodeSyntaxPlatform, string>>, hiddenFromPublishing?: boolean) => void;
2427
handleRemoveFigmaVariable: () => void;
2528
}) {
29+
const { t } = useTranslation(['tokens']);
2630
const [figmaVariableVisible, setFigmaVariableVisible] = React.useState(false);
2731

2832
const currentScopes = useMemo(() => internalEditToken?.$extensions?.['com.figma.scopes'] as VariableScope[] || [], [internalEditToken]);
2933

3034
const currentCodeSyntax = useMemo(() => internalEditToken?.$extensions?.['com.figma.codeSyntax'] as Partial<Record<CodeSyntaxPlatform, string>> || {}, [internalEditToken]);
3135

36+
const currentHiddenFromPublishing = useMemo(() => internalEditToken?.$extensions?.['com.figma.hiddenFromPublishing'] as boolean | undefined, [internalEditToken]);
37+
3238
const shouldShowFigmaVariableSection = useMemo(() => tokenTypesToCreateVariable.includes(internalEditToken.type), [internalEditToken.type]);
3339

3440
// Filter variable scope options based on token type
@@ -38,14 +44,14 @@ export default function FigmaVariableForm({
3844
}, [internalEditToken.type]);
3945

4046
React.useEffect(() => {
41-
if (internalEditToken?.$extensions?.['com.figma.scopes'] || internalEditToken?.$extensions?.['com.figma.codeSyntax']) {
47+
if (internalEditToken?.$extensions?.['com.figma.scopes'] || internalEditToken?.$extensions?.['com.figma.codeSyntax'] || typeof internalEditToken?.$extensions?.['com.figma.hiddenFromPublishing'] === 'boolean') {
4248
setFigmaVariableVisible(true);
4349
}
4450
}, [internalEditToken]);
4551

4652
const addFigmaVariable = useCallback(() => {
4753
setFigmaVariableVisible(true);
48-
handleFigmaVariableChange([], {});
54+
handleFigmaVariableChange([], {}, undefined);
4955
}, [handleFigmaVariableChange]);
5056

5157
const removeFigmaVariable = useCallback(() => {
@@ -55,21 +61,25 @@ export default function FigmaVariableForm({
5561

5662
const handleScopeChange = useCallback((scope: VariableScope, checked: boolean) => {
5763
let newScopes: VariableScope[];
64+
track('Select variable scope', { scope, checked });
5865

5966
if (scope === 'ALL_SCOPES') {
6067
// If selecting ALL_SCOPES, clear all other scopes
61-
// If deselecting ALL_SCOPES, keep other scopes as they are
62-
newScopes = checked ? ['ALL_SCOPES'] : currentScopes.filter((s) => s !== 'ALL_SCOPES');
68+
newScopes = checked ? ['ALL_SCOPES'] : [];
69+
} else if (scope === 'NONE') {
70+
// If selecting NONE, clear all other scopes
71+
newScopes = checked ? ['NONE'] : [];
6372
} else if (checked) {
64-
// If selecting any other scope, add it and remove ALL_SCOPES
65-
newScopes = [...currentScopes.filter((s) => s !== 'ALL_SCOPES'), scope];
73+
// If selecting any other scope, add it and remove ALL_SCOPES and NONE
74+
newScopes = [...currentScopes.filter((s) => s !== 'ALL_SCOPES' && s !== 'NONE'), scope];
6675
} else {
6776
// If deselecting, just remove the specific scope
6877
newScopes = currentScopes.filter((s) => s !== scope);
6978
}
7079

71-
handleFigmaVariableChange(newScopes, currentCodeSyntax);
72-
}, [currentScopes, currentCodeSyntax, handleFigmaVariableChange]);
80+
handleFigmaVariableChange(newScopes, currentCodeSyntax, currentHiddenFromPublishing);
81+
}, [currentScopes, currentCodeSyntax, currentHiddenFromPublishing, handleFigmaVariableChange]);
82+
7383

7484
const handleCodeSyntaxPlatformChange = useCallback((platform: CodeSyntaxPlatform, checked: boolean) => {
7585
const newCodeSyntax = { ...currentCodeSyntax };
@@ -78,14 +88,14 @@ export default function FigmaVariableForm({
7888
} else {
7989
delete newCodeSyntax[platform];
8090
}
81-
handleFigmaVariableChange(currentScopes, newCodeSyntax);
82-
}, [currentScopes, currentCodeSyntax, handleFigmaVariableChange]);
91+
handleFigmaVariableChange(currentScopes, newCodeSyntax, currentHiddenFromPublishing);
92+
}, [currentScopes, currentCodeSyntax, currentHiddenFromPublishing, handleFigmaVariableChange]);
8393

8494
const handleCodeSyntaxValueChange = useCallback((platform: CodeSyntaxPlatform, value: string) => {
8595
const newCodeSyntax = { ...currentCodeSyntax };
8696
newCodeSyntax[platform] = value;
87-
handleFigmaVariableChange(currentScopes, newCodeSyntax);
88-
}, [currentScopes, currentCodeSyntax, handleFigmaVariableChange]);
97+
handleFigmaVariableChange(currentScopes, newCodeSyntax, currentHiddenFromPublishing);
98+
}, [currentScopes, currentCodeSyntax, currentHiddenFromPublishing, handleFigmaVariableChange]);
8999

90100
const handleScopeCheckedChange = useCallback((scope: VariableScope) => (checked: boolean | string) => {
91101
handleScopeChange(scope, !!checked);
@@ -99,6 +109,29 @@ export default function FigmaVariableForm({
99109
handleCodeSyntaxValueChange(platform, e.target.value);
100110
}, [handleCodeSyntaxValueChange]);
101111

112+
// Three-state cycle: indeterminate (undefined) → true → false → indeterminate (undefined)
113+
const handleHiddenFromPublishingCheckedChange = useCallback(() => {
114+
let nextValue: boolean | undefined;
115+
if (currentHiddenFromPublishing === undefined) {
116+
nextValue = true;
117+
} else if (currentHiddenFromPublishing === true) {
118+
nextValue = false;
119+
} else {
120+
nextValue = undefined; // back to indeterminate — removes the extension key
121+
}
122+
track('Set hidden from publishing', { checked: nextValue });
123+
handleFigmaVariableChange(currentScopes, currentCodeSyntax, nextValue);
124+
}, [currentHiddenFromPublishing, currentScopes, currentCodeSyntax, handleFigmaVariableChange]);
125+
126+
let hiddenFromPublishingTooltip: string;
127+
if (currentHiddenFromPublishing === true) {
128+
hiddenFromPublishingTooltip = t('figmaVariable.hiddenFromPublishing.tooltipOn');
129+
} else if (currentHiddenFromPublishing === false) {
130+
hiddenFromPublishingTooltip = t('figmaVariable.hiddenFromPublishing.tooltipOff');
131+
} else {
132+
hiddenFromPublishingTooltip = t('figmaVariable.hiddenFromPublishing.tooltipNotSet');
133+
}
134+
102135
if (!shouldShowFigmaVariableSection) {
103136
return null;
104137
}
@@ -163,6 +196,29 @@ export default function FigmaVariableForm({
163196
</Box>
164197
)}
165198

199+
<Box>
200+
<Text muted size="small" css={{ marginBottom: '$2' }}>
201+
Variable settings
202+
</Text>
203+
<Box css={{ display: 'inline-flex', alignItems: 'center', gap: '$2' }}>
204+
<Tooltip
205+
label={hiddenFromPublishingTooltip}
206+
side="top"
207+
>
208+
<span>
209+
<Checkbox
210+
id="hiddenFromPublishing"
211+
checked={currentHiddenFromPublishing === undefined ? 'indeterminate' : currentHiddenFromPublishing}
212+
onCheckedChange={handleHiddenFromPublishingCheckedChange}
213+
/>
214+
</span>
215+
</Tooltip>
216+
<Label css={{ fontWeight: '$sansRegular', fontSize: '$small' }} htmlFor="hiddenFromPublishing">
217+
{t('figmaVariable.hiddenFromPublishing.label')}
218+
</Label>
219+
</Box>
220+
</Box>
221+
166222
<Box>
167223
<Text muted size="small" css={{ marginBottom: '$2' }}>
168224
Code syntax

packages/tokens-studio-for-figma/src/constants/FigmaVariableMetaData.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { TokenTypes } from './TokenTypes';
22
import { VariableScope, CodeSyntaxPlatform } from '@/types/tokens';
33

44
export const VARIABLE_SCOPE_OPTIONS: { value: VariableScope; label: string }[] = [
5+
{ value: 'NONE', label: 'None' },
56
{ value: 'ALL_SCOPES', label: 'Show in all supported properties' },
67
{ value: 'TEXT_CONTENT', label: 'Text content' },
78
{ value: 'CORNER_RADIUS', label: 'Corner radius' },
@@ -28,53 +29,53 @@ export const VARIABLE_SCOPE_OPTIONS: { value: VariableScope; label: string }[] =
2829

2930
export const TOKEN_TYPE_TO_SCOPES_MAP: Record<string, VariableScope[]> = {
3031
[TokenTypes.COLOR]: [
31-
'ALL_SCOPES', 'ALL_FILLS', 'FRAME_FILL', 'SHAPE_FILL', 'TEXT_FILL', 'STROKE_COLOR', 'EFFECT_COLOR',
32+
'NONE', 'ALL_SCOPES', 'ALL_FILLS', 'FRAME_FILL', 'SHAPE_FILL', 'TEXT_FILL', 'STROKE_COLOR', 'EFFECT_COLOR',
3233
],
3334
[TokenTypes.BORDER_RADIUS]: [
34-
'ALL_SCOPES', 'CORNER_RADIUS',
35+
'NONE', 'ALL_SCOPES', 'CORNER_RADIUS',
3536
],
3637
[TokenTypes.SIZING]: [
37-
'ALL_SCOPES', 'WIDTH_HEIGHT', 'GAP',
38+
'NONE', 'ALL_SCOPES', 'WIDTH_HEIGHT', 'GAP',
3839
],
3940
[TokenTypes.SPACING]: [
40-
'ALL_SCOPES', 'WIDTH_HEIGHT', 'GAP',
41+
'NONE', 'ALL_SCOPES', 'WIDTH_HEIGHT', 'GAP',
4142
],
4243
[TokenTypes.DIMENSION]: [
43-
'ALL_SCOPES', 'WIDTH_HEIGHT', 'GAP', 'CORNER_RADIUS',
44+
'NONE', 'ALL_SCOPES', 'WIDTH_HEIGHT', 'GAP', 'CORNER_RADIUS',
4445
],
4546
[TokenTypes.BORDER_WIDTH]: [
46-
'ALL_SCOPES', 'STROKE_FLOAT',
47+
'NONE', 'ALL_SCOPES', 'STROKE_FLOAT',
4748
],
4849
[TokenTypes.OPACITY]: [
49-
'ALL_SCOPES', 'OPACITY',
50+
'NONE', 'ALL_SCOPES', 'OPACITY',
5051
],
5152
[TokenTypes.FONT_FAMILIES]: [
52-
'ALL_SCOPES', 'FONT_FAMILY',
53+
'NONE', 'ALL_SCOPES', 'FONT_FAMILY',
5354
],
5455
[TokenTypes.FONT_WEIGHTS]: [
55-
'ALL_SCOPES', 'FONT_WEIGHT',
56+
'NONE', 'ALL_SCOPES', 'FONT_WEIGHT',
5657
],
5758
[TokenTypes.FONT_SIZES]: [
58-
'ALL_SCOPES', 'FONT_SIZE',
59+
'NONE', 'ALL_SCOPES', 'FONT_SIZE',
5960
],
6061
[TokenTypes.LINE_HEIGHTS]: [
61-
'ALL_SCOPES', 'LINE_HEIGHT',
62+
'NONE', 'ALL_SCOPES', 'LINE_HEIGHT',
6263
],
6364
[TokenTypes.LETTER_SPACING]: [
64-
'ALL_SCOPES', 'LETTER_SPACING',
65+
'NONE', 'ALL_SCOPES', 'LETTER_SPACING',
6566
],
6667
[TokenTypes.PARAGRAPH_SPACING]: [
67-
'ALL_SCOPES', 'PARAGRAPH_SPACING',
68+
'NONE', 'ALL_SCOPES', 'PARAGRAPH_SPACING',
6869
],
6970
[TokenTypes.PARAGRAPH_INDENT]: [
70-
'ALL_SCOPES', 'PARAGRAPH_INDENT',
71+
'NONE', 'ALL_SCOPES', 'PARAGRAPH_INDENT',
7172
],
7273
[TokenTypes.TEXT]: [
73-
'ALL_SCOPES', 'TEXT_CONTENT', 'FONT_FAMILY', 'FONT_STYLE',
74+
'NONE', 'ALL_SCOPES', 'TEXT_CONTENT', 'FONT_FAMILY', 'FONT_STYLE', 'FONT_WEIGHT',
7475
],
75-
[TokenTypes.BOOLEAN]: [],
76+
[TokenTypes.BOOLEAN]: ['NONE'],
7677
[TokenTypes.NUMBER]: [
77-
'ALL_SCOPES', 'TEXT_CONTENT', 'WIDTH_HEIGHT', 'GAP', 'CORNER_RADIUS', 'STROKE_FLOAT', 'EFFECT_FLOAT', 'OPACITY', 'FONT_WEIGHT', 'FONT_SIZE', 'LINE_HEIGHT', 'LETTER_SPACING', 'PARAGRAPH_SPACING', 'PARAGRAPH_INDENT',
78+
'NONE', 'ALL_SCOPES', 'TEXT_CONTENT', 'WIDTH_HEIGHT', 'GAP', 'CORNER_RADIUS', 'STROKE_FLOAT', 'EFFECT_FLOAT', 'OPACITY', 'FONT_WEIGHT', 'FONT_SIZE', 'LINE_HEIGHT', 'LETTER_SPACING', 'PARAGRAPH_SPACING', 'PARAGRAPH_INDENT',
7879
],
7980
};
8081

packages/tokens-studio-for-figma/src/i18n/lang/en/tokens.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,5 +246,13 @@
246246
"generateDocumentationDescription": "Generate documentation is a Pro feature that creates visual token documentation layers directly on your Figma canvas from your design tokens.",
247247
"all": "All",
248248
"applyTokensToCreatedLayers": "Apply active theme values",
249-
"clearSearch": "Clear search"
250-
}
249+
"clearSearch": "Clear search",
250+
"figmaVariable": {
251+
"hiddenFromPublishing": {
252+
"label": "Hidden from publishing",
253+
"tooltipOn": "Will be hidden in published libraries",
254+
"tooltipOff": "Will be visible in published libraries",
255+
"tooltipNotSet": "Current Figma setting will be preserved"
256+
}
257+
}
258+
}

packages/tokens-studio-for-figma/src/i18n/lang/es/tokens.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,5 +255,13 @@
255255
"generateDocumentationDescription": "Generar documentación es una función Pro que crea capas de documentación visual de tokens directamente en su lienzo de Figma a partir de sus tokens de diseño.",
256256
"all": "Todos",
257257
"applyTokensToCreatedLayers": "Aplicar valores del tema activo",
258-
"clearSearch": "Limpiar búsqueda"
259-
}
258+
"clearSearch": "Limpiar búsqueda",
259+
"figmaVariable": {
260+
"hiddenFromPublishing": {
261+
"label": "Oculto de la publicación",
262+
"tooltipOn": "Se ocultará en las bibliotecas publicadas",
263+
"tooltipOff": "Será visible en las bibliotecas publicadas",
264+
"tooltipNotSet": "Se conservará la configuración actual de Figma"
265+
}
266+
}
267+
}

packages/tokens-studio-for-figma/src/i18n/lang/fr/tokens.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,5 +255,13 @@
255255
"generateDocumentationDescription": "Générer la documentation est une fonctionnalité Pro qui crée des couches de documentation visuelle des jetons directement sur votre canevas Figma à partir de vos jetons de conception.",
256256
"all": "Tous",
257257
"applyTokensToCreatedLayers": "Appliquer les valeurs du thème actif",
258-
"clearSearch": "Effacer la recherche"
259-
}
258+
"clearSearch": "Effacer la recherche",
259+
"figmaVariable": {
260+
"hiddenFromPublishing": {
261+
"label": "Masqué de la publication",
262+
"tooltipOn": "Sera masqué dans les bibliothèques publiées",
263+
"tooltipOff": "Sera visible dans les bibliothèques publiées",
264+
"tooltipNotSet": "Le paramètre Figma actuel sera conservé"
265+
}
266+
}
267+
}

packages/tokens-studio-for-figma/src/i18n/lang/hi/tokens.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,5 +255,13 @@
255255
"generateDocumentationDescription": "दस्तावेज़ीकरण उत्पन्न करना एक प्रो सुविधा है जो आपके डिज़ाइन टोकन से सीधे आपके Figma कैनवास पर दृश्य टोकन दस्तावेज़ीकरण परतें बनाती है।",
256256
"all": "सभी",
257257
"applyTokensToCreatedLayers": "सक्रिय थीम मान लागू करें",
258-
"clearSearch": "खोज साफ़ करें"
259-
}
258+
"clearSearch": "खोज साफ़ करें",
259+
"figmaVariable": {
260+
"hiddenFromPublishing": {
261+
"label": "प्रकाशन से छिपाएं",
262+
"tooltipOn": "प्रकाशित लाइब्रेरी में छिपा होगा",
263+
"tooltipOff": "प्रकाशित लाइब्रेरी में दिखाई देगा",
264+
"tooltipNotSet": "Figma की वर्तमान सेटिंग बनाए रखी जाएगी"
265+
}
266+
}
267+
}

packages/tokens-studio-for-figma/src/i18n/lang/nl/tokens.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,5 +255,13 @@
255255
"generateDocumentationDescription": "Documentatie genereren is een Pro-functie die visuele token documentatie lagen direct op uw Figma canvas creëert vanuit uw ontwerp tokens.",
256256
"all": "Alle",
257257
"applyTokensToCreatedLayers": "Pas actieve thema waarden toe",
258-
"clearSearch": "Zoekopdracht wissen"
259-
}
258+
"clearSearch": "Zoekopdracht wissen",
259+
"figmaVariable": {
260+
"hiddenFromPublishing": {
261+
"label": "Verborgen voor publicatie",
262+
"tooltipOn": "Wordt verborgen in gepubliceerde bibliotheken",
263+
"tooltipOff": "Wordt zichtbaar in gepubliceerde bibliotheken",
264+
"tooltipNotSet": "De huidige Figma-instelling wordt behouden"
265+
}
266+
}
267+
}

packages/tokens-studio-for-figma/src/i18n/lang/zh/tokens.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,5 +259,13 @@
259259
"generateDocumentationDescription": "生成文档是一项专业版功能,可以直接在您的 Figma 画布上从设计令牌创建可视化令牌文档图层。",
260260
"all": "全部",
261261
"applyTokensToCreatedLayers": "应用当前主题值",
262-
"clearSearch": "清除搜索"
263-
}
262+
"clearSearch": "清除搜索",
263+
"figmaVariable": {
264+
"hiddenFromPublishing": {
265+
"label": "从发布中隐藏",
266+
"tooltipOn": "将在已发布的库中隐藏",
267+
"tooltipOff": "将在已发布的库中可见",
268+
"tooltipNotSet": "将保留当前的 Figma 设置"
269+
}
270+
}
271+
}

0 commit comments

Comments
 (0)