Skip to content

Commit

Permalink
Merge pull request #1513 from tokens-studio/release-133
Browse files Browse the repository at this point in the history
Release 133
  • Loading branch information
six7 authored Jan 10, 2023
2 parents 5bf1c1a + 66608a0 commit 80aa200
Show file tree
Hide file tree
Showing 30 changed files with 406 additions and 104 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "tokens-studio-for-figma",
"version": "1.0.0",
"plugin_version": "132",
"plugin_version": "133",
"description": "Tokens Studio for Figma",
"license": "MIT",
"scripts": {
Expand Down Expand Up @@ -50,7 +50,7 @@
"buffer": "^6.0.3",
"case": "^1.6.3",
"classnames": "^2.3.1",
"color2k": "^1.2.4",
"color2k": "^2.0.1",
"core-js": "^3.9.1",
"cypress-react-selector": "^2.3.6",
"dnd-core": "^12.0.1",
Expand Down
2 changes: 1 addition & 1 deletion script/release.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION=figma-tokens@132
VERSION=figma-tokens@133
sentry-cli releases -p figma-tokens files "$VERSION" upload-sourcemaps --ext ts --ext tsx --ext map --ext js --ignore-file .sentryignore .
sentry-cli releases set-commits "$VERSION" --auto
sentry-cli releases finalize "$VERSION"
111 changes: 90 additions & 21 deletions src/app/components/BorderTokenForm.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import React from 'react';
import React, { useState } from 'react';
import { useUIDSeed } from 'react-uid';
import get from 'just-safe-get';
import { TokensIcon, LinkBreak2Icon } from '@radix-ui/react-icons';
import { EditTokenObject } from '@/types/tokens';
import Heading from './Heading';
import { TokenTypes } from '@/constants/TokenTypes';
import { ResolveTokenValuesResult } from '@/plugin/tokenHelpers';
import Stack from './Stack';
import BorderTokenDownShiftInput from './BorderTokenDownShiftInput';
import ColorPicker from './ColorPicker';
import IconButton from './IconButton';
import DownshiftInput from './DownshiftInput';
import ResolvedTokenDisplay from './ResolvedTokenDisplay';
import { checkIfContainsAlias } from '@/utils/alias';
import { findReferences } from '@/utils/findReferences';

const propertyTypes = {
color: TokenTypes.COLOR,
Expand All @@ -20,45 +26,108 @@ export default function BorderTokenForm({
resolvedTokens,
handleBorderValueChange,
handleBorderValueDownShiftInputChange,
handleBorderAliasValueChange,
handleDownShiftInputChange,
}: {
internalEditToken: Extract<EditTokenObject, { type: TokenTypes.BORDER }>;
resolvedTokens: ResolveTokenValuesResult[];
handleBorderValueChange: React.ChangeEventHandler;
handleBorderValueDownShiftInputChange: (newInputValue: string, property: string) => void;
handleBorderAliasValueChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
handleDownShiftInputChange: (newInputValue: string) => void;
}) {
const seed = useUIDSeed();
const [inputHelperOpen, setInputHelperOpen] = React.useState<boolean>(false);
const isAliasMode = (internalEditToken.value && typeof internalEditToken.value === 'string');
const [mode, setMode] = useState(isAliasMode ? 'alias' : 'input');
const [alias, setAlias] = useState('');
const [inputHelperOpen, setInputHelperOpen] = useState<boolean>(false);

const selectedToken = React.useMemo(() => {
const search = findReferences(String(internalEditToken.value));
if (search && search.length > 0) {
const nameToLookFor = search[0].slice(1, search[0].length - 1);
const foundToken = resolvedTokens.find((t) => t.name === nameToLookFor);
if (foundToken) return foundToken;
}
return null;
}, [internalEditToken, resolvedTokens]);

const handleToggleInputHelper = React.useCallback(() => setInputHelperOpen(!inputHelperOpen), [inputHelperOpen]);
const onColorChange = React.useCallback((color: string) => {
handleBorderValueDownShiftInputChange(color, 'color');
}, [handleBorderValueChange]);
}, [handleBorderValueDownShiftInputChange]);

const handleMode = React.useCallback(() => {
const changeMode = (mode === 'input') ? 'alias' : 'input';
setMode(changeMode);
setAlias('');
}, [mode]);

return (
<Stack direction="column" gap={2}>
<Stack direction="row" gap={2} justify="between" align="center">
<Heading>Value</Heading>
</Stack>
<Stack gap={2} direction="column">
{Object.entries(internalEditToken.schema.schemas.value.properties ?? {}).map(([key], keyIndex) => (
<>
<BorderTokenDownShiftInput
name={key}
key={`border-input-${seed(keyIndex)}`}
value={typeof internalEditToken.value === 'object' ? get(internalEditToken.value, key, '') : ''}
type={propertyTypes[key as keyof typeof propertyTypes]}
resolvedTokens={resolvedTokens}
handleChange={handleBorderValueChange}
setInputValue={handleBorderValueDownShiftInputChange}
handleToggleInputHelper={handleToggleInputHelper}
{
mode === 'input' ? (
<IconButton
tooltip="Reference mode"
dataCy="mode-change-button"
onClick={handleMode}
icon={<TokensIcon />}
/>
{inputHelperOpen && key === 'color' && (
) : (
<IconButton
tooltip="Input mode"
dataCy="mode-change-button"
onClick={handleMode}
icon={<LinkBreak2Icon />}
/>
)
}
</Stack>
{(mode === 'input' && internalEditToken.schema.schemas.value.type === 'object') ? (
<Stack gap={2} direction="column">
{Object.entries(internalEditToken.schema.schemas.value.properties ?? {}).map(([key], keyIndex) => (
<>
<BorderTokenDownShiftInput
name={key}
key={`border-input-${seed(keyIndex)}`}
value={typeof internalEditToken.value === 'object' ? get(internalEditToken.value, key, '') : ''}
type={propertyTypes[key as keyof typeof propertyTypes]}
resolvedTokens={resolvedTokens}
handleChange={handleBorderValueChange}
setInputValue={handleBorderValueDownShiftInputChange}
handleToggleInputHelper={handleToggleInputHelper}
/>
{inputHelperOpen && key === 'color' && (
<ColorPicker value={typeof internalEditToken.value === 'object' && get(internalEditToken.value, key, '')} onChange={onColorChange} />
)}
</>
))}
)}
</>
))}
</Stack>
) : (
<Stack direction="column" gap={2}>
<DownshiftInput
value={!isAliasMode ? '' : String(internalEditToken.value)}
type={internalEditToken.type}
label={internalEditToken.schema.property}
inlineLabel
resolvedTokens={resolvedTokens}
initialName={internalEditToken.initialName}
handleChange={handleBorderAliasValueChange}
setInputValue={handleDownShiftInputChange}
placeholder="Value or {alias}"
suffix
/>

</Stack>
{isAliasMode && typeof internalEditToken.value === 'string' && checkIfContainsAlias(internalEditToken.value) && (
<ResolvedTokenDisplay
alias={alias}
selectedToken={selectedToken}
/>
)}
</Stack>
)}
</Stack>
);
}
4 changes: 2 additions & 2 deletions src/app/components/BoxShadowInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ export default function BoxShadowInput({
<Box css={{ display: 'flex' }}>
{mode === 'input' ? (
<IconButton
tooltip="Reference"
tooltip="Reference mode"
dataCy="mode-change-button"
onClick={handleMode}
icon={<TokensIcon />}
/>
) : (
<IconButton
tooltip="input mode"
tooltip="Input mode"
dataCy="mode-change-button"
onClick={handleMode}
icon={<LinkBreak2Icon />}
Expand Down
28 changes: 4 additions & 24 deletions src/app/components/EditTokenForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,6 @@ function EditTokenForm({ resolvedTokens }: Props) {
[internalEditToken],
);

const handleBoxShadowAliasValueChange = React.useCallback<React.ChangeEventHandler<HTMLInputElement>>(
(e) => {
setError(null);
e.persist();
if (internalEditToken) {
setInternalEditToken({ ...internalEditToken, [e.target.name]: e.target.value });
}
},
[internalEditToken],
);

const handleColorValueChange = React.useCallback(
(color: string) => {
setError(null);
Expand All @@ -184,17 +173,6 @@ function EditTokenForm({ resolvedTokens }: Props) {
[internalEditToken],
);

const handleTypographyAliasValueChange = React.useCallback<React.ChangeEventHandler<HTMLInputElement>>(
(e) => {
setError(null);
e.persist();
if (internalEditToken) {
setInternalEditToken({ ...internalEditToken, [e.target.name]: e.target.value });
}
},
[internalEditToken],
);

const handleTypographyValueDownShiftInputChange = React.useCallback((newInputValue: string, property: string) => {
if (internalEditToken?.type === TokenTypes.TYPOGRAPHY && typeof internalEditToken?.value !== 'string') {
setInternalEditToken({
Expand Down Expand Up @@ -384,7 +362,7 @@ function EditTokenForm({ resolvedTokens }: Props) {
return (
<BoxShadowInput
handleBoxShadowValueChange={handleBoxShadowValueChange}
handleBoxShadowAliasValueChange={handleBoxShadowAliasValueChange}
handleBoxShadowAliasValueChange={handleChange}
resolvedTokens={resolvedTokens}
internalEditToken={internalEditToken}
handleDownShiftInputChange={handleDownShiftInputChange}
Expand All @@ -396,7 +374,7 @@ function EditTokenForm({ resolvedTokens }: Props) {
<TypographyInput
internalEditToken={internalEditToken}
handleTypographyValueChange={handleTypographyValueChange}
handleTypographyAliasValueChange={handleTypographyAliasValueChange}
handleTypographyAliasValueChange={handleChange}
resolvedTokens={resolvedTokens}
handleTypographyValueDownShiftInputChange={handleTypographyValueDownShiftInputChange}
handleDownShiftInputChange={handleDownShiftInputChange}
Expand All @@ -419,6 +397,8 @@ function EditTokenForm({ resolvedTokens }: Props) {
resolvedTokens={resolvedTokens}
handleBorderValueChange={handleBorderValueChange}
handleBorderValueDownShiftInputChange={handleBorderValueDownShiftInputChange}
handleBorderAliasValueChange={handleChange}
handleDownShiftInputChange={handleDownShiftInputChange}
/>
);
}
Expand Down
10 changes: 5 additions & 5 deletions src/app/components/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,16 @@ const Input = React.forwardRef<HTMLInputElement, Props>(({
}, ref) => {
// if isMasked is true, then we need to handle toggle visibility
const [show, setShow] = React.useState(false);
const htmlInputRef = React.useRef<HTMLInputElement>(null);
const reifiedRef = inputRef || htmlInputRef;

const handleVisibility = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
setShow(!show);
if (inputRef?.current?.type) {
inputRef.current.type = inputRef?.current?.type === 'password' ? 'text' : 'password';
if (reifiedRef?.current?.type) {
reifiedRef.current.type = reifiedRef?.current?.type === 'password' ? 'text' : 'password';
}
}, [show, inputRef]);

const htmlInputRef = React.useRef<HTMLInputElement>(null);
}, [show, reifiedRef]);

React.useEffect(() => {
if (autofocus && htmlInputRef && htmlInputRef.current) {
Expand Down
21 changes: 21 additions & 0 deletions src/app/components/Link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { styled } from '@/stitches.config';

const StyledLink = styled('a', {
color: '$fgAccent',
textDecoration: 'none',
':hover': {
textDecoration: 'underline',
},
});

type Props = {
href: string
children: React.ReactNode
};

export default function Link({ href, children }: Props) {
return (
<StyledLink href={href} target="_blank" rel="noreferrer">{children}</StyledLink>
);
}
50 changes: 50 additions & 0 deletions src/app/components/ResolvedBorderValueDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import { useUIDSeed } from 'react-uid';
import { SingleBorderToken } from '@/types/tokens';
import { styled } from '@/stitches.config';
import Box from './Box';

type Props = {
value: SingleBorderToken['value']
};

const StyledPropertyItem = styled('div', {
color: '$textSubtle',
marginBottom: '$2',
});

const StyledValueItem = styled('div', {
marginBottom: '$2',
});

const properties = {
color: 'Border Color',
width: 'Border Width',
style: 'Border Style',
};

export const ResolvedBorderValueDisplay: React.FC<Props> = ({ value }) => {
const seed = useUIDSeed();

return (
<Box css={{
display: 'flex', backgroundColor: '$bgSubtle', padding: '$4', fontSize: '$xsmall',
}}
>
<Box css={{ display: 'grid', marginRight: '$6' }}>
{Object.values(properties).map((value) => (
<StyledPropertyItem key={seed(value)}>{value}</StyledPropertyItem>
))}

</Box>
<Box>
{Object.keys(properties).map((key) => (
<StyledValueItem key={seed(key)}>
{value[key as keyof typeof value]}
&nbsp;
</StyledValueItem>
))}
</Box>
</Box>
);
};
13 changes: 11 additions & 2 deletions src/app/components/ResolvedTokenDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react';
import { ResolveTokenValuesResult } from '@/plugin/tokenHelpers';
import { isSingleBoxShadowToken, isSingleTypographyToken } from '@/utils/is';
import { SingleTypographyToken } from '@/types/tokens';
import { isSingleBorderToken, isSingleBoxShadowToken, isSingleTypographyToken } from '@/utils/is';
import { SingleBorderToken, SingleTypographyToken } from '@/types/tokens';
import Box from './Box';
import { ResolvedShadowValueDisplay } from './ResolvedShadowValueDisplay';
import { ResolvedTypographyValueDisplay } from './ResolvedTypographyValueDisplay';
import { TokenBoxshadowValue } from '@/types/values';
import { ResolvedBorderValueDisplay } from './ResolvedBorderValueDisplay';

export default function ResolvedTokenDisplay({
alias,
Expand All @@ -31,6 +32,14 @@ export default function ResolvedTokenDisplay({
return <ResolvedShadowValueDisplay shadows={[valueToCheck as TokenBoxshadowValue]} />;
}

if (selectedToken && isSingleBorderToken(selectedToken)) {
return (
<ResolvedBorderValueDisplay
value={valueToCheck as SingleBorderToken['value']}
/>
);
}

if (typeof valueToCheck !== 'string' && typeof valueToCheck !== 'number') {
return <div>{JSON.stringify(valueToCheck, null, 2)}</div>;
}
Expand Down
Loading

0 comments on commit 80aa200

Please sign in to comment.