From 765ae7b2980c793d7f88cebb1011da8d50fc8109 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 13 Apr 2024 12:33:07 -0600 Subject: [PATCH] Add 80+ additional icons from FontAwesome (#6803) * Add 80+ additional icons from FontAwesome * fix: color switch transition, add 6 more icons to fill out grid * Add strict validation for collection icon * fix: Avoid import from app in server --- app/components/Collection/CollectionForm.tsx | 2 +- app/components/DocumentCard.tsx | 2 +- app/components/Flex.tsx | 37 +-- app/components/IconPicker.tsx | 125 +++++---- app/components/Icons/CollectionIcon.tsx | 2 +- app/components/Sharing/OtherAccess.tsx | 2 +- app/components/Sharing/PublicAccess.tsx | 2 +- app/components/Text.ts | 6 +- package.json | 3 + .../api/collections/collections.test.ts | 15 ++ server/routes/api/collections/schema.ts | 25 +- server/validation.ts | 4 - shared/components/Flex.tsx | 38 +++ .../components}/LetterIcon.tsx | 4 +- {app => shared}/components/Squircle.tsx | 0 shared/i18n/locales/en_US/translation.json | 4 +- .../Icons => shared/utils}/IconLibrary.tsx | 250 +++++++++++++++++- yarn.lock | 63 ++--- 18 files changed, 429 insertions(+), 155 deletions(-) create mode 100644 shared/components/Flex.tsx rename {app/components/Icons => shared/components}/LetterIcon.tsx (92%) rename {app => shared}/components/Squircle.tsx (100%) rename {app/components/Icons => shared/utils}/IconLibrary.tsx (59%) diff --git a/app/components/Collection/CollectionForm.tsx b/app/components/Collection/CollectionForm.tsx index 93e16485f..8c629b5d5 100644 --- a/app/components/Collection/CollectionForm.tsx +++ b/app/components/Collection/CollectionForm.tsx @@ -5,13 +5,13 @@ import { Trans, useTranslation } from "react-i18next"; import styled from "styled-components"; import { randomElement } from "@shared/random"; import { CollectionPermission } from "@shared/types"; +import { IconLibrary } from "@shared/utils/IconLibrary"; import { colorPalette } from "@shared/utils/collections"; import { CollectionValidation } from "@shared/validations"; import Collection from "~/models/Collection"; import Button from "~/components/Button"; import Flex from "~/components/Flex"; import IconPicker from "~/components/IconPicker"; -import { IconLibrary } from "~/components/Icons/IconLibrary"; import Input from "~/components/Input"; import InputSelectPermission from "~/components/InputSelectPermission"; import Switch from "~/components/Switch"; diff --git a/app/components/DocumentCard.tsx b/app/components/DocumentCard.tsx index 6cc2ea83f..1875f35f6 100644 --- a/app/components/DocumentCard.tsx +++ b/app/components/DocumentCard.tsx @@ -7,6 +7,7 @@ import * as React from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import styled, { useTheme } from "styled-components"; +import Squircle from "@shared/components/Squircle"; import { s, ellipsis } from "@shared/styles"; import Document from "~/models/Document"; import Pin from "~/models/Pin"; @@ -17,7 +18,6 @@ import useStores from "~/hooks/useStores"; import { hover } from "~/styles"; import CollectionIcon from "./Icons/CollectionIcon"; import EmojiIcon from "./Icons/EmojiIcon"; -import Squircle from "./Squircle"; import Text from "./Text"; import Tooltip from "./Tooltip"; diff --git a/app/components/Flex.tsx b/app/components/Flex.tsx index eb3e86e9a..4671600b6 100644 --- a/app/components/Flex.tsx +++ b/app/components/Flex.tsx @@ -1,38 +1,3 @@ -import { CSSProperties } from "react"; -import styled from "styled-components"; - -type JustifyValues = CSSProperties["justifyContent"]; - -type AlignValues = CSSProperties["alignItems"]; - -const Flex = styled.div<{ - auto?: boolean; - column?: boolean; - align?: AlignValues; - justify?: JustifyValues; - wrap?: boolean; - shrink?: boolean; - reverse?: boolean; - gap?: number; -}>` - display: flex; - flex: ${({ auto }) => (auto ? "1 1 auto" : "initial")}; - flex-direction: ${({ column, reverse }) => - reverse - ? column - ? "column-reverse" - : "row-reverse" - : column - ? "column" - : "row"}; - align-items: ${({ align }) => align}; - justify-content: ${({ justify }) => justify}; - flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "initial")}; - flex-shrink: ${({ shrink }) => - shrink === true ? 1 : shrink === false ? 0 : "initial"}; - gap: ${({ gap }) => (gap ? `${gap}px` : "initial")}; - min-height: 0; - min-width: 0; -`; +import Flex from "@shared/components/Flex"; export default Flex; diff --git a/app/components/IconPicker.tsx b/app/components/IconPicker.tsx index 4b4d22b7c..182f51c4e 100644 --- a/app/components/IconPicker.tsx +++ b/app/components/IconPicker.tsx @@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next"; import { PopoverDisclosure, usePopoverState } from "reakit"; import { MenuItem } from "reakit/Menu"; import styled, { useTheme } from "styled-components"; +import { IconLibrary } from "@shared/utils/IconLibrary"; import { colorPalette } from "@shared/utils/collections"; import Flex from "~/components/Flex"; import NudeButton from "~/components/NudeButton"; @@ -10,7 +11,7 @@ import Text from "~/components/Text"; import useOnClickOutside from "~/hooks/useOnClickOutside"; import lazyWithRetry from "~/utils/lazyWithRetry"; import DelayedMount from "./DelayedMount"; -import { IconLibrary } from "./Icons/IconLibrary"; +import InputSearch from "./InputSearch"; import Popover from "./Popover"; const icons = IconLibrary.mapping; @@ -38,11 +39,12 @@ function IconPicker({ onChange, className, }: Props) { + const [query, setQuery] = React.useState(""); const { t } = useTranslation(); const theme = useTheme(); const popover = usePopoverState({ gutter: 0, - placement: "bottom", + placement: "right", modal: true, }); @@ -51,9 +53,15 @@ function IconPicker({ onOpen?.(); } else { onClose?.(); + setQuery(""); } }, [onOpen, onClose, popover.visible]); + const filteredIcons = IconLibrary.findIcons(query); + const handleFilter = (event: React.ChangeEvent) => { + setQuery(event.target.value.toLowerCase()); + }; + const styles = React.useMemo( () => ({ default: { @@ -92,6 +100,9 @@ function IconPicker({ { capture: true } ); + const iconNames = Object.keys(icons); + const delayPerIcon = 250 / iconNames.length; + return ( <> @@ -112,69 +123,77 @@ function IconPicker({ - - {Object.keys(icons).map((name, index) => ( - onChange(color, name)}> - {(props) => ( - - + + {t("Choose an icon")} + + +
+ {iconNames.map((name, index) => ( + onChange(color, name)}> + {(props) => ( + - {initial} - - - )} - - ))} - - - - {t("Loading")}… - - } - > - onChange(color.hex, icon)} - colors={colorPalette} - triangle="hide" - styles={styles} - /> - - + + {initial} + + + )} + + ))} +
+ + + {t("Loading")}… + + } + > + onChange(color.hex, icon)} + colors={colorPalette} + triangle="hide" + styles={styles} + /> + + +
); } const Icon = styled.svg` - transition: fill 150ms ease-in-out; + transition: color 150ms ease-in-out, fill 150ms ease-in-out; transition-delay: var(--delay); `; -const Colors = styled(Flex)` - padding: 8px; -`; - -const Icons = styled.div` - padding: 8px; -`; - const IconButton = styled(NudeButton)` vertical-align: top; border-radius: 4px; diff --git a/app/components/Icons/CollectionIcon.tsx b/app/components/Icons/CollectionIcon.tsx index 89582f4e2..2454bc316 100644 --- a/app/components/Icons/CollectionIcon.tsx +++ b/app/components/Icons/CollectionIcon.tsx @@ -2,10 +2,10 @@ import { observer } from "mobx-react"; import { CollectionIcon } from "outline-icons"; import { getLuminance } from "polished"; import * as React from "react"; +import { IconLibrary } from "@shared/utils/IconLibrary"; import Collection from "~/models/Collection"; import useStores from "~/hooks/useStores"; import Logger from "~/utils/Logger"; -import { IconLibrary } from "./IconLibrary"; type Props = { /** The collection to show an icon for */ diff --git a/app/components/Sharing/OtherAccess.tsx b/app/components/Sharing/OtherAccess.tsx index 4a8151a22..97346b573 100644 --- a/app/components/Sharing/OtherAccess.tsx +++ b/app/components/Sharing/OtherAccess.tsx @@ -3,6 +3,7 @@ import { MoreIcon, QuestionMarkIcon, UserIcon } from "outline-icons"; import * as React from "react"; import { useTranslation } from "react-i18next"; import { useTheme } from "styled-components"; +import Squircle from "@shared/components/Squircle"; import { CollectionPermission } from "@shared/types"; import type Collection from "~/models/Collection"; import type Document from "~/models/Document"; @@ -14,7 +15,6 @@ import useStores from "~/hooks/useStores"; import Avatar from "../Avatar"; import { AvatarSize } from "../Avatar/Avatar"; import CollectionIcon from "../Icons/CollectionIcon"; -import Squircle from "../Squircle"; import Tooltip from "../Tooltip"; import { StyledListItem } from "./MemberListItem"; diff --git a/app/components/Sharing/PublicAccess.tsx b/app/components/Sharing/PublicAccess.tsx index c5d21b2b4..dd848ca0e 100644 --- a/app/components/Sharing/PublicAccess.tsx +++ b/app/components/Sharing/PublicAccess.tsx @@ -8,6 +8,7 @@ import { Trans, useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { toast } from "sonner"; import styled, { useTheme } from "styled-components"; +import Squircle from "@shared/components/Squircle"; import { s } from "@shared/styles"; import { UrlHelper } from "@shared/utils/UrlHelper"; import Document from "~/models/Document"; @@ -21,7 +22,6 @@ import { AvatarSize } from "../Avatar/Avatar"; import CopyToClipboard from "../CopyToClipboard"; import NudeButton from "../NudeButton"; import { ResizingHeightContainer } from "../ResizingHeightContainer"; -import Squircle from "../Squircle"; import Text from "../Text"; import Tooltip from "../Tooltip"; import { StyledListItem } from "./MemberListItem"; diff --git a/app/components/Text.ts b/app/components/Text.ts index 07dd36e69..9f81f7ac6 100644 --- a/app/components/Text.ts +++ b/app/components/Text.ts @@ -11,7 +11,7 @@ type Props = { /** Whether the text should be selectable (defaults to false) */ selectable?: boolean; /** The font weight of the text */ - weight?: "bold" | "normal"; + weight?: "xbold" | "bold" | "normal"; /** Whether the text should be truncated with an ellipsis */ ellipsis?: boolean; }; @@ -47,7 +47,9 @@ const Text = styled.span` ${(props) => props.weight && css` - font-weight: ${props.weight === "bold" + font-weight: ${props.weight === "xbold" + ? 600 + : props.weight === "bold" ? 500 : props.weight === "normal" ? 400 diff --git a/package.json b/package.json index 257a05076..2bbdd96ac 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,9 @@ "@dnd-kit/sortable": "^7.0.2", "@emoji-mart/data": "^1.0.6", "@emoji-mart/react": "^1.1.1", + "@fortawesome/fontawesome-svg-core": "^6.5.2", + "@fortawesome/free-solid-svg-icons": "^6.5.2", + "@fortawesome/react-fontawesome": "^0.2.0", "@getoutline/y-prosemirror": "^1.0.18", "@hocuspocus/extension-throttle": "1.1.2", "@hocuspocus/provider": "1.1.2", diff --git a/server/routes/api/collections/collections.test.ts b/server/routes/api/collections/collections.test.ts index fb8ec1ed1..148fa63bb 100644 --- a/server/routes/api/collections/collections.test.ts +++ b/server/routes/api/collections/collections.test.ts @@ -174,6 +174,7 @@ describe("#collections.move", () => { token: admin.getJwtToken(), id: collection.id, index: "P", + icon: "flame", }, }); const body = await res.json(); @@ -181,6 +182,20 @@ describe("#collections.move", () => { expect(body.success).toBe(true); }); + it("should return error when icon is not valid", async () => { + const team = await buildTeam(); + const admin = await buildAdmin({ teamId: team.id }); + const collection = await buildCollection({ teamId: team.id }); + const res = await server.post("/api/collections.move", { + body: { + token: admin.getJwtToken(), + id: collection.id, + icon: "nonsRence", + }, + }); + expect(res.status).toEqual(400); + }); + it("should return error when index is not valid", async () => { const team = await buildTeam(); const admin = await buildAdmin({ teamId: team.id }); diff --git a/server/routes/api/collections/schema.ts b/server/routes/api/collections/schema.ts index 17df1648d..1a8f64de3 100644 --- a/server/routes/api/collections/schema.ts +++ b/server/routes/api/collections/schema.ts @@ -2,11 +2,20 @@ import isUndefined from "lodash/isUndefined"; import { z } from "zod"; import { randomElement } from "@shared/random"; import { CollectionPermission, FileOperationFormat } from "@shared/types"; +import { IconLibrary } from "@shared/utils/IconLibrary"; import { colorPalette } from "@shared/utils/collections"; import { Collection } from "@server/models"; -import { ValidateColor, ValidateIcon, ValidateIndex } from "@server/validation"; +import { ValidateColor, ValidateIndex } from "@server/validation"; import { BaseSchema } from "../schema"; +function zodEnumFromObjectKeys< + TI extends Record, + R extends string = TI extends Record ? R : never +>(input: TI): z.ZodEnum<[R, ...R[]]> { + const [firstKey, ...otherKeys] = Object.keys(input) as [R, ...R[]]; + return z.enum([firstKey, ...otherKeys]); +} + export const CollectionsCreateSchema = BaseSchema.extend({ body: z.object({ name: z.string(), @@ -20,12 +29,7 @@ export const CollectionsCreateSchema = BaseSchema.extend({ .nullish() .transform((val) => (isUndefined(val) ? null : val)), sharing: z.boolean().default(true), - icon: z - .string() - .max(ValidateIcon.maxLength, { - message: `Must be ${ValidateIcon.maxLength} or fewer characters long`, - }) - .optional(), + icon: zodEnumFromObjectKeys(IconLibrary.mapping).optional(), sort: z .object({ field: z.union([z.literal("title"), z.literal("index")]), @@ -171,12 +175,7 @@ export const CollectionsUpdateSchema = BaseSchema.extend({ id: z.string().uuid(), name: z.string().optional(), description: z.string().nullish(), - icon: z - .string() - .max(ValidateIcon.maxLength, { - message: `Must be ${ValidateIcon.maxLength} or fewer characters long`, - }) - .nullish(), + icon: zodEnumFromObjectKeys(IconLibrary.mapping).nullish(), permission: z.nativeEnum(CollectionPermission).nullish(), color: z .string() diff --git a/server/validation.ts b/server/validation.ts index eff13375c..eaf36fd80 100644 --- a/server/validation.ts +++ b/server/validation.ts @@ -246,7 +246,3 @@ export class ValidateColor { public static regex = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i; public static message = "Must be a hex value (please use format #FFFFFF)"; } - -export class ValidateIcon { - public static maxLength = 50; -} diff --git a/shared/components/Flex.tsx b/shared/components/Flex.tsx new file mode 100644 index 000000000..eb3e86e9a --- /dev/null +++ b/shared/components/Flex.tsx @@ -0,0 +1,38 @@ +import { CSSProperties } from "react"; +import styled from "styled-components"; + +type JustifyValues = CSSProperties["justifyContent"]; + +type AlignValues = CSSProperties["alignItems"]; + +const Flex = styled.div<{ + auto?: boolean; + column?: boolean; + align?: AlignValues; + justify?: JustifyValues; + wrap?: boolean; + shrink?: boolean; + reverse?: boolean; + gap?: number; +}>` + display: flex; + flex: ${({ auto }) => (auto ? "1 1 auto" : "initial")}; + flex-direction: ${({ column, reverse }) => + reverse + ? column + ? "column-reverse" + : "row-reverse" + : column + ? "column" + : "row"}; + align-items: ${({ align }) => align}; + justify-content: ${({ justify }) => justify}; + flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "initial")}; + flex-shrink: ${({ shrink }) => + shrink === true ? 1 : shrink === false ? 0 : "initial"}; + gap: ${({ gap }) => (gap ? `${gap}px` : "initial")}; + min-height: 0; + min-width: 0; +`; + +export default Flex; diff --git a/app/components/Icons/LetterIcon.tsx b/shared/components/LetterIcon.tsx similarity index 92% rename from app/components/Icons/LetterIcon.tsx rename to shared/components/LetterIcon.tsx index 15cba1a91..88c1b18c2 100644 --- a/app/components/Icons/LetterIcon.tsx +++ b/shared/components/LetterIcon.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import styled from "styled-components"; -import { s } from "@shared/styles"; -import Squircle from "../Squircle"; +import { s } from "../styles"; +import Squircle from "./Squircle"; type Props = { /** The width and height of the icon, including standard padding. */ diff --git a/app/components/Squircle.tsx b/shared/components/Squircle.tsx similarity index 100% rename from app/components/Squircle.tsx rename to shared/components/Squircle.tsx diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index fd7140fd3..3fa8257d9 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -238,7 +238,8 @@ "{{authorName}} created <3>": "{{authorName}} created <3>", "{{authorName}} opened <3>": "{{authorName}} opened <3>", "Show menu": "Show menu", - "Choose icon": "Choose icon", + "Choose an icon": "Choose an icon", + "Filter": "Filter", "Loading": "Loading", "Select a color": "Select a color", "Search": "Search", @@ -864,7 +865,6 @@ "Enterprise": "Enterprise", "Recent imports": "Recent imports", "Everyone that has signed into {{appName}} is listed here. It’s possible that there are other users who have access through {{signinMethods}} but haven’t signed in yet.": "Everyone that has signed into {{appName}} is listed here. It’s possible that there are other users who have access through {{signinMethods}} but haven’t signed in yet.", - "Filter": "Filter", "Receive a notification whenever a new document is published": "Receive a notification whenever a new document is published", "Document updated": "Document updated", "Receive a notification when a document you are subscribed to is edited": "Receive a notification when a document you are subscribed to is edited", diff --git a/app/components/Icons/IconLibrary.tsx b/shared/utils/IconLibrary.tsx similarity index 59% rename from app/components/Icons/IconLibrary.tsx rename to shared/utils/IconLibrary.tsx index b6cca80a9..1e69bd984 100644 --- a/app/components/Icons/IconLibrary.tsx +++ b/shared/utils/IconLibrary.tsx @@ -1,3 +1,92 @@ +import { + faHeart, + faWandSparkles, + faUmbrella, + faMugHot, + faBook, + faDroplet, + faBrush, + faSnowflake, + faShop, + faShirt, + faBagShopping, + faGauge, + faMountainSun, + faPassport, + faPhoneVolume, + faNewspaper, + faNetworkWired, + faRocket, + faStarOfLife, + faSeedling, + faTrain, + faMicrochip, + faRecordVinyl, + faTrophy, + faHammer, + faRobot, + faCrown, + faCube, + faRoad, + faPuzzlePiece, + faIndustry, + faWorm, + faVault, + faUtensils, + faUserGraduate, + faUniversalAccess, + faTractor, + faTent, + faSpa, + faSocks, + faScissors, + faSailboat, + faPizzaSlice, + faPaw, + faMap, + faLaptopCode, + faKitMedical, + faFaceSurprise, + faFaceSmileWink, + faFaceSmileBeam, + faFaceMeh, + faFaceLaugh, + faFaceGrinStars, + faFaceDizzy, + faDog, + faCrow, + faCompactDisc, + faClapperboard, + faFeather, + faFish, + faCat, + faTree, + faShield, + faLaptop, + faDisplay, + faPrescription, + faWheelchairMove, + faGift, + faMagnet, + faPaintRoller, + faGamepad, + faCookieBite, + faTowerCell, + faTooth, + faDollarSign, + faSterlingSign, + faYenSign, + faPesoSign, + faRainbow, + faPenRuler, + faSwatchbook, + faStarAndCrescent, + faSolarPanel, + faUmbrellaBeach, + faGem, + faDna, +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import intersection from "lodash/intersection"; import { BookmarkedIcon, @@ -54,7 +143,21 @@ import { ThumbsUpIcon, TruckIcon, } from "outline-icons"; -import LetterIcon from "./LetterIcon"; +import * as React from "react"; +import styled from "styled-components"; +import LetterIcon from "../components/LetterIcon"; + +type IconMapping = { + component: React.ComponentType; + keywords?: string; +}; + +type FAProps = { + color?: string; + size?: number; + className?: string; + style?: React.CSSProperties; +}; export class IconLibrary { /** @@ -78,7 +181,7 @@ export class IconLibrary { for (const key of keys) { const icon = this.mapping[key]; - const keywords = icon.keywords.split(" "); + const keywords = icon.keywords?.split(" "); const namewords = keyword.toLocaleLowerCase().split(" "); const matches = intersection(namewords, keywords); @@ -90,11 +193,32 @@ export class IconLibrary { return undefined; } + /** + * Find icons. + * + * @param query The query to search for + * @returns The icon results. + */ + public static findIcons(query: string) { + return Object.keys(this.mapping) + .map((key) => { + const icon = this.mapping[key]; + const keywords = `${icon.keywords ?? ""} ${key}`; + + if (keywords.includes(query.toLocaleLowerCase())) { + return key; + } + return undefined; + }) + .filter(Boolean); + } + /** * A map of all icons available to end users in the app. This does not include icons that are used * internally only, which can be imported from `outline-icons` directly. */ - public static mapping = { + public static mapping: Record = { + // Internal icons academicCap: { component: AcademicCapIcon, keywords: "learn teach lesson guide tutorial onboarding training", @@ -311,5 +435,123 @@ export class IconLibrary { component: WarningIcon, keywords: "warning alert error", }, - }; + + // Font awesome + ...Object.fromEntries( + [ + faHeart, + faWandSparkles, + faUmbrella, + faMugHot, + faBook, + faDroplet, + faBrush, + faSnowflake, + faShop, + faShirt, + faBagShopping, + faGauge, + faMountainSun, + faPassport, + faPhoneVolume, + faNewspaper, + faNetworkWired, + faRocket, + faStarOfLife, + faSeedling, + faTrain, + faMicrochip, + faRecordVinyl, + faTrophy, + faHammer, + faRobot, + faCrown, + faCube, + faRoad, + faPuzzlePiece, + faIndustry, + faWorm, + faVault, + faUtensils, + faUserGraduate, + faUniversalAccess, + faTractor, + faTent, + faSpa, + faSocks, + faScissors, + faSailboat, + faPizzaSlice, + faPaw, + faMap, + faLaptopCode, + faKitMedical, + faFaceSurprise, + faFaceSmileWink, + faFaceSmileBeam, + faFaceMeh, + faFaceLaugh, + faFaceGrinStars, + faFaceDizzy, + faDog, + faCrow, + faCompactDisc, + faClapperboard, + faFeather, + faFish, + faCat, + faTree, + faShield, + faLaptop, + faDisplay, + faPrescription, + faWheelchairMove, + faGift, + faMagnet, + faPaintRoller, + faGamepad, + faCookieBite, + faTowerCell, + faTooth, + faDollarSign, + faSterlingSign, + faYenSign, + faPesoSign, + faRainbow, + faPenRuler, + faSwatchbook, + faStarAndCrescent, + faSolarPanel, + faUmbrellaBeach, + faGem, + faDna, + ].map((icon) => [ + icon.iconName, + { + component: ({ color, size = 24, className, style }: FAProps) => ( + + + + ), + }, + ]) + ), + } as const; } + +const FontAwesomeWrapper = styled.span<{ size: number }>` + display: flex; + align-items: center; + justify-content: center; + width: ${(props) => props.size}px; + height: ${(props) => props.size}px; +`; diff --git a/yarn.lock b/yarn.lock index d9b25506e..7abfc65f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -110,22 +110,7 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5", "@babel/helper-create-class-features-plugin@^7.20.7", "@babel/helper-create-class-features-plugin@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz#97a61b385e57fe458496fad19f8e63b63c867de4" - integrity "sha1-l6YbOF5X/kWElvrRn45jtjyGfeQ= sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==" - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.15" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - semver "^6.3.1" - -"@babel/helper-create-class-features-plugin@^7.24.1": +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5", "@babel/helper-create-class-features-plugin@^7.20.7", "@babel/helper-create-class-features-plugin@^7.22.15", "@babel/helper-create-class-features-plugin@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz#db58bf57137b623b916e24874ab7188d93d7f68f" integrity sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA== @@ -248,16 +233,7 @@ "@babel/helper-wrap-function" "^7.18.9" "@babel/types" "^7.18.9" -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7", "@babel/helper-replace-supers@^7.22.20", "@babel/helper-replace-supers@^7.22.9": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" - integrity "sha1-4302cSPKmP5FWpiHc07S4W63p5M= sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==" - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-member-expression-to-functions" "^7.22.15" - "@babel/helper-optimise-call-expression" "^7.22.5" - -"@babel/helper-replace-supers@^7.24.1": +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7", "@babel/helper-replace-supers@^7.22.20", "@babel/helper-replace-supers@^7.22.9", "@babel/helper-replace-supers@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz#7085bd19d4a0b7ed8f405c1ed73ccb70f323abc1" integrity sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ== @@ -624,20 +600,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.24.1": +"@babel/plugin-syntax-typescript@^7.24.1", "@babel/plugin-syntax-typescript@^7.7.2": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz#b3bcc51f396d15f3591683f90239de143c076844" integrity sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw== dependencies: "@babel/helper-plugin-utils" "^7.24.0" -"@babel/plugin-syntax-typescript@^7.7.2": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f" - integrity "sha1-JPRgyF27yYPNK5xJlBeLzAHflY8= sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==" - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-transform-arrow-functions@^7.18.6": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" @@ -1663,6 +1632,32 @@ dependencies: tslib "2.4.0" +"@fortawesome/fontawesome-common-types@6.5.2": + version "6.5.2" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz#eaf2f5699f73cef198454ebc0c414e3688898179" + integrity sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw== + +"@fortawesome/fontawesome-svg-core@^6.5.2": + version "6.5.2" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.2.tgz#4b42de71e196039b0d5ccf88559b8044e3296c21" + integrity sha512-5CdaCBGl8Rh9ohNdxeeTMxIj8oc3KNBgIeLMvJosBMdslK/UnEB8rzyDRrbKdL1kDweqBPo4GT9wvnakHWucZw== + dependencies: + "@fortawesome/fontawesome-common-types" "6.5.2" + +"@fortawesome/free-solid-svg-icons@^6.5.2": + version "6.5.2" + resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.2.tgz#9b40b077b27400a5e9fcbf2d15b986c7be69e9ca" + integrity sha512-QWFZYXFE7O1Gr1dTIp+D6UcFUF0qElOnZptpi7PBUMylJh+vFmIedVe1Ir6RM1t2tEQLLSV1k7bR4o92M+uqlw== + dependencies: + "@fortawesome/fontawesome-common-types" "6.5.2" + +"@fortawesome/react-fontawesome@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz#d90dd8a9211830b4e3c08e94b63a0ba7291ddcf4" + integrity sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw== + dependencies: + prop-types "^15.8.1" + "@getoutline/y-prosemirror@^1.0.18": version "1.0.18" resolved "https://registry.yarnpkg.com/@getoutline/y-prosemirror/-/y-prosemirror-1.0.18.tgz#17245c0362d30adb85131c86fb9a59358884b234"