From d864e228e7b4d2206c793a05bc3e649b8ec075d7 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Fri, 19 Jun 2020 17:18:03 -0700 Subject: [PATCH] feat: Collection Icons (#1281) * wip: Working for creation, and display * feat: IconPicker * fix * feat: Invert collection icon color when dark in dark mode * Improve readability of dropdown menus in dark mode Suggest icon based on collection name * Add additional icons Tweaks and final polish * fix: Write default icon as empty icon column * feat: Improve icon selection logic add more keywords Improve icon coloring when selected and in dark mode * lint * lint --- app/components/CollectionIcon.js | 45 ++++ app/components/ColorPicker.js | 106 -------- app/components/DropdownMenu/DropdownMenu.js | 8 + app/components/IconPicker.js | 238 ++++++++++++++++++ app/components/PathToDocument.js | 10 +- app/components/Sidebar/Main.js | 14 +- .../Sidebar/components/CollectionLink.js | 13 +- .../Sidebar/components/Collections.js | 2 +- app/menus/NewDocumentMenu.js | 10 +- app/models/Collection.js | 10 +- app/scenes/Collection.js | 23 +- app/scenes/CollectionEdit.js | 15 +- app/scenes/CollectionNew.js | 40 ++- package.json | 4 +- server/api/collections.js | 6 +- .../20200522054958-collection-icon.js | 14 ++ server/models/Collection.js | 7 + server/presenters/collection.js | 1 + shared/components/Breadcrumb.js | 15 +- shared/styles/theme.js | 1 + yarn.lock | 25 +- 21 files changed, 417 insertions(+), 190 deletions(-) create mode 100644 app/components/CollectionIcon.js delete mode 100644 app/components/ColorPicker.js create mode 100644 app/components/IconPicker.js create mode 100644 server/migrations/20200522054958-collection-icon.js diff --git a/app/components/CollectionIcon.js b/app/components/CollectionIcon.js new file mode 100644 index 00000000..a5b1c348 --- /dev/null +++ b/app/components/CollectionIcon.js @@ -0,0 +1,45 @@ +// @flow +import * as React from 'react'; +import { inject, observer } from 'mobx-react'; +import { getLuminance } from 'polished'; +import { PrivateCollectionIcon, CollectionIcon } from 'outline-icons'; +import Collection from 'models/Collection'; +import { icons } from 'components/IconPicker'; +import UiStore from 'stores/UiStore'; + +type Props = { + collection: Collection, + expanded?: boolean, + size?: number, + ui: UiStore, +}; + +function ResolvedCollectionIcon({ collection, expanded, size, ui }: Props) { + // If the chosen icon color is very dark then we invert it in dark mode + // otherwise it will be impossible to see against the dark background. + const color = + ui.resolvedTheme === 'dark' + ? getLuminance(collection.color) > 0.12 + ? collection.color + : 'currentColor' + : collection.color; + + if (collection.icon && collection.icon !== 'collection') { + try { + const Component = icons[collection.icon].component; + return ; + } catch (error) { + console.warn('Failed to render custom icon ' + collection.icon); + } + } + + if (collection.private) { + return ( + + ); + } + + return ; +} + +export default inject('ui')(observer(ResolvedCollectionIcon)); diff --git a/app/components/ColorPicker.js b/app/components/ColorPicker.js deleted file mode 100644 index 5037dcb9..00000000 --- a/app/components/ColorPicker.js +++ /dev/null @@ -1,106 +0,0 @@ -// @flow -import * as React from 'react'; -import { observable } from 'mobx'; -import { observer } from 'mobx-react'; -import { TwitterPicker } from 'react-color'; -import styled from 'styled-components'; -import Fade from 'components/Fade'; -import { LabelText } from 'components/Input'; - -const colors = [ - '#4E5C6E', - '#19B7FF', - '#7F6BFF', - '#FC7419', - '#FC2D2D', - '#FFE100', - '#14CF9F', - '#00D084', - '#EE84F0', - '#2F362F', -]; - -type Props = { - onChange: (color: string) => void, - value?: string, -}; - -@observer -class ColorPicker extends React.Component { - @observable isOpen: boolean = false; - node: ?HTMLElement; - - componentDidMount() { - window.addEventListener('click', this.handleClickOutside); - } - - componentWillUnmount() { - window.removeEventListener('click', this.handleClickOutside); - } - - handleClose = () => { - this.isOpen = false; - }; - - handleOpen = () => { - this.isOpen = true; - }; - - handleClickOutside = (ev: SyntheticMouseEvent<>) => { - // $FlowFixMe - if (ev.target && this.node && this.node.contains(ev.target)) { - return; - } - - this.handleClose(); - }; - - render() { - return ( - (this.node = ref)}> - - - - {this.isOpen && ( - - this.props.onChange(color.hex)} - triangle="top-right" - /> - - )} - - - ); - } -} - -const Wrapper = styled('div')` - display: inline-block; - position: relative; -`; -const Floating = styled('div')` - position: absolute; - top: 60px; - right: 0; - z-index: 1; -`; - -const Swatch = styled('div')` - display: inline-block; - width: 48px; - height: 32px; - border: 1px solid ${({ active, color }) => (active ? 'white' : 'transparent')}; - border-radius: 4px; - background: ${({ color }) => color}; -`; - -export default ColorPicker; diff --git a/app/components/DropdownMenu/DropdownMenu.js b/app/components/DropdownMenu/DropdownMenu.js index abf02a3c..d4bab28b 100644 --- a/app/components/DropdownMenu/DropdownMenu.js +++ b/app/components/DropdownMenu/DropdownMenu.js @@ -253,6 +253,10 @@ const Menu = styled.div` animation: ${fadeAndScaleIn} 200ms ease; transform-origin: ${props => (props.left !== undefined ? '25%' : '75%')} 0; background: ${props => props.theme.menuBackground}; + ${props => + props.theme.menuBorder + ? `border: 1px solid ${props.theme.menuBorder}` + : ''}; border-radius: 2px; padding: 0.5em 0; min-width: 180px; @@ -261,6 +265,10 @@ const Menu = styled.div` box-shadow: ${props => props.theme.menuShadow}; pointer-events: all; + hr { + margin: 0.5em 12px; + } + @media print { display: none; } diff --git a/app/components/IconPicker.js b/app/components/IconPicker.js new file mode 100644 index 00000000..5d60b17d --- /dev/null +++ b/app/components/IconPicker.js @@ -0,0 +1,238 @@ +// @flow +import * as React from 'react'; +import { observable } from 'mobx'; +import { observer } from 'mobx-react'; +import { TwitterPicker } from 'react-color'; +import { + CollectionIcon, + CoinsIcon, + AcademicCapIcon, + BeakerIcon, + BuildingBlocksIcon, + CloudIcon, + CodeIcon, + EditIcon, + EyeIcon, + LeafIcon, + LightBulbIcon, + MoonIcon, + NotepadIcon, + PadlockIcon, + PaletteIcon, + QuestionMarkIcon, + SunIcon, + VehicleIcon, +} from 'outline-icons'; +import styled from 'styled-components'; +import { LabelText } from 'components/Input'; +import { DropdownMenu } from 'components/DropdownMenu'; +import NudeButton from 'components/NudeButton'; +import Flex from 'shared/components/Flex'; + +export const icons = { + collection: { + component: CollectionIcon, + keywords: 'collection', + }, + coins: { + component: CoinsIcon, + keywords: 'coins money finance sales income revenue cash', + }, + academicCap: { + component: AcademicCapIcon, + keywords: 'learn teach lesson guide tutorial onboarding training', + }, + beaker: { + component: BeakerIcon, + keywords: 'lab research experiment test', + }, + buildingBlocks: { + component: BuildingBlocksIcon, + keywords: 'app blocks product prototype', + }, + cloud: { + component: CloudIcon, + keywords: 'cloud service aws infrastructure', + }, + code: { + component: CodeIcon, + keywords: 'developer api code development engineering programming', + }, + eye: { + component: EyeIcon, + keywords: 'eye view', + }, + leaf: { + component: LeafIcon, + keywords: 'leaf plant outdoors nature ecosystem climate', + }, + lightbulb: { + component: LightBulbIcon, + keywords: 'lightbulb idea', + }, + moon: { + component: MoonIcon, + keywords: 'night moon dark', + }, + notepad: { + component: NotepadIcon, + keywords: 'journal notepad write notes', + }, + padlock: { + component: PadlockIcon, + keywords: 'padlock private security authentication authorization auth', + }, + palette: { + component: PaletteIcon, + keywords: 'design palette art brand', + }, + pencil: { + component: EditIcon, + keywords: 'copy writing post blog', + }, + question: { + component: QuestionMarkIcon, + keywords: 'question help support faq', + }, + sun: { + component: SunIcon, + keywords: 'day sun weather', + }, + vehicle: { + component: VehicleIcon, + keywords: 'truck car travel transport', + }, +}; + +const colors = [ + '#4E5C6E', + '#0366d6', + '#7F6BFF', + '#E76F51', + '#FC2D2D', + '#FFBE0B', + '#2A9D8F', + '#00D084', + '#EE84F0', + '#2F362F', +]; + +type Props = { + onOpen?: () => void, + onChange: (color: string, icon: string) => void, + icon: string, + color: string, +}; + +function preventEventBubble(event) { + event.stopPropagation(); +} + +@observer +class IconPicker extends React.Component { + @observable isOpen: boolean = false; + node: ?HTMLElement; + + componentDidMount() { + window.addEventListener('click', this.handleClickOutside); + } + + componentWillUnmount() { + window.removeEventListener('click', this.handleClickOutside); + } + + handleClose = () => { + this.isOpen = false; + }; + + handleOpen = () => { + this.isOpen = true; + + if (this.props.onOpen) { + this.props.onOpen(); + } + }; + + handleClickOutside = (ev: SyntheticMouseEvent<>) => { + // $FlowFixMe + if (ev.target && this.node && this.node.contains(ev.target)) { + return; + } + + this.handleClose(); + }; + + render() { + const Component = icons[this.props.icon || 'collection'].component; + + return ( + (this.node = ref)}> + + + + + } + > + + {Object.keys(icons).map(name => { + const Component = icons[name].component; + return ( + this.props.onChange(this.props.color, name)} + style={{ width: 30, height: 30 }} + > + + + ); + })} + + + + this.props.onChange(color.hex, this.props.icon) + } + colors={colors} + triangle="hide" + /> + + + + ); + } +} + +const Icons = styled.div` + padding: 15px 9px 9px 15px; + width: 276px; +`; + +const LabelButton = styled(NudeButton)` + border: 1px solid ${props => props.theme.inputBorder}; + width: 32px; + height: 32px; +`; + +const IconButton = styled(NudeButton)` + border-radius: 4px; + margin: 0px 6px 6px 0px; + width: 30px; + height: 30px; +`; + +const ColorPicker = styled(TwitterPicker)` + box-shadow: none !important; + background: transparent !important; +`; + +const Wrapper = styled('div')` + display: inline-block; + position: relative; +`; + +export default IconPicker; diff --git a/app/components/PathToDocument.js b/app/components/PathToDocument.js index e839638a..942b4b4e 100644 --- a/app/components/PathToDocument.js +++ b/app/components/PathToDocument.js @@ -2,12 +2,13 @@ import * as React from 'react'; import { observer } from 'mobx-react'; import styled from 'styled-components'; -import { GoToIcon, CollectionIcon, PrivateCollectionIcon } from 'outline-icons'; +import { GoToIcon } from 'outline-icons'; import Flex from 'shared/components/Flex'; import Document from 'models/Document'; import Collection from 'models/Collection'; import type { DocumentPath } from 'stores/CollectionsStore'; +import CollectionIcon from 'components/CollectionIcon'; type Props = { result: DocumentPath, @@ -41,12 +42,7 @@ class PathToDocument extends React.Component { return ( - {collection && - (collection.private ? ( - - ) : ( - - ))} + {collection && } {result.path .map(doc => {doc.title}) .reduce((prev, curr) => [prev, , curr])} diff --git a/app/components/Sidebar/Main.js b/app/components/Sidebar/Main.js index c6919ee4..8c79c8b6 100644 --- a/app/components/Sidebar/Main.js +++ b/app/components/Sidebar/Main.js @@ -84,7 +84,7 @@ class MainSidebar extends React.Component {
} + icon={} exact={false} label="Home" /> @@ -93,19 +93,19 @@ class MainSidebar extends React.Component { pathname: '/search', state: { fromMenu: true }, }} - icon={} + icon={} label="Search" exact={false} /> } + icon={} exact={false} label="Starred" /> } + icon={} label={ Drafts{draftDocumentsCount > 0 && ( @@ -127,7 +127,7 @@ class MainSidebar extends React.Component {
} + icon={} exact={false} label="Archive" active={ @@ -138,7 +138,7 @@ class MainSidebar extends React.Component { /> } + icon={} exact={false} label="Trash" active={ @@ -149,7 +149,7 @@ class MainSidebar extends React.Component { } + icon={} label="Invite people…" /> )} diff --git a/app/components/Sidebar/components/CollectionLink.js b/app/components/Sidebar/components/CollectionLink.js index ce76b78d..107c8683 100644 --- a/app/components/Sidebar/components/CollectionLink.js +++ b/app/components/Sidebar/components/CollectionLink.js @@ -2,7 +2,6 @@ import * as React from 'react'; import { observer } from 'mobx-react'; import { observable } from 'mobx'; -import { CollectionIcon, PrivateCollectionIcon } from 'outline-icons'; import Collection from 'models/Collection'; import Document from 'models/Document'; import CollectionMenu from 'menus/CollectionMenu'; @@ -10,6 +9,7 @@ import UiStore from 'stores/UiStore'; import DocumentsStore from 'stores/DocumentsStore'; import SidebarLink from './SidebarLink'; import DocumentLink from './DocumentLink'; +import CollectionIcon from 'components/CollectionIcon'; import DropToImport from 'components/DropToImport'; import Flex from 'shared/components/Flex'; @@ -44,16 +44,7 @@ class CollectionLink extends React.Component { - ) : ( - - ) - } + icon={} iconColor={collection.color} expanded={expanded} hideDisclosure diff --git a/app/components/Sidebar/components/Collections.js b/app/components/Sidebar/components/Collections.js index 3eb03cbb..10c9834e 100644 --- a/app/components/Sidebar/components/Collections.js +++ b/app/components/Sidebar/components/Collections.js @@ -70,7 +70,7 @@ class Collections extends React.Component { } + icon={} label="New collection…" exact /> diff --git a/app/menus/NewDocumentMenu.js b/app/menus/NewDocumentMenu.js index abc15366..33c709ee 100644 --- a/app/menus/NewDocumentMenu.js +++ b/app/menus/NewDocumentMenu.js @@ -3,13 +3,14 @@ import * as React from 'react'; import { observable } from 'mobx'; import { inject, observer } from 'mobx-react'; import { Redirect } from 'react-router-dom'; -import { PlusIcon, CollectionIcon, PrivateCollectionIcon } from 'outline-icons'; +import { PlusIcon } from 'outline-icons'; import { newDocumentUrl } from 'utils/routeHelpers'; import CollectionsStore from 'stores/CollectionsStore'; import PoliciesStore from 'stores/PoliciesStore'; import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; import Button from 'components/Button'; +import CollectionIcon from 'components/CollectionIcon'; type Props = { label?: React.Node, @@ -64,12 +65,7 @@ class NewDocumentMenu extends React.Component { onClick={() => this.handleNewDocument(collection.id)} disabled={!can.update} > - {collection.private ? ( - - ) : ( - - )}{' '} - {collection.name} + {collection.name} ); })} diff --git a/app/models/Collection.js b/app/models/Collection.js index c69d7f12..4639226d 100644 --- a/app/models/Collection.js +++ b/app/models/Collection.js @@ -13,6 +13,7 @@ export default class Collection extends BaseModel { id: string; name: string; description: string; + icon: string; color: string; private: boolean; type: 'atlas' | 'journal'; @@ -101,7 +102,14 @@ export default class Collection extends BaseModel { } toJS = () => { - return pick(this, ['id', 'name', 'color', 'description', 'private']); + return pick(this, [ + 'id', + 'name', + 'color', + 'description', + 'icon', + 'private', + ]); }; export = () => { diff --git a/app/scenes/Collection.js b/app/scenes/Collection.js index d7a025e4..1704e6b7 100644 --- a/app/scenes/Collection.js +++ b/app/scenes/Collection.js @@ -5,13 +5,7 @@ import { observer, inject } from 'mobx-react'; import { Redirect, Link, Switch, Route } from 'react-router-dom'; import styled, { withTheme } from 'styled-components'; -import { - CollectionIcon, - PrivateCollectionIcon, - NewDocumentIcon, - PlusIcon, - PinIcon, -} from 'outline-icons'; +import { NewDocumentIcon, PlusIcon, PinIcon } from 'outline-icons'; import RichMarkdownEditor from 'rich-markdown-editor'; import { newDocumentUrl, collectionUrl } from 'utils/routeHelpers'; @@ -42,6 +36,7 @@ import CollectionMembers from 'scenes/CollectionMembers'; import Tabs from 'components/Tabs'; import Tab from 'components/Tab'; import PaginatedDocumentList from 'components/PaginatedDocumentList'; +import CollectionIcon from 'components/CollectionIcon'; type Props = { ui: UiStore, @@ -210,19 +205,7 @@ class CollectionScene extends React.Component { ) : ( - {collection.private ? ( - - ) : ( - - )}{' '} + {' '} {collection.name} diff --git a/app/scenes/CollectionEdit.js b/app/scenes/CollectionEdit.js index 71eecca5..881da8f5 100644 --- a/app/scenes/CollectionEdit.js +++ b/app/scenes/CollectionEdit.js @@ -8,7 +8,7 @@ import Button from 'components/Button'; import Switch from 'components/Switch'; import Flex from 'shared/components/Flex'; import HelpText from 'components/HelpText'; -import ColorPicker from 'components/ColorPicker'; +import IconPicker from 'components/IconPicker'; import Collection from 'models/Collection'; import UiStore from 'stores/UiStore'; @@ -22,6 +22,7 @@ type Props = { class CollectionEdit extends React.Component { @observable name: string; @observable description: string = ''; + @observable icon: string = ''; @observable color: string = '#4E5C6E'; @observable isSaving: boolean; @observable private: boolean = false; @@ -29,6 +30,7 @@ class CollectionEdit extends React.Component { componentDidMount() { this.name = this.props.collection.name; this.description = this.props.collection.description; + this.icon = this.props.collection.icon; this.color = this.props.collection.color; this.private = this.props.collection.private; } @@ -41,6 +43,7 @@ class CollectionEdit extends React.Component { await this.props.collection.save({ name: this.name, description: this.description, + icon: this.icon, color: this.color, private: this.private, }); @@ -61,8 +64,9 @@ class CollectionEdit extends React.Component { this.name = ev.target.value; }; - handleColor = (color: string) => { + handleChange = (color: string, icon: string) => { this.color = color; + this.icon = icon; }; handlePrivateChange = (ev: SyntheticInputEvent<*>) => { @@ -87,7 +91,12 @@ class CollectionEdit extends React.Component { autoFocus flex /> -   +   + { @observable name: string = ''; @observable description: string = ''; + @observable icon: string = ''; @observable color: string = '#4E5C6E'; @observable private: boolean = false; @observable isSaving: boolean; + hasOpenedIconPicker: boolean = false; handleSubmit = async (ev: SyntheticEvent<>) => { ev.preventDefault(); @@ -37,6 +40,7 @@ class CollectionNew extends React.Component { { name: this.name, description: this.description, + icon: this.icon, color: this.color, private: this.private, }, @@ -56,6 +60,29 @@ class CollectionNew extends React.Component { handleNameChange = (ev: SyntheticInputEvent<*>) => { this.name = ev.target.value; + + // If the user hasn't picked an icon yet, go ahead and suggest one based on + // the name of the collection. It's the little things sometimes. + if (!this.hasOpenedIconPicker) { + const keys = Object.keys(icons); + for (const key of keys) { + const icon = icons[key]; + const keywords = icon.keywords.split(' '); + const namewords = this.name.toLowerCase().split(' '); + const matches = intersection(namewords, keywords); + + if (matches.length > 0) { + this.icon = key; + return; + } + } + + this.icon = 'collection'; + } + }; + + handleIconPickerOpen = () => { + this.hasOpenedIconPicker = true; }; handleDescriptionChange = getValue => { @@ -66,8 +93,9 @@ class CollectionNew extends React.Component { this.private = ev.target.checked; }; - handleColor = (color: string) => { + handleChange = (color: string, icon: string) => { this.color = color; + this.icon = icon; }; render() { @@ -88,7 +116,13 @@ class CollectionNew extends React.Component { autoFocus flex /> -   +   + { - const { name, color, description, type } = ctx.body; + const { name, color, description, icon, type } = ctx.body; const isPrivate = ctx.body.private; ctx.assertPresent(name, 'name is required'); @@ -44,6 +44,7 @@ router.post('collections.create', auth(), async ctx => { let collection = await Collection.create({ name, description, + icon, color, type: type || 'atlas', teamId: user.teamId, @@ -445,7 +446,7 @@ router.post('collections.export_all', auth(), async ctx => { }); router.post('collections.update', auth(), async ctx => { - const { id, name, description, color } = ctx.body; + const { id, name, description, icon, color } = ctx.body; const isPrivate = ctx.body.private; ctx.assertPresent(name, 'name is required'); @@ -480,6 +481,7 @@ router.post('collections.update', auth(), async ctx => { collection.name = name; collection.description = description; + collection.icon = icon; collection.color = color; collection.private = isPrivate; diff --git a/server/migrations/20200522054958-collection-icon.js b/server/migrations/20200522054958-collection-icon.js new file mode 100644 index 00000000..b1579f45 --- /dev/null +++ b/server/migrations/20200522054958-collection-icon.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.addColumn('collections', 'icon', { + type: Sequelize.TEXT, + allowNull: true, + }); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn('collections', 'icon'); + } +}; \ No newline at end of file diff --git a/server/models/Collection.js b/server/models/Collection.js index 56cb055b..2b25b1de 100644 --- a/server/models/Collection.js +++ b/server/models/Collection.js @@ -19,6 +19,7 @@ const Collection = sequelize.define( urlId: { type: DataTypes.STRING, unique: true }, name: DataTypes.STRING, description: DataTypes.STRING, + icon: DataTypes.STRING, color: DataTypes.STRING, private: DataTypes.BOOLEAN, maintainerApprovalRequired: DataTypes.BOOLEAN, @@ -46,6 +47,12 @@ const Collection = sequelize.define( } ); +Collection.addHook('beforeSave', async model => { + if (model.icon === 'collection') { + model.icon = null; + } +}); + // Class methods Collection.associate = models => { diff --git a/server/presenters/collection.js b/server/presenters/collection.js index b2bd8cbe..2f88ee8c 100644 --- a/server/presenters/collection.js +++ b/server/presenters/collection.js @@ -24,6 +24,7 @@ export default function present(collection: Collection) { url: collection.url, name: collection.name, description: collection.description, + icon: collection.icon, color: collection.color || '#4E5C6E', type: collection.type, private: collection.private, diff --git a/shared/components/Breadcrumb.js b/shared/components/Breadcrumb.js index 9acb622c..5696a184 100644 --- a/shared/components/Breadcrumb.js +++ b/shared/components/Breadcrumb.js @@ -4,19 +4,14 @@ import { observer, inject } from 'mobx-react'; import breakpoint from 'styled-components-breakpoint'; import styled from 'styled-components'; import { Link } from 'react-router-dom'; -import { - CollectionIcon, - PrivateCollectionIcon, - PadlockIcon, - GoToIcon, - MoreIcon, -} from 'outline-icons'; +import { PadlockIcon, GoToIcon, MoreIcon } from 'outline-icons'; import Document from 'models/Document'; import CollectionsStore from 'stores/CollectionsStore'; import { collectionUrl } from 'utils/routeHelpers'; import Flex from 'shared/components/Flex'; import BreadcrumbMenu from './BreadcrumbMenu'; +import CollectionIcon from 'components/CollectionIcon'; type Props = { document: Document, @@ -56,11 +51,7 @@ const Breadcrumb = observer(({ document, collections, onlyText }: Props) => { return ( - {collection.private ? ( - - ) : ( - - )}{' '} + {' '} {collection.name} {isNestedDocument && ( diff --git a/shared/styles/theme.js b/shared/styles/theme.js index 8f726c7d..09b2abd6 100644 --- a/shared/styles/theme.js +++ b/shared/styles/theme.js @@ -155,6 +155,7 @@ export const dark = { sidebarText: colors.slate, shadow: 'rgba(0, 0, 0, 0.6)', + menuBorder: lighten(0.1, colors.almostBlack), menuBackground: lighten(0.015, colors.almostBlack), menuShadow: '0 0 0 1px rgba(0, 0, 0, 0.1), 0 8px 16px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.08), inset 0 0 1px rgba(255,255,255,.2)', diff --git a/yarn.lock b/yarn.lock index 2b9946b7..fdd629a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -122,6 +122,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.9.2": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.1.tgz#b6eb75cac279588d3100baecd1b9894ea2840822" + integrity sha512-nQbbCbQc9u/rpg1XCxoMYQTbSMVZjCDxErQ1ClCn9Pvcmv1lGads19ep0a2VsEiIJeHqjZley6EQGEC3Yo1xMA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.8.3": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" @@ -7159,10 +7166,10 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -outline-icons@^1.15.0, outline-icons@^1.16.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.16.0.tgz#0a71d2fe32170f0e00b8775681f0339f4fc8777a" - integrity sha512-6bAk5rBGVtYiFP6AONx7NdmpFP+daKUJEev7PAjdfTVmE3bPXeSzzaGY51Y1XM8UdP5XqJICWncktRHeSfn1Pw== +outline-icons@^1.15.0, outline-icons@^1.18.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.18.0.tgz#2f128d668b0874725b5c0656a04fd73a7f8fe418" + integrity sha512-odEYBLN+zFcTDhfs4af2RHUGneQBYbVgaBuI/I30BLaJQsKT2jBw2Shjie/HR8MYpZtgMwixP51XPPlGBSIqgw== oy-vey@^0.10.0: version "0.10.0" @@ -7549,10 +7556,12 @@ pn@^1.1.0: resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== -polished@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/polished/-/polished-1.2.1.tgz#83c18a85bf9d7023477cfc7049763b657d50f0f7" - integrity sha1-g8GKhb+dcCNHfPxwSXY7ZX1Q8Pc= +polished@3.6.4: + version "3.6.4" + resolved "https://registry.yarnpkg.com/polished/-/polished-3.6.4.tgz#cec6bc0fbffc5d6ce5799c85bcc1bca5e63f1dee" + integrity sha512-21moJXCm/7EvjeKQz5w89QDDKNPCoimc83CqwZZGJluFdMXsFlMQl9lPA/OMRkoceZ19kU0anKlMgZmY7LJSJw== + dependencies: + "@babel/runtime" "^7.9.2" popper.js@^1.14.7: version "1.16.1"