Skip to content

Commit

Permalink
feat(core): added fontSize and basic implementation of textStyles
Browse files Browse the repository at this point in the history
started adding font-related slices, added a basic implementation of a dropdown

wip #22
  • Loading branch information
gabrieleAngius committed Jun 21, 2023
1 parent dea53c6 commit a27d854
Show file tree
Hide file tree
Showing 21 changed files with 638 additions and 17 deletions.
21 changes: 13 additions & 8 deletions widget-src/components/Buttons/DeleteButton.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
const { widget } = figma;
const { Frame, SVG } = widget;
const { AutoLayout, SVG } = widget;

export function DeleteButton(props: { onClick: () => void }) {
type Props = {
width?: number;
height?: number;
} & Omit<FrameProps, "width" | "height">;

export function DeleteButton({ width = 41, height = 41, ...props }: Props) {
return (
<Frame
width={41}
height={41}
<AutoLayout
width={width}
height={height}
name="DeleteButton"
opacity={0}
hoverStyle={{ opacity: 1 }}
Expand All @@ -15,11 +20,11 @@ export function DeleteButton(props: { onClick: () => void }) {
visible: true,
}}
tooltip="Delete this variant and all related component instances"
horizontalAlignItems="center"
verticalAlignItems="center"
{...props}
>
<SVG
x={10}
y={10}
src={`
<svg width="20" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 10V15" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
Expand All @@ -30,6 +35,6 @@ export function DeleteButton(props: { onClick: () => void }) {
</svg>
`}
/>
</Frame>
</AutoLayout>
);
}
88 changes: 88 additions & 0 deletions widget-src/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { StateKeys } from "../../constants";
import { DropdownItem } from "./DropdownItem";
import { DropdownItemModel, DropdownSetItem } from "./types";
const { widget } = figma;
const { SVG, Text, AutoLayout, useSyncedState } = widget;

type Props = {
label: string;
items: DropdownItemModel[];
selectedId: string;
setNewItem: DropdownSetItem;
};

export const Dropdown = ({ label, items, selectedId, setNewItem }: Props) => {
const svg = `
<svg width='7' height='7' viewBox='0 0 7 7' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M3.5 7L0.468911 1.75L6.53109 1.75L3.5 7Z' fill='black'/>
</svg>
`;

const [isVisible, setIsVisible] = useSyncedState(
StateKeys.IsDropdownVisible,
false
);

const selected = items.find((item) => item.id === selectedId);
const filteredItems = items.filter((item) => item.id !== selectedId);

return (
<AutoLayout
name="Dropdown"
overflow="visible"
direction="vertical"
spacing={5}
verticalAlignItems="start"
onClick={() => setIsVisible((prev) => !prev)}
>
<Text
name="Label"
fill="#000"
fontFamily="Inter"
fontSize={13}
fontWeight={700}
>
{label}
</Text>
<AutoLayout
name="Frame 4"
fill="#FFF"
stroke="#000"
cornerRadius={4}
strokeWidth={0.5}
spacing={10}
padding={{
vertical: 4,
horizontal: 6,
}}
verticalAlignItems="center"
>
<Text
name="S"
fill="#000"
fontFamily="Inter"
fontSize={11}
fontWeight={500}
>
{selected?.name}
</Text>
<SVG name="Icon" height={7} width={7} src={svg} />
</AutoLayout>
<AutoLayout
verticalAlignItems="center"
fill="#FFF"
stroke="#000"
cornerRadius={4}
strokeWidth={0.5}
width="fill-parent"
direction="vertical"
y={50}
hidden={!isVisible}
>
{filteredItems.map((item) => (
<DropdownItem key={item.id} {...item} setNewItem={setNewItem} />
))}
</AutoLayout>
</AutoLayout>
);
};
33 changes: 33 additions & 0 deletions widget-src/components/Dropdown/DropdownItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { DropdownItemModel, DropdownSetItem } from "./types";

const { widget } = figma;
const { Text, AutoLayout } = widget;

interface Props extends DropdownItemModel {
setNewItem: DropdownSetItem;
}

export const DropdownItem = ({ name, id, setNewItem }: Props) => {
return (
<AutoLayout
hoverStyle={{ fill: { r: 0.5, g: 0.5, b: 0.5, a: 0.2 } }}
onClick={() => setNewItem(id)}
width="fill-parent"
padding={{
vertical: 4,
horizontal: 6,
}}
fill="#FFF"
>
<Text
name="S"
fill="#000"
fontFamily="Inter"
fontSize={11}
fontWeight={500}
>
{name}
</Text>
</AutoLayout>
);
};
6 changes: 6 additions & 0 deletions widget-src/components/Dropdown/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface DropdownItemModel {
id: string;
name: string;
}

export type DropdownSetItem = (id: string) => void;
108 changes: 108 additions & 0 deletions widget-src/components/FontSizes/FontSizeSlice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { FontSliceItem, Slice, Store } from "../../types";
import { DeleteButton } from "../Buttons/DeleteButton";
import { deleteFontSize } from "./deleteFontSize";

const { widget } = figma;
const { Frame, Text, Input, AutoLayout, Rectangle } = widget;

interface Props extends FontSliceItem {
store: Store;
}

export const FontSizeSlice = ({ store, ...slice }: Props) => {
const { [Slice.FontSizes]: fontSizesMap, [Slice.TextStyles]: textStylesMap } =
store;
const boxSize = slice.value > 41 ? slice.value : 41;
const deleteSlice = () =>
deleteFontSize({ id: slice.id, fontSizesMap, textStylesMap });
return (
<AutoLayout
name="Font size Slice"
strokeWidth={0}
overflow="visible"
direction="horizontal"
verticalAlignItems="center"
spacing={8}
key={slice.id}
>
<Frame width={boxSize} height={boxSize}>
<AutoLayout
height={boxSize}
width={boxSize}
direction="horizontal"
spacing={2}
horizontalAlignItems="center"
verticalAlignItems="center"
>
<Text
name="A"
fill="#000"
width="hug-contents"
height="hug-contents"
fontFamily="Inter"
fontSize={slice.value}
>
A
</Text>
<AutoLayout
name="Frame6"
strokeWidth={0.5}
direction="vertical"
width={slice.value / 5}
height={slice.value}
verticalAlignItems="center"
horizontalAlignItems="center"
>
<Rectangle
name="Rectangle 25"
fill="#DD00E1"
width="fill-parent"
height={1}
/>
<Rectangle
name="Rectangle 23"
fill="#DD00E1"
width={1}
height="fill-parent"
/>
<Rectangle
name="Rectangle 25"
fill="#DD00E1"
width="fill-parent"
height={1}
/>
</AutoLayout>
</AutoLayout>
<DeleteButton onClick={deleteSlice} width={boxSize} height={boxSize} />
</Frame>
<AutoLayout direction="vertical" spacing={2}>
<Input
name={slice.name}
x={48}
y={2}
fill="#000"
fontFamily="Inter"
fontWeight={700}
value={slice.name}
width={40}
inputBehavior="truncate"
inputFrameProps={{ direction: "horizontal" }}
onTextEditEnd={() => {}}
/>
<Input
width="fill-parent"
name="Font size value"
x={48}
y={21}
fill="#000"
fontFamily="Inter"
fontSize={10}
fontWeight={500}
strokeWidth={1}
value={`${slice.value}`}
onTextEditEnd={() => {}}
/>
</AutoLayout>
</AutoLayout>
);
};
56 changes: 56 additions & 0 deletions widget-src/components/FontSizes/FontSizeSlices.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Slice, Store } from "../../types";
import { AddButton } from "../Buttons/AddButton";
import { FontSizeSlice } from "./FontSizeSlice";
const { widget } = figma;
const { Text, AutoLayout } = widget;

type Props = {
store: Store;
};

export const FontSizeSlices = ({ store }: Props) => {
const add = () => {};
return (
<AutoLayout
name="Font sizes frame"
strokeWidth={0}
overflow="visible"
direction="vertical"
spacing={30}
>
<AutoLayout
verticalAlignItems="center"
spacing={10}
name="Group 10"
strokeWidth={0}
overflow="visible"
width={102}
height={35}
>
<Text
name="Font sizes"
fill="#000"
fontFamily="Inter"
fontSize={28}
fontWeight={500}
strokeWidth={2}
>
Font sizes
</Text>
<AddButton onClick={add} />
</AutoLayout>
<AutoLayout
name="Frame 2"
y={65}
overflow="visible"
spacing={40}
direction="horizontal"
verticalAlignItems="center"
>
{store[Slice.FontSizes].values().map((fontSize) => (
<FontSizeSlice {...fontSize} store={store} />
))}
</AutoLayout>
</AutoLayout>
);
};
42 changes: 42 additions & 0 deletions widget-src/components/FontSizes/deleteFontSize.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { mockSyncedMap } from "../../test-utils/mockSyncedMap";
import { FontSliceItem, TextStyleSliceItem } from "../../types";
import { deleteFontSize } from "./deleteFontSize";

describe("deleteFontSize", () => {
it("should delete a fontSize and replace the reference on textStyles when is used in one of them", () => {
const fontSizesMap = mockSyncedMap<FontSliceItem>({
"font-size-1": { id: "font-size-1", name: "any name", value: 10 },
"font-size-2": { id: "font-size-2", name: "any name", value: 20 },
});
const textStylesMap = mockSyncedMap<TextStyleSliceItem>({
"text-style-1": {
id: "text-style-1",
name: "any name",
fontSizeId: "font-size-1",
},
});
deleteFontSize({ id: "font-size-1", fontSizesMap, textStylesMap });

expect(fontSizesMap.delete).toBeCalledTimes(1);
expect(fontSizesMap.delete).toBeCalledWith("font-size-1");
expect(textStylesMap.set).toBeCalledWith("text-style-1", {
id: "text-style-1",
name: "any name",
fontSizeId: "font-size-2",
});
});
it("should prevent the user to delete all slices", () => {
const fontSizesMap = mockSyncedMap<FontSliceItem>({
"font-size-1": { id: "font-size-1", name: "any name", value: 10 },
});
const textStylesMap = mockSyncedMap<TextStyleSliceItem>();
deleteFontSize({ id: "font-size-1", fontSizesMap, textStylesMap });

expect(fontSizesMap.delete).not.toBeCalled();
expect(figma.notify).toBeCalledTimes(1);
expect(figma.notify).toBeCalledWith(
expect.any(String),
expect.objectContaining({ error: true })
);
});
});
Loading

0 comments on commit a27d854

Please sign in to comment.