[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleList/Operations.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

import ListStyles from './graphql/ListStyles.graphql'
import CreateStyleMutation, {
  CreateStyleMutationFn,
} from './mutations/CreateStyle'
import DeleteStyleMutation, {
  DeleteStyleMutationFn,
} from './mutations/DeleteStyle'
import SaveSelectedStyleMutation, {
  SaveSelectedStyleMutationFn,
} from './mutations/SaveSelectedStyle'
import ListStylesQuery, { ListStylesQueryResult } from './queries/ListStyles'

interface Props {
  children: (mutations: OperationsObj) => React.ReactNode
}

interface OperationsObj {
  listStyles: ListStylesQueryResult
  saveSelectedStyle: SaveSelectedStyleMutationFn
  createStyle: CreateStyleMutationFn
  deleteStyle: DeleteStyleMutationFn
}

const Operations = (props: Props) => (
  <ListStylesQuery fetchPolicy={'network-only'}>
    {listStyles => (
      <SaveSelectedStyleMutation
        refetchQueries={() => [{ query: ListStyles }]}
        awaitRefetchQueries
      >
        {saveSelectedStyle => (
          <CreateStyleMutation
            refetchQueries={() => [{ query: ListStyles }]}
            awaitRefetchQueries
          >
            {createStyle => (
              <DeleteStyleMutation
                refetchQueries={() => [{ query: ListStyles }]}
                awaitRefetchQueries
              >
                {deleteStyle =>
                  props.children({
                    createStyle,
                    deleteStyle,
                    listStyles,
                    saveSelectedStyle,
                  })
                }
              </DeleteStyleMutation>
            )}
          </CreateStyleMutation>
        )}
      </SaveSelectedStyleMutation>
    )}
  </ListStylesQuery>
)

export default Operations

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { createMemoryHistory } from 'history'
import { injectIntl, IntlShape } from 'react-intl'
import { Router } from 'react-router-dom'
import { ToastConsumer } from 'vtex.styleguide'

import RenameStyleMutation from './mutations/RenameStyle'
import UpdateStyleMutation from './mutations/UpdateStyle'
import StyleEditorHooks from './StyleEditorHooks'

interface Props {
  intl: IntlShape
  setStyleAsset: (asset: StyleAssetInfo) => void
  stopEditing: () => void
  style: Style
}

const StyleEditor: React.FunctionComponent<Props> = props => {
  return (
    <Router history={createMemoryHistory()}>
      <RenameStyleMutation>
        {renameStyle => (
          <UpdateStyleMutation>
            {updateStyle => (
              <ToastConsumer>
                {({ showToast }) => (
                  <StyleEditorHooks
                    {...{
                      ...props,
                      renameStyle,
                      showToast,
                      updateStyle,
                    }}
                  />
                )}
              </ToastConsumer>
            )}
          </UpdateStyleMutation>
        )}
      </RenameStyleMutation>
    </Router>
  )
}

export default injectIntl(StyleEditor)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/StyleEditorHooks.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { mergeDeepRight } from 'ramda'
import React, { useCallback, useReducer, useState } from 'react'
import { defineMessages, useIntl } from 'react-intl'
import { ShowToastFunction } from 'vtex.styleguide'

import { RenameStyleFunction } from './mutations/RenameStyle'
import { UpdateStyleFunction } from './mutations/UpdateStyle'
import GenerateStyleSheetQuery from './queries/GenerateStyleSheet'
import StyleEditorRouter from './StyleEditorRouter'

interface Props {
  style: Style
  renameStyle: RenameStyleFunction
  updateStyle: UpdateStyleFunction
  setStyleAsset: (asset: StyleAssetInfo) => void
  showToast: ShowToastFunction
  stopEditing: () => void
}

type ConfigReducer = (
  config: TachyonsConfig,
  partialConfig: DeepPartial<TachyonsConfig>
) => TachyonsConfig

defineMessages({
  saveFailed: {
    defaultMessage: 'An error occurred while saving your style.',
    id: 'admin/pages.editor.styles.edit.save.failed',
  },
  saveSuccessful: {
    defaultMessage: 'Style saved successfully.',
    id: 'admin/pages.editor.styles.edit.save.successful',
  },
})

const StyleEditorStates: React.FunctionComponent<Props> = ({
  style,
  renameStyle,
  setStyleAsset,
  updateStyle,
  showToast,
  stopEditing,
}) => {
  const intl = useIntl()
  const nameState = useState<string>(style.name)
  const [name] = nameState
  const configReducer = useReducer<ConfigReducer>(
    mergeDeepRight as ConfigReducer,
    style.config
  )
  const [config] = configReducer

  const editingState = useState(false)
  const setEditing = editingState[1]

  const hooks = {
    config: configReducer,
    editing: editingState,
    name: nameState,
  }

  const saveStyle = useCallback(async () => {
    await renameStyle({ variables: { id: style.id, name } })
    const result = await updateStyle({
      variables: { id: style.id, config },
    })
    const styleInfo = result && result.data && result.data.updateStyle
    if (styleInfo) {
      const { path, selected } = styleInfo
      setStyleAsset({
        keepSheet: true,
        selected,
        type: 'path',
        value: path,
      })

      showToast({
        horizontalPosition: 'left',
        message: intl.formatMessage({
          id: 'admin/pages.editor.styles.edit.save.successful',
        }),
      })
    } else {
      showToast({
        horizontalPosition: 'left',
        message: intl.formatMessage({
          id: 'admin/pages.editor.styles.edit.save.failed',
        }),
      })
    }
  }, [
    renameStyle,
    style.id,
    name,
    updateStyle,
    config,
    setStyleAsset,
    showToast,
    intl,
  ])

  const onSave = useCallback(() => {
    setEditing(false)
    saveStyle()
  }, [saveStyle, setEditing])

  return (
    <GenerateStyleSheetQuery
      variables={{ config }}
      fetchPolicy={'network-only'}
    >
      {({ data }) => (
        <StyleEditorRouter
          data={data || null}
          hooks={hooks}
          onSave={onSave}
          setStyleAsset={setStyleAsset}
          stopEditing={stopEditing}
          style={style}
        />
      )}
    </GenerateStyleSheetQuery>
  )
}

export default StyleEditorStates

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/EditorSelector.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { defineMessages, FormattedMessage } from 'react-intl'

import Colors from '../components/Colors'
import AvailableEditor from './AvailableEditor'
import StyleEditorHeader from './StyleEditorHeader'
import { EditorPath, IdParam } from './StyleEditorRouter'

interface Props {
  config: TachyonsConfig
  name: string
  onSave: () => void
  stopEditing: () => void
}

defineMessages({
  colorsTitle: {
    defaultMessage: 'Colors',
    id: 'admin/pages.editor.styles.edit.colors.title',
  },
  typographyTitle: {
    defaultMessage: 'Typography',
    id: 'admin/pages.editor.styles.edit.typography.title',
  },
})

const EditorSelector: React.FunctionComponent<Props> = ({
  config,
  name,
  onSave,
  stopEditing,
}) => {
  const {
    semanticColors: {
      background: { action_primary: actionPrimary, emphasis },
    },
  } = config

  const colorEditorProps = {
    path: EditorPath.colors.replace(IdParam, ''),
    titleId: 'admin/pages.editor.styles.edit.colors.title',
    widget: <Colors colors={[emphasis, actionPrimary]} />,
  }

  const typographyEditorProps = {
    path: EditorPath.typography,
    titleId: 'admin/pages.editor.styles.edit.typography.title',
    widget: <div className="t-heading-2">Aa</div>,
  }

  const saveButtonLabel = (
    <FormattedMessage
      id="admin/pages.editor.components.button.save"
      defaultMessage="Save"
    />
  )

  return (
    <>
      <StyleEditorHeader
        onButtonClick={onSave}
        buttonLabel={saveButtonLabel}
        afterOnBack={stopEditing}
        title={name}
      />
      <div className="ph6">
        <AvailableEditor {...colorEditorProps} />
        <AvailableEditor {...typographyEditorProps} />
      </div>
    </>
  )
}

export default EditorSelector

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/StyleEditorHeader.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { useKeydownFromClick } from 'keydown-from-click'
import React, { useCallback } from 'react'
import { RouteComponentProps, withRouter } from 'react-router'
import { Button, IconArrowBack } from 'vtex.styleguide'

interface Props extends RouteComponentProps {
  buttonLabel?: string | React.ReactNode
  onButtonClick?: () => void
  afterOnBack?: () => void
  title: string | React.ReactNode
}

const StyleEditorHeader: React.FunctionComponent<Props> = ({
  afterOnBack,
  buttonLabel,
  children,
  history,
  onButtonClick,
  title,
}) => {
  const onBack = useCallback(() => {
    history.goBack()
    if (afterOnBack) {
      afterOnBack()
    }
  }, [history, afterOnBack])

  const onBackByKeyDown = useKeydownFromClick(onBack)

  return (
    <div className="mh6 mt6">
      <div className="flex justify-between items-center mv4">
        <div className="flex items-center">
          <div
            className="pointer f3 ph4 outline-0"
            onClick={onBack}
            onKeyDown={onBackByKeyDown}
            role="button"
            tabIndex={0}
          >
            <IconArrowBack />
          </div>
          <span className="f3">{title}</span>
        </div>
        {buttonLabel && (
          <Button
            variation="tertiary"
            size="small"
            onClick={onButtonClick}
            disabled={!onButtonClick}
          >
            {buttonLabel}
          </Button>
        )}
        {children}
      </div>
    </div>
  )
}

export default withRouter(StyleEditorHeader)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/AvailableEditor.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { useKeydownFromClick } from 'keydown-from-click'
import React from 'react'
import { FormattedMessage } from 'react-intl'
import { RouteComponentProps, withRouter } from 'react-router-dom'

interface Props extends RouteComponentProps {
  path: string
  titleId: string
  widget?: React.ReactNode
}

const AvailableEditor: React.FunctionComponent<Props> = ({
  history,
  path,
  titleId,
  widget,
}) => {
  const redirect = React.useCallback(() => history.push(path), [history, path])

  const redirectByKeyDown = useKeydownFromClick(redirect)

  return (
    <div
      className="pointer flex justify-between items-center pv6 bb b--muted-4 outline-0"
      onClick={redirect}
      onKeyDown={redirectByKeyDown}
      role="button"
      tabIndex={0}
    >
      <span className="f4">
        <FormattedMessage id={titleId} />
      </span>
      {widget}
    </div>
  )
}

export default withRouter(AvailableEditor)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/StyleEditorRouter.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useEffect } from 'react'
import { Route, RouteComponentProps, Switch } from 'react-router'

import ColorsEditor from './ColorsEditor'
import EditorSelector from './EditorSelector'
import FontFamilyList from './typography/FontFamilyList'
import TypographyEditor from './typography/TypographyEditor'

import { GenerateStyleSheetData } from './queries/GenerateStyleSheet'
import CustomFont from './typography/FontEditor'
import TypeToken from './typography/TypeToken'
import TypeTokensList from './typography/TypeTokensList'

export const IdParam = ':id'

export enum EditorPath {
  selector = '/',
  colors = '/colors/:id',
  typography = '/typography',
  fontFamily = '/font-family',
  typeToken = '/type-token/:id',
  typeTokens = '/type-tokens',
  customFont = '/custom-font/:id',
  customFontFile = '/custom-font/file/:id',
  customFontLink = '/custom-font/link/:id',
}

interface Props {
  data: GenerateStyleSheetData | null
  hooks: {
    config: [TachyonsConfig, React.Dispatch<DeepPartial<TachyonsConfig>>]
    editing: [boolean, React.Dispatch<React.SetStateAction<boolean>>]
    name: [string, React.Dispatch<React.SetStateAction<string>>]
  }
  onSave: () => void
  setStyleAsset: (asset: StyleAssetInfo) => void
  stopEditing: () => void
  style: Style
}

const StyleEditorRouter: React.FunctionComponent<Props> = ({
  data,
  hooks: {
    config: [config, updateConfig],
    name: [name],
  },
  onSave,
  setStyleAsset,
  stopEditing,
  style,
}) => {
  const stylesheet = data && data.generateStyleSheet

  useEffect(() => {
    if (stylesheet) {
      setStyleAsset({ type: 'stylesheet', value: stylesheet })
    }
  }, [setStyleAsset, stylesheet])

  const renderEditorSelector = () => (
    <EditorSelector {...{ config, name, onSave, stopEditing }} />
  )

  const colorsPaths = [
    EditorPath.colors,
    EditorPath.colors.replace(IdParam, ''),
  ]
  const renderColorsEditor = (props: RouteComponentProps<ColorRouteParams>) => (
    <ColorsEditor
      {...{ ...props, updateStyle: updateConfig, config, onSave }}
    />
  )

  const customFontPaths = [
    EditorPath.customFont,
    EditorPath.customFont.replace(IdParam, ''),
  ]

  const renderTypeTokensList = (props: RouteComponentProps) => (
    <TypeTokensList {...props} style={style} />
  )

  const renderTypeToken = (props: RouteComponentProps<TypeTokenParams>) => (
    <TypeToken
      {...props}
      config={config}
      updateConfig={updateConfig}
      onSave={onSave}
    />
  )

  return (
    <div className="h-100 flex flex-column flex-grow-1 overflow-y-auto overflow-x-hidden">
      <Switch>
        <Route exact path={EditorPath.selector} render={renderEditorSelector} />
        <Route exact path={colorsPaths} render={renderColorsEditor} />
        <Route
          exact
          path={EditorPath.typography}
          component={TypographyEditor}
        />
        <Route exact path={EditorPath.fontFamily} component={FontFamilyList} />

        <Route path={customFontPaths} component={CustomFont} />
        <Route
          exact
          path={EditorPath.typeTokens}
          render={renderTypeTokensList}
        />
        <Route exact path={EditorPath.typeToken} render={renderTypeToken} />
      </Switch>
    </div>
  )
}

export default StyleEditorRouter

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/typings.d.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
type DeepPartial<T> = T extends object
  ? { [K in keyof T]?: DeepPartial<T[K]> }
  : T

interface ButtonInfo {
  action?: () => void
  text: string
}

interface RouteInfo {
  backButton: ButtonInfo
  auxButton?: ButtonInfo
  title: string
}

interface NavigationUpdate {
  type: 'push' | 'pop' | 'update'
  route: EditorRoute
}

interface ColorRouteParams {
  id: string
}

interface CustomFontParams {
  id: string
}

interface TypeTokenParams {
  id: string
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/colors.d.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface ColorInfo {
  path: string
  color: string
  configField: string
}

type Colors = Record<string, ColorInfo[]>

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/typings/config.d.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  FontFamilyProperty,
  FontSizeProperty,
  FontWeightProperty,
  LetterSpacingProperty,
  TextTransformProperty,
} from 'csstype'

declare global {
  interface Font {
    fontFamily: FontFamilyProperty
    fontWeight: FontWeightProperty
    fontSize: FontSizeProperty
    textTransform: TextTransformProperty
    letterSpacing: LetterSpacingProperty
  }

  interface Tokens {
    [token: string]: string
  }

  interface SemanticColors {
    [field: string]: Tokens
  }

  interface TypographyStyles {
    heading_1: Font
    heading_2: Font
    heading_3: Font
    heading_4: Font
    heading_5: Font
    heading_6: Font
    body: Font
    small: Font
    mini: Font
    action: Font
    action__small: Font
    action__large: Font
    code: Font
  }

  interface TachyonsConfig {
    semanticColors: SemanticColors
    typography: {
      styles: TypographyStyles
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/typings/typings.d.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface BasicStyle {
  id: string
  app: string
  name: string
}

interface Style extends BasicStyle {
  editable: boolean
  selected: boolean
  path: string
  config: TachyonsConfig
}

interface ActionMenuOption {
  label: string
  onClick: (style: Style) => void
}

interface StyleAssetInfo {
  type: 'path' | 'stylesheet'
  keepSheet?: boolean
  selected?: boolean
  value: string
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/components/Colors.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  colors: string[]
}

const Colors: React.FunctionComponent<Props> = ({ colors }) => (
  <div className="flex ba br2 b--muted-5 bg-muted-5 overflow-hidden">
    {colors.map((color, index) => (
      <div
        key={index}
        className={`w2 h2`}
        style={{
          backgroundColor: color,
        }}
      />
    ))}
  </div>
)

export default Colors

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/components/Typography.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  typography: Font
  textColor?: string
}

const Typography: React.FunctionComponent<Props> = ({
  typography: {
    fontFamily,
    fontSize,
    fontWeight,
    letterSpacing,
    textTransform,
  },
  textColor,
}) => {
  return (
    <div
      style={{
        color: textColor || 'black',
        fontFamily,
        fontSize,
        fontWeight,
        letterSpacing,
        textTransform,
      }}
    >
      Aa
    </div>
  )
}

export default Typography

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleList/mutations/CreateStyle.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Mutation, MutationFn } from 'react-apollo'
import CreateStyle from '../graphql/CreateStyle.graphql'

interface CreateStyleData {
  createStyle: {
    id: string
    app: string
    name: string
  }
}

interface CreateStyleVariables {
  name: string
  config?: TachyonsConfig
}

export type CreateStyleMutationFn = MutationFn<
  CreateStyleData,
  CreateStyleVariables
>

class CreateStyleMutation extends Mutation<
  CreateStyleData,
  CreateStyleVariables
> {
  public static defaultProps = {
    mutation: CreateStyle,
  }
}

export default CreateStyleMutation

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleList/mutations/DeleteStyle.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Mutation, MutationFn } from 'react-apollo'
import DeleteStyle from '../graphql/DeleteStyle.graphql'

interface DeleteStyleData {
  deleteStyle: {
    id: string
    app: string
    name: string
  }
}

interface DeleteStyleVariables {
  id: string
}

export type DeleteStyleMutationFn = MutationFn<
  DeleteStyleData,
  DeleteStyleVariables
>

class DeleteStyleMutation extends Mutation<
  DeleteStyleData,
  DeleteStyleVariables
> {
  public static defaultProps = {
    mutation: DeleteStyle,
  }
}

export default DeleteStyleMutation

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleList/mutations/SaveSelectedStyle.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Mutation, MutationFn } from 'react-apollo'
import SaveSelectedStyle from '../graphql/SaveSelectedStyle.graphql'

interface SaveSelectedStyleData {
  saveSelectedStyle: {
    id: string
    app: string
    name: string
  }
}

interface SaveSelectedStyleVariables {
  id: string
}

export type SaveSelectedStyleMutationFn = MutationFn<
  SaveSelectedStyleData,
  SaveSelectedStyleVariables
>

class SaveSelectedStyleMutation extends Mutation<
  SaveSelectedStyleData,
  SaveSelectedStyleVariables
> {
  public static defaultProps = {
    mutation: SaveSelectedStyle,
  }
}

export default SaveSelectedStyleMutation

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleList/queries/ListStyles.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Query, QueryResult } from 'react-apollo'
import ListStyles from '../graphql/ListStyles.graphql'

export interface ListStylesData {
  listStyles: Style[]
}

export type ListStylesQueryResult = QueryResult<ListStylesData>

class ListStylesQuery extends Query<ListStylesData, {}> {
  public static defaultProps = {
    query: ListStyles,
  }
}

export default ListStylesQuery

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleList/StyleCard/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useState } from 'react'
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'
import { ActionMenu, Card, IconOptionsDots, Tag } from 'vtex.styleguide'

import Colors from '../../components/Colors'
import Typography from '../../components/Typography'

type StyleFunction = (style: Style) => void
type StyleMutation = (style: Style) => Promise<unknown>

interface Props {
  deleteStyle: StyleMutation
  duplicateStyle: StyleMutation
  selectStyle: StyleMutation
  startEditing: StyleFunction
  style: Style
}

const messages = defineMessages({
  delete: {
    defaultMessage: 'Delete',
    id: 'admin/pages.editor.styles.card.menu.delete',
  },
  duplicate: {
    defaultMessage: 'Duplicate',
    id: 'admin/pages.editor.styles.card.menu.duplicate',
  },
  edit: {
    defaultMessage: 'Edit',
    id: 'admin/pages.editor.styles.card.menu.edit',
  },
  select: {
    defaultMessage: 'Select as store style',
    id: 'admin/pages.editor.styles.card.menu.select',
  },
})

const StyleCard: React.FunctionComponent<Props> = ({
  deleteStyle,
  duplicateStyle,
  selectStyle,
  startEditing,
  style,
  style: { app: appId, name, selected, editable, config },
}) => {
  const [isLoading, setIsLoading] = useState(false)
  const intl = useIntl()

  const setIsLoadingWrapper = (fn: () => Promise<unknown>) => async () => {
    setIsLoading(true)
    await fn()
    setIsLoading(false)
  }

  const {
    background: {
      action_primary: actionPrimary,
      action_secondary: actionSecondary,
      base,
      emphasis,
    },
  } = config.semanticColors

  const typography = config.typography.styles.heading_2

  const createMenuOptions = () => {
    const options = [
      editable && {
        label: intl.formatMessage(messages.edit),
        onClick: () => startEditing(style),
      },
      !selected && {
        label: intl.formatMessage(messages.select),
        onClick: setIsLoadingWrapper(() => selectStyle(style)),
      },
      {
        label: intl.formatMessage(messages.duplicate),
        onClick: setIsLoadingWrapper(() => duplicateStyle(style)),
      },
      !selected &&
        editable && {
          label: intl.formatMessage(messages.delete),
          onClick: async () => {
            setIsLoading(true)
            try {
              await deleteStyle(style)
            } catch (e) {
              setIsLoading(false)
            }
          },
        },
    ]
    return options.filter(option => option)
  }

  return (
    <div className="mh3 mb3">
      <Card noPadding>
        <div className="ph5 pt5 pb2">
          <div className="flex justify-between items-center mb4">
            <div className="flex items-center h2 w-80">
              <span className="mr5 truncate">{name}</span>
              {!editable && (
                <span className="f7 c-muted-2 truncate">
                  <FormattedMessage
                    id="admin/pages.editor.styles.card.name.app-subtitle"
                    defaultMessage="created by {app}"
                    values={{ app: appId.split('@')[0] }}
                  />
                </span>
              )}
            </div>
            <ActionMenu
              hideCaretIcon
              buttonProps={{
                icon: <IconOptionsDots />,
                iconPosition: 'right',
                isLoading,
                variation: 'tertiary',
              }}
              options={createMenuOptions()}
            />
          </div>
          <div className="flex justify-between items-center mb5">
            <div className="flex items-center">
              <Typography typography={typography} />
              <div className="pl5">
                <Colors
                  colors={[emphasis, actionPrimary, actionSecondary, base]}
                />
              </div>
            </div>
            {selected && (
              <Tag bgColor="#F71963" color="#FFFFFF">
                <FormattedMessage
                  id="admin/pages.editor.styles.card.current"
                  defaultMessage="Current"
                />
              </Tag>
            )}
          </div>
        </div>
      </Card>
    </div>
  )
}

export default StyleCard

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleList/graphql/DeleteStyle.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation DeleteStyle($id: String) {
  deleteStyle(id: $id) {
    id
    app
    name
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleList/graphql/SaveSelectedStyle.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation SaveSelectedStyle($id: String) {
  saveSelectedStyle(id: $id) {
    id
    app
    name
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleList/graphql/CreateStyle.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation CreateStyle($name: String, $config: ConfigInput) {
  createStyle(name: $name, config: $config) {
    id
    app
    name
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleList/graphql/ListStyles.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
query ListStyles {
  listStyles {
    id
    app
    name
    path
    editable
    selected
    config {
      semanticColors {
        background {
          base
          base__inverted
          action_primary
          action_secondary
          emphasis
          disabled
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
          muted_1
          muted_2
          muted_3
          muted_4
          muted_5
        }
        hover_background {
          action_primary
          action_secondary
          emphasis
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
          muted_1
          muted_2
          muted_3
          muted_4
          muted_5
        }
        active_background {
          action_primary
          action_secondary
          emphasis
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
          muted_1
          muted_2
          muted_3
          muted_4
          muted_5
        }
        text {
          action_primary
          action_secondary
          link
          emphasis
          disabled
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
          muted_1
          muted_2
          muted_3
          muted_4
          muted_5
        }
        visited_text {
          link
        }
        hover_text {
          action_primary
          action_secondary
          link
          emphasis
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
        }
        active_text {
          link
          emphasis
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
        }
        border {
          action_primary
          action_secondary
          emphasis
          disabled
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
          muted_1
          muted_2
          muted_3
          muted_4
          muted_5
        }
        hover_border {
          action_primary
          action_secondary
          emphasis
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
          muted_1
          muted_2
          muted_3
          muted_4
          muted_5
        }
        active_border {
          action_primary
          action_secondary
          emphasis
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
          muted_1
          muted_2
          muted_3
          muted_4
          muted_5
        }
        on {
          base
          base__inverted
          action_primary
          action_secondary
          emphasis
          disabled
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
          muted_1
          muted_2
          muted_3
          muted_4
          muted_5
        }
        hover_on {
          action_primary
          action_secondary
          emphasis
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
        }
        active_on {
          action_primary
          action_secondary
          emphasis
          success
          success__faded
          danger
          danger__faded
          warning
          warning__faded
        }
      }
      typography {
        styles {
          heading_1 {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          heading_2 {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          heading_3 {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          heading_4 {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          heading_5 {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          heading_6 {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          body {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          small {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          mini {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          action {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          action__small {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          action__large {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
          code {
            fontFamily
            fontWeight
            fontSize
            textTransform
            letterSpacing
          }
        }
      }
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleList/icons/CreateNewIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

// TODO: Use tachyons on stroke color
const CreateNewIcon: React.FunctionComponent = () => (
  <svg
    width="14"
    height="14"
    viewBox="0 0 14 14"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <path
      d="M7 4.42773V9.57059"
      stroke="#134CD8"
      strokeWidth="1.4"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <path
      d="M4.42969 7H9.57254"
      stroke="#134CD8"
      strokeWidth="1.4"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <path
      d="M12.1429 13H1.85714C1.384 13 1 12.616 1 12.1429V1.85714C1 1.384 1.384 1 1.85714 1H12.1429C12.616 1 13 1.384 13 1.85714V12.1429C13 12.616 12.616 13 12.1429 13Z"
      stroke="#134CD8"
      strokeWidth="1.4"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
  </svg>
)

export default CreateNewIcon

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/ColorsEditor/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useCallback, useMemo } from 'react'
import { FormattedMessage } from 'react-intl'
import { RouteComponentProps } from 'react-router'

import startCase from 'lodash/startCase'
import { fromPairs, groupBy, toPairs } from 'ramda'

import StyleEditorHeader from '../StyleEditorHeader'
import { EditorPath, IdParam } from '../StyleEditorRouter'
import fromTachyonsConfig from '../utils/colors'
import ColorEditor from './ColorEditor'
import ColorGroup from './ColorGroup'

interface Props extends RouteComponentProps<ColorRouteParams> {
  updateStyle: (partialConfig: Partial<TachyonsConfig>) => void
  config: TachyonsConfig
  onSave: () => void
}

const updateColor = (updateStyle: Props['updateStyle']) => (
  token: string,
  colorInfo: ColorInfo
) => {
  const { configField, color } = colorInfo
  const partialConfig = {
    semanticColors: {
      [configField]: {
        [token]: color,
      },
    },
  }
  updateStyle(partialConfig)
}

const ColorsEditor: React.FunctionComponent<Props> = ({
  config: {
    semanticColors,
    typography: {
      styles: { heading_6: font },
    },
  },
  match,
  updateStyle,
  history,
  onSave,
}) => {
  const {
    params: { id },
  } = match
  const info = fromTachyonsConfig(semanticColors)

  const colorsLabel = (
    <FormattedMessage
      id="admin/pages.editor.styles.edit.colors.title"
      defaultMessage="Colors"
    />
  )

  const saveButtonLabel = (
    <FormattedMessage
      id="admin/pages.editor.components.button.save"
      defaultMessage="Save"
    />
  )

  const MemoizedHeader = useMemo(() => {
    const Header = ({ name }: { name: string | React.ReactNode }) => (
      <StyleEditorHeader
        onButtonClick={onSave}
        buttonLabel={saveButtonLabel}
        title={name}
      />
    )

    return Header
  }, [onSave, saveButtonLabel])

  const startEditing = useCallback(
    (token: string) => history.push(EditorPath.colors.replace(IdParam, token)),
    [history]
  )

  const groups = useMemo(
    () =>
      groupBy(([token]) => {
        if (token === 'emphasis') {
          return 'emphasis'
        } else if (token.startsWith('base')) {
          return 'base'
        } else if (token.startsWith('muted')) {
          return 'muted'
        } else if (
          token.startsWith('action') ||
          token === 'link' ||
          token === 'disabled'
        ) {
          return 'action'
        } else {
          return 'feedback'
        }
      }, toPairs(info)),
    [info]
  )

  const colourGroups = useMemo(
    () =>
      toPairs(groups).map(([groupName, group]) => (
        <ColorGroup
          colorsInfo={fromPairs(group)}
          font={font}
          groupName={startCase(groupName as string)}
          key={groupName}
          semanticColors={semanticColors}
          startEditing={startEditing}
        />
      )),
    [font, groups, semanticColors, startEditing]
  )

  if (id) {
    return (
      <>
        <MemoizedHeader name={startCase(id)} />
        <div className="flex-grow-1 overflow-y-auto overflow-x-hidden">
          <ColorEditor
            updateColor={updateColor(updateStyle)}
            token={id}
            colorInfo={info[id]}
          />
        </div>
      </>
    )
  }

  return (
    <>
      <MemoizedHeader name={colorsLabel} />
      <div className="flex-grow-1">{colourGroups}</div>
    </>
  )
}

export default ColorsEditor

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/ColorsEditor/ColorGroup.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { useKeydownFromClick } from 'keydown-from-click'
import startCase from 'lodash/startCase'
import { find, mapObjIndexed, propEq } from 'ramda'
import React from 'react'

import Typography from '../../components/Typography'

const COLOR_BLOCK_SIZE = '40px'

interface Props {
  groupName: string
  colorsInfo: Colors
  font: Font
  startEditing: (token: string) => void
  semanticColors: SemanticColors
}

const ColorGroup: React.FunctionComponent<Props> = ({
  colorsInfo,
  font,
  groupName,
  semanticColors,
  startEditing,
}) => {
  return (
    <div className="bt b--muted-5">
      <div className="ph6 f4 flex items-center pv5">{groupName}</div>
      {Object.values(
        mapObjIndexed((info: ColorInfo[], token: string) => {
          return (
            <ColorPreview
              key={token}
              previewInfo={{
                backgroundColor: semanticColors.background.base,
                font,
                textColor: semanticColors.text.link,
              }}
              token={token}
              startEditing={startEditing}
              colorInfo={info}
            />
          )
        }, colorsInfo)
      )}
    </div>
  )
}

interface ColorPreviewProps {
  colorInfo: ColorInfo[]
  previewInfo: {
    font: Font
    backgroundColor: string
    textColor: string
  }
  startEditing: (token: string) => void
  token: string
}

const ColorPreview: React.FunctionComponent<ColorPreviewProps> = ({
  colorInfo,
  previewInfo,
  startEditing,
  token,
}) => {
  const handleClick = React.useCallback(() => startEditing(token), [
    startEditing,
    token,
  ])

  const handleKeyDown = useKeydownFromClick(handleClick)

  const backgroundColor = find(propEq('configField', 'background'), colorInfo)
  const textColor = find(propEq('configField', 'on'), colorInfo)

  return (
    <div
      className="pv5 ph6 flex items-center hover-bg-light-silver pointer outline-0"
      onClick={handleClick}
      onKeyDown={handleKeyDown}
      role="button"
      tabIndex={0}
    >
      <div
        className="ba br2 b--muted-5 flex justify-center items-center"
        style={{
          backgroundColor:
            (backgroundColor && backgroundColor.color) ||
            previewInfo.backgroundColor,
          height: COLOR_BLOCK_SIZE,
          width: COLOR_BLOCK_SIZE,
        }}
      >
        <Typography
          typography={previewInfo.font}
          textColor={(textColor && textColor.color) || previewInfo.textColor}
        />
      </div>
      <div className="ml3">{startCase(token)}</div>
    </div>
  )
}

export default ColorGroup

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/ColorsEditor/ColorEditor.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import startCase from 'lodash/startCase'
import { groupBy, mapObjIndexed } from 'ramda'
import React from 'react'
import { ColorPicker } from 'vtex.styleguide'

interface Props {
  updateColor: (token: string, color: ColorInfo) => void
  token: string
  colorInfo: ColorInfo[]
}

interface ColorPickerColors {
  hex?: string
  rgba?: {
    r: number
    g: number
    b: number
    a: number
  }
}

const getColorObject = (color: string): ColorPickerColors => {
  if (/^#[A-Fa-f0-9]{3}$/.test(color)) {
    return {
      hex: color.replace(
        /#([A-Fa-f0-9])([A-Fa-f0-9])([A-Fa-f0-9])/,
        '#$1$1$2$2$3$3'
      ),
    }
  }
  const rgbaGroup = color.match(/\(.*\)/)
  if (rgbaGroup) {
    const rgbaInfo = rgbaGroup[0].slice(1, -1).split(',')
    return {
      hex: '#ffffff',
      rgba: {
        a: parseFloat(rgbaInfo[3]),
        b: parseFloat(rgbaInfo[2]),
        g: parseFloat(rgbaInfo[1]),
        r: parseFloat(rgbaInfo[0]),
      },
    }
  }
  return { hex: color }
}

const fieldName = (id: string) => {
  switch (id) {
    case 'background':
      return 'Background'
    case 'on':
      return 'Text on background'
    case 'text':
      return 'Text'
    case 'border':
      return 'Border'
    default:
      return ''
  }
}

const ColorsEditor: React.FunctionComponent<Props> = ({
  colorInfo,
  updateColor,
  token,
}) => {
  const groups = groupBy(info => {
    return fieldName(info.path.split('.')[0])
  }, colorInfo)
  return (
    <div className="overflow-scroll h-100">
      {Object.values(
        mapObjIndexed((group: ColorInfo[], field: string) => {
          return (
            <div className="ph6 bt b--muted-5" key={field}>
              <div className="f4 bb b--muted-5 pv6">{field}</div>
              <div className="pv4">
                {group.map(info => {
                  const key = info.path.split('.')[1]

                  return (
                    <ColorPicker
                      key={key}
                      label={startCase(key)}
                      color={getColorObject(info.color)}
                      colorHistory={[]}
                      onChange={({ rgba }: ColorPickerColors) => {
                        if (rgba) {
                          const { r, g, b, a } = rgba
                          const newInfo = {
                            ...info,
                            color: `rgba(${r},${g},${b},${a})`,
                          }
                          updateColor(token, newInfo)
                        }
                      }}
                    />
                  )
                })}
              </div>
            </div>
          )
        }, groups)
      )}
    </div>
  )
}

export default ColorsEditor

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/mutations/DeleteFontFamily.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { DataProxy } from 'apollo-cache'
import { Mutation, MutationFn, QueryResult } from 'react-apollo'

import DeleteFontFamilyMutation from '../graphql/DeleteFontFamily.graphql'
import ListFonts from '../graphql/ListFonts.graphql'
import { ListFontsData } from '../queries/ListFontsQuery'

interface FontFamilyVariables {
  id: string
}

export interface DeleteFontFamilyData {
  deleteFontFamily: {
    id: string
  }
}

export type DeleteFontFamilyFn = MutationFn<
  DeleteFontFamilyData,
  FontFamilyVariables
>

type DeleteFontFamilyResult = QueryResult<DeleteFontFamilyData>

const updateFontsAfterDelete = (
  cache: DataProxy,
  result: DeleteFontFamilyResult
) => {
  const listData = cache.readQuery<ListFontsData>({ query: ListFonts })
  if (
    result.data == null ||
    result.data.deleteFontFamily == null ||
    listData == null
  ) {
    return
  }
  const { listFonts: families } = listData
  const { id: removedId } = result.data.deleteFontFamily
  const currentFonts = families.filter(family => family.id !== removedId)
  cache.writeQuery<ListFontsData>({
    data: { listFonts: currentFonts },
    query: ListFonts,
  })
}

class DeleteFontFamily extends Mutation<
  DeleteFontFamilyData,
  FontFamilyVariables
> {
  public static defaultProps = {
    mutation: DeleteFontFamilyMutation,
    update: updateFontsAfterDelete,
  }
}

export default DeleteFontFamily

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/mutations/SaveFontFamily.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { DataProxy } from 'apollo-cache'
import { Mutation, MutationFn, QueryResult } from 'react-apollo'

import ListFonts from '../graphql/ListFonts.graphql'
import SaveFontFamilyMutation from '../graphql/SaveFontFamily.graphql'
import { FontFamily, ListFontsData } from '../queries/ListFontsQuery'

interface FontFamilyVariables {
  font: FontFamilyInput
}

export interface FontFamilyInput {
  id?: string
  fontFamily: string
  fontFiles: FontFileInput[]
}

export type FontFileInput = FontFileUpload | FontFile

export interface FontFileUpload {
  fontFile: File
  fontWeight?: string
  fontStyle?: string
}

export interface FontFile {
  filename: string
  fontWeight: string
  fontStyle: string
}

export interface FontFamilyData {
  saveFontFamily: FontFamily
}

type SaveFontFamilyResult = QueryResult<FontFamilyData>

export type SaveFontFamilyFn = MutationFn<FontFamilyData, FontFamilyVariables>

const updateFontsAfterSave = (
  cache: DataProxy,
  result: SaveFontFamilyResult
) => {
  const listData = cache.readQuery<ListFontsData>({ query: ListFonts })

  if (result.data == null || listData == null) {
    return
  }

  const { listFonts: families } = listData
  const savedFamily = result.data.saveFontFamily
  const familyIndex = families.findIndex(({ id }) => id === savedFamily.id)
  families.splice(
    familyIndex < 0 ? families.length : familyIndex,
    1,
    savedFamily
  )

  cache.writeQuery<ListFontsData>({
    data: {
      listFonts: families,
    },
    query: ListFonts,
  })
}

class SaveFontFamily extends Mutation<FontFamilyData, FontFamilyVariables> {
  public static defaultProps = {
    mutation: SaveFontFamilyMutation,
    update: updateFontsAfterSave,
  }
}

export default SaveFontFamily

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/mutations/RenameStyle.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Mutation, MutationFn } from 'react-apollo'
import RenameStyle from '../graphql/RenameStyle.graphql'

interface RenameStyleData {
  renameStyle: {
    id: string
  }
}

interface RenameStyleVariables {
  id: string
  name: string
}

export type RenameStyleFunction = MutationFn<
  RenameStyleData,
  RenameStyleVariables
>

class RenameStyleMutation extends Mutation<
  RenameStyleData,
  RenameStyleVariables
> {
  public static defaultProps = {
    mutation: RenameStyle,
  }
}

export default RenameStyleMutation

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/mutations/UpdateStyle.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Mutation, MutationFn } from 'react-apollo'
import UpdateStyle from '../graphql/UpdateStyle.graphql'

interface UpdateStyleData {
  updateStyle: {
    path: string
    selected: boolean
  }
}

interface UpdateStyleVariables {
  id: string
  config: TachyonsConfig
}

export type UpdateStyleFunction = MutationFn<
  UpdateStyleData,
  UpdateStyleVariables
>

class UpdateStyleMutation extends Mutation<
  UpdateStyleData,
  UpdateStyleVariables
> {
  public static defaultProps = {
    mutation: UpdateStyle,
  }
}

export default UpdateStyleMutation

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/queries/ListFontsQuery.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Query, QueryResult } from 'react-apollo'
import ListFonts from '../graphql/ListFonts.graphql'
import { FontFile } from '../mutations/SaveFontFamily'

export interface FontFamily {
  id: string
  fontFamily: string
  fonts: FontFile[]
}

export interface ListFontsData {
  listFonts: FontFamily[]
}

export type ListFontsQueryResult = QueryResult<ListFontsData>

class ListFontsQuery extends Query<ListFontsData, {}> {
  public static defaultProps = {
    query: ListFonts,
  }
}

export default ListFontsQuery

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/queries/GenerateStyleSheet.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Query } from 'react-apollo'
import GenerateStyleSheet from '../graphql/GenerateStyleSheet.graphql'

export interface GenerateStyleSheetData {
  generateStyleSheet: string
}

interface GenerateStyleSheetVariables {
  config: TachyonsConfig
}

class GenerateStyleSheetQuery extends Query<
  GenerateStyleSheetData,
  GenerateStyleSheetVariables
> {
  public static defaultProps = {
    query: GenerateStyleSheet,
  }
}

export default GenerateStyleSheetQuery

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/utils/colors.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { forEachObjIndexed } from 'ramda'

const fromSemanticColors = (semanticColors: SemanticColors): Colors => {
  const getFieldPath = (name: string) => {
    let fieldPath = name.split('_')
    if (fieldPath.length === 1) {
      fieldPath = ['default'].concat(fieldPath)
    }
    return fieldPath.reverse().join('.')
  }

  const colors: Colors = {}
  forEachObjIndexed((tokens: Tokens, field: string) => {
    forEachObjIndexed((color: string, token: string) => {
      if (token.startsWith('__')) {
        return
      }
      const info: ColorInfo = {
        color,
        configField: field,
        path: getFieldPath(field),
      }
      colors[token] = (colors[token] || []).concat(info)
    }, tokens)
  }, semanticColors)

  return colors
}

export default fromSemanticColors

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/utils/typography.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import startCase from 'lodash/startCase'
import { xprod } from 'ramda'

import { FontFileInput } from '../mutations/SaveFontFamily'
import { FontFamily } from '../queries/ListFontsQuery'

export type FontFlavour = [FontStyle, FontWeight]

export enum FontWeight {
  thin = 'Thin',
  'extra-light' = 'Extra Light',
  light = 'Light',
  regular = 'Regular',
  medium = 'Medium',
  bold = 'Bold',
  'extra-bold' = 'Extra Bold',
  black = 'Black',
}

export enum FontStyle {
  normal = '',
  italic = 'Italic',
}

// Number representing the mega multiplier (2^20)
export const MEGA = 1048576
export const FONT_FILE_EXTENSIONS = [
  '.woff',
  '.woff2',
  '.ttf',
  '.otf',
  '.eot',
  '.svg',
]

const WEIGHT_STYLE_SEPARATOR = ','

export function flavourToString(value: FontFlavour | undefined) {
  return value && value.join(WEIGHT_STYLE_SEPARATOR)
}

export const STYLE_FLAVOUR_OPTIONS = xprod(
  Object.entries(FontStyle),
  Object.entries(FontWeight)
).map(([[styleValue, styleLabel], [weightValue, weightLabel]]) => ({
  label: (weightLabel + ' ' + styleLabel).trim(),
  value: flavourToString([styleValue, weightValue] as FontFlavour),
}))

export function stringToFlavour(value: string) {
  return value.split(WEIGHT_STYLE_SEPARATOR) as FontFlavour
}

export function getFontFlavour({
  fontStyle,
  fontWeight,
}: FontFileInput): FontFlavour {
  return [fontStyle, fontWeight] as FontFlavour
}

interface DropdownOption {
  value: string
  label: string
}

export function getTypeTokenDropdownOptions(
  key: keyof Font,
  font: Font,
  fontFamilies: FontFamily[]
): DropdownOption[] {
  const options = (() => {
    switch (key) {
      case 'fontFamily':
        return [
          ...fontFamilies.map(({ fontFamily, id }) => ({
            label: fontFamily,
            value: id,
          })),
          { label: 'Default', value: '' },
        ]
      case 'fontWeight':
        return [
          { label: 'Thin', value: '100' },
          { label: 'Extra Light', value: '200' },
          { label: 'Light', value: '300' },
          { label: 'Normal', value: '400' },
          { label: 'Medium', value: '500' },
          { label: 'Semi Bold', value: '600' },
          { label: 'Bold', value: '700' },
          { label: 'Extra Bold', value: '800' },
          { label: 'Black', value: '900' },
        ]
      case 'fontSize':
        return [
          { label: '48px', value: '3rem' },
          { label: '36px', value: '2.25rem' },
          { label: '24px', value: '1.5rem' },
          { label: '20px', value: '1.25rem' },
          { label: '16px', value: '1rem' },
          { label: '14px', value: '0.875rem' },
          { label: '12px', value: '0.75rem' },
        ]
      case 'letterSpacing':
        return [
          { label: 'Normal', value: 'normal' },
          { label: 'Tracked', value: '.1em' },
          { label: 'Tracked Tight', value: '-.05em' },
          { label: 'Tracked Mega', value: '.25em' },
          { label: 'Zero', value: '0' },
        ]
      case 'textTransform':
        return [
          { label: 'None', value: 'none' },
          { label: 'Capitalize', value: 'capitalize' },
          { label: 'Uppercase', value: 'uppercase' },
          { label: 'Lowercase', value: 'lowercase' },
        ]
    }
  })()

  const equalsCurrentValue = ({ value }: DropdownOption) =>
    value === (font[key] || '')

  const currentIfNotInOptions =
    options.find(equalsCurrentValue) == null
      ? [{ label: startCase(font[key]), value: font[key] as string }]
      : []

  return [...currentIfNotInOptions, ...options]
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/graphql/DeleteFontFamily.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation DeleteFontFamily($id: String) {
  deleteFontFamily(id: $id) {
    id
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/graphql/ListFonts.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
query ListFonts {
  listFonts {
    id
    fontFamily
    fonts {
      id
      filename
      fontWeight
      fontStyle
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/graphql/GenerateStyleSheet.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
query GenerateStyleSheet($config: ConfigInput) {
  generateStyleSheet(config: $config)
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/graphql/UpdateStyle.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation UpdateStyle($id: String, $config: ConfigInput) {
  updateStyle(id: $id, config: $config) {
    path
    selected
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/graphql/SaveFontFamily.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation SaveFontFamily($font: SaveFontFamilyInput) {
  saveFontFamily(font: $font) {
    id
    fontFamily
    fonts {
      filename
      fontWeight
      fontStyle
      id
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/graphql/RenameStyle.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation RenameStyle($id: String, $name: String) {
  renameStyle(id: $id, name: $name) {
    id
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/typography/TypeTokensEntry.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { useKeydownFromClick } from 'keydown-from-click'
import startCase from 'lodash/startCase'
import React from 'react'
import { RouteComponentProps } from 'react-router'

import { EditorPath, IdParam } from '../StyleEditorRouter'

interface EntryProps {
  name: string
  history: RouteComponentProps['history']
}

const TypeTokenEntry: React.FunctionComponent<EntryProps> = ({
  name,
  history,
}) => {
  const handleClick = React.useCallback(
    () => history.push(EditorPath.typeToken.replace(IdParam, name)),
    [history, name]
  )

  const handleKeyDown = useKeydownFromClick(handleClick)

  return (
    <div
      className="pointer flex justify-between items-center pv6 bb b--muted-4 outline-0"
      onClick={handleClick}
      onKeyDown={handleKeyDown}
      role="button"
      tabIndex={0}
    >
      <span className="f4">{startCase(name)}</span>
    </div>
  )
}

export default TypeTokenEntry

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/typography/TypographyEditor.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { defineMessages, FormattedMessage } from 'react-intl'

import AvailableEditor from '../AvailableEditor'
import StyleEditorHeader from '../StyleEditorHeader'
import { EditorPath } from '../StyleEditorRouter'

defineMessages({
  fontFamilyTitle: {
    defaultMessage: 'Font Family',
    id: 'admin/pages.editor.styles.edit.font-family.title',
  },
})

const TypographyEditor: React.FunctionComponent = () => {
  const title = (
    <FormattedMessage
      id="admin/pages.editor.styles.edit.typography.title"
      defaultMessage="Typography"
    />
  )

  return (
    <>
      <StyleEditorHeader title={title} />
      <div className="ph6">
        <AvailableEditor
          path={EditorPath.fontFamily}
          titleId="admin/pages.editor.styles.edit.font-family.title"
        />
        <AvailableEditor
          path={EditorPath.typeTokens}
          titleId="admin/pages.editor.styles.edit.type-tokens.title"
        />
      </div>
    </>
  )
}

export default TypographyEditor

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/typography/TypeTokenDropdown.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Dispatch } from 'react'

import startCase from 'lodash/startCase'
import { Dropdown } from 'vtex.styleguide'

import { FontFamily } from '../queries/ListFontsQuery'
import { getTypeTokenDropdownOptions } from '../utils/typography'

function getOnChange(key: keyof Font, dispatch: Dispatch<Partial<Font>>) {
  return (_: React.ChangeEvent, value: string) => {
    // Workaround since Dropdown does not work well with nil values
    dispatch({ [key]: value === '' ? null : value })
  }
}

function getValue(key: keyof Font, font: Font): string {
  return font[key] || ''
}

interface EntryProps {
  font: Font
  fontFamilies: FontFamily[]
  id: keyof Font
  dispatch: Dispatch<Partial<Font>>
}

const TypeTokenDropdown: React.FunctionComponent<EntryProps> = ({
  font,
  id,
  dispatch,
  fontFamilies,
}) => {
  return (
    <div className="flex justify-between items-center pv6 bb b--muted-4">
      <span className="f4">{startCase(id)}</span>
      <Dropdown
        variation="inline"
        onChange={getOnChange(id, dispatch)}
        options={getTypeTokenDropdownOptions(id, font, fontFamilies)}
        value={getValue(id, font)}
      />
    </div>
  )
}

export default TypeTokenDropdown

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/typography/TypeToken.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Dispatch } from 'react'
import { FormattedMessage } from 'react-intl'
import { RouteComponentProps } from 'react-router'

import startCase from 'lodash/startCase'
import { Spinner } from 'vtex.styleguide'

import ListFontsQuery, { ListFontsQueryResult } from '../queries/ListFontsQuery'
import StyleEditorHeader from '../StyleEditorHeader'
import TypeTokenDropdown from './TypeTokenDropdown'

interface WrapperProps extends RouteComponentProps<TypeTokenParams> {
  config: TachyonsConfig
  onSave: () => void
  updateConfig: (config: DeepPartial<TachyonsConfig>) => void
}

interface Props extends WrapperProps {
  result: ListFontsQueryResult
}

const TypeTokenWrapper: React.FunctionComponent<WrapperProps> = props => {
  return (
    <ListFontsQuery>
      {result => <TypeToken {...props} result={result} />}
    </ListFontsQuery>
  )
}

const TypeToken: React.FunctionComponent<Props> = ({
  match,
  result: { loading, data, error },
  config,
  onSave: saveStyle,
  updateConfig,
}) => {
  const token = match.params.id as keyof TypographyStyles
  const { [token]: font } = config.typography.styles

  if (loading) {
    return (
      <>
        <StyleEditorHeader title={startCase(token)} />
        <div className="pv8 flex justify-center">
          <Spinner />
        </div>
      </>
    )
  }

  if (error != null || data == null) {
    // TODO: add error state
    return <div />
  }

  const dispatch: Dispatch<Partial<Font>> = (fontChange: Partial<Font>) =>
    updateConfig({ typography: { styles: { [token]: fontChange } } })

  const saveLabel = (
    <FormattedMessage
      id="admin/pages.admin.pages.form.button.save"
      defaultMessage="Save"
    />
  )

  return (
    <>
      <StyleEditorHeader
        title={startCase(token)}
        buttonLabel={saveLabel}
        onButtonClick={saveStyle}
      />
      <div className="ph6">
        {Object.keys(font)
          .filter(key => !key.startsWith('__'))
          .map(key => (
            <TypeTokenDropdown
              font={font}
              dispatch={dispatch}
              fontFamilies={data.listFonts}
              key={key}
              id={key as keyof Font}
            />
          ))}
      </div>
    </>
  )
}

export default TypeTokenWrapper

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/typography/TypeTokensList.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { FormattedMessage } from 'react-intl'
import { RouteComponentProps } from 'react-router'

import StyleEditorHeader from '../StyleEditorHeader'
import TypeTokenEntry from './TypeTokensEntry'

interface Props extends RouteComponentProps {
  style: Style
}

const TypeTokensList: React.FunctionComponent<Props> = ({ history, style }) => {
  const title = (
    <FormattedMessage
      id="admin/pages.editor.styles.edit.type-tokens.title"
      defaultMessage="Type Tokens"
    />
  )

  const { styles } = style.config.typography
  const tokens = Object.entries(styles)
    .filter(([name]) => !name.startsWith('__'))
    .map(([name, value]) => ({ ...value, name }))

  return (
    <>
      <StyleEditorHeader title={title} />
      <div className="ph6">
        {tokens.map(({ name }) => (
          <TypeTokenEntry name={name} key={name} history={history} />
        ))}
      </div>
    </>
  )
}

export default TypeTokensList

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/typography/FontEditor.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useContext, useReducer, useState } from 'react'
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'
import { matchPath, RouteComponentProps } from 'react-router'
import { Button, Spinner, Tab, Tabs, ToastContext } from 'vtex.styleguide'
import { ApolloError } from 'apollo-client'

import DeleteFontFamily, {
  DeleteFontFamilyFn,
} from '../mutations/DeleteFontFamily'
import SaveFontFamily, {
  FontFileInput,
  SaveFontFamilyFn,
} from '../mutations/SaveFontFamily'
import ListFontsQuery, { ListFontsQueryResult } from '../queries/ListFontsQuery'
import StyleEditorHeader from '../StyleEditorHeader'
import { EditorPath, IdParam } from '../StyleEditorRouter'
import { FontFlavour } from '../utils/typography'
import FileFontEditor from './FileFontEditor'

interface FontFileAppend {
  type: 'append'
  files: FontFileInput[]
}

interface FontFileRemove {
  type: 'remove'
  index: number
}

interface FontFileUpdate {
  type: 'update'
  style: FontFlavour
  index: number
}

export type FontFileAction = FontFileAppend | FontFileRemove | FontFileUpdate

const {
  addFontSuccessMessage,
  fileLinkMessage,
  fileUploadMessage,
  removeFontFailureMessage,
  removeFontSuccessMessage,
} = defineMessages({
  addFontSuccessMessage: {
    defaultMessage: 'Font family saved succesfully.',
    id: 'admin/pages.editor.styles.edit.font-family.add-font-success',
  },
  fileLinkMessage: {
    defaultMessage: 'File link',
    id: 'admin/pages.editor.styles.edit.custom-font.file-link',
  },
  fileUploadMessage: {
    defaultMessage: 'Upload a file',
    id: 'admin/pages.editor.styles.edit.custom-font.file-upload',
  },
  removeFontFailureMessage: {
    defaultMessage: 'Cannot delete font family, because some style uses it.',
    id: 'admin/pages.editor.styles.edit.font-family.remove-font-failure',
  },
  removeFontSuccessMessage: {
    defaultMessage: 'Font family removed succesfully.',
    id: 'admin/pages.editor.styles.edit.font-family.remove-font-success',
  },
})

function reducer(
  prevState: FontFileInput[],
  action: FontFileAction
): FontFileInput[] {
  switch (action.type) {
    case 'append':
      return [...prevState, ...action.files]
    case 'remove':
      prevState.splice(action.index, 1)
      return [...prevState]
    case 'update':
      prevState[action.index] = {
        ...prevState[action.index],
        fontStyle: action.style[0],
        fontWeight: action.style[1],
      }

      return [...prevState]
  }
}

function canSave(family: string, files: FontFileInput[]) {
  return (
    family &&
    files.length > 0 &&
    files.every(
      ({ fontStyle, fontWeight }) => fontStyle != null && fontWeight != null
    )
  )
}

function hasErrorCode(error: ApolloError | void, code: string): boolean {
  if (error == null || error.graphQLErrors == null) {
    return false
  }

  const errorWithCode = error.graphQLErrors.find(
    ({ extensions }) =>
      extensions && extensions.exception && extensions.exception.code === code
  )

  return errorWithCode !== undefined
}

function FontEditorTitle() {
  return (
    <FormattedMessage
      id="admin/pages.editor.styles.edit.custom-font.title"
      defaultMessage="Custom Font"
    />
  )
}

function FontEditorLoading() {
  return (
    <>
      <StyleEditorHeader title={<FontEditorTitle />} />
      <div className="pv8 flex justify-center">
        <Spinner />
      </div>
    </>
  )
}

interface Props
  extends RouteComponentProps<CustomFontParams>,
    ListFontsQueryResult {
  deleteFont: DeleteFontFamilyFn
  saveFont: SaveFontFamilyFn
  mutationError: ApolloError | undefined
  loadingSave: boolean
  loadingDelete: boolean
}

const CustomFont: React.FunctionComponent<Props> = ({
  data,
  deleteFont,
  error,
  loading,
  loadingDelete,
  loadingSave,
  history,
  saveFont,
}) => {
  const intl = useIntl()
  const { customFontFile, customFontLink } = EditorPath
  const { pathname } = history.location

  const match = matchPath<CustomFontParams>(pathname, customFontFile)
  const paramId = match ? match.params.id : undefined

  const fonts = (data && data.listFonts) || []
  const editFamily = fonts.find(
    family => encodeURIComponent(family.id) === paramId
  ) || {
    fontFamily: '',
    fonts: [],
    id: undefined,
  }

  const filesReducer = useReducer(reducer, editFamily.fonts)
  const familyState = useState(editFamily.fontFamily)
  const [id] = useState<string | undefined>(editFamily.id)
  const { showToast } = useContext(ToastContext)

  const [fontFamily] = familyState
  const [fontFiles] = filesReducer

  if (loading) {
    return <FontEditorLoading />
  }

  if (error != null || data == null) {
    // TODO: add error state
    return <div />
  }

  const disableSave =
    !canSave(fontFamily, fontFiles) || loadingSave || loadingDelete

  const onSave = () => {
    saveFont({
      variables: { font: { fontFamily, fontFiles, id } },
    }).then(result => {
      if (result == null || result.data == null) {
        return // TODO: treat errors on delete
      }
      showToast(intl.formatMessage(addFontSuccessMessage))
      history.goBack()
    })
  }

  const disableDelete = loadingSave || loadingDelete
  const onDelete = () => {
    deleteFont({ variables: { id: id as string } })
      .then(() => {
        showToast(intl.formatMessage(removeFontSuccessMessage))
        history.goBack()
      })
      .catch(result => {
        if (hasErrorCode(result, 'USED_FONT_DELETE')) {
          showToast(intl.formatMessage(removeFontFailureMessage))
        }
      })
  }

  const fontFileTabProps = {
    active:
      matchPath(pathname, customFontFile) != null ||
      matchPath(pathname, EditorPath.customFont.replace(IdParam, '')) != null,
    key: 0,
    label: intl.formatMessage(fileUploadMessage),
    onClick: () =>
      history.replace(EditorPath.customFontFile.replace(IdParam, id || '')),
  }

  const fontLinkTabProps = {
    active: matchPath(pathname, customFontLink) != null,
    disabled: true,
    key: 1,
    label: intl.formatMessage(fileLinkMessage),
    onClick: () => history.replace(EditorPath.customFontLink),
  }

  return (
    <>
      <StyleEditorHeader title={<FontEditorTitle />}>
        <div>
          {id != null && (
            <Button
              variation="tertiary"
              size="small"
              isLoading={loadingDelete}
              onClick={onDelete}
              disabled={disableDelete}
            >
              <FormattedMessage
                id="admin/pages.admin.pages.form.button.delete"
                defaultMessage="Delete"
              />
            </Button>
          )}
          <Button
            variation="tertiary"
            size="small"
            isLoading={loadingSave}
            onClick={onSave}
            disabled={disableSave}
          >
            <FormattedMessage
              id="admin/pages.admin.pages.form.button.save"
              defaultMessage="Save"
            />
          </Button>
        </div>
      </StyleEditorHeader>
      <div className="pa7 h-100 overflow-y-auto flex flex-column">
        <Tabs>
          <Tab {...fontFileTabProps}>
            <FileFontEditor {...{ familyState, filesReducer, showToast }} />
          </Tab>
          <Tab {...fontLinkTabProps} />
        </Tabs>
      </div>
    </>
  )
}

type WrapperProps = RouteComponentProps<CustomFontParams>

const FontEditorWrapper: React.FunctionComponent<WrapperProps> = props => {
  return (
    <ListFontsQuery>
      {queryResult => (
        <DeleteFontFamily>
          {(deleteFont, { loading: deleteLoading, error: deleteError }) => (
            <SaveFontFamily>
              {(saveFont, { loading: saveLoading, error: saveError }) => (
                <CustomFont
                  {...props}
                  {...queryResult}
                  saveFont={saveFont}
                  deleteFont={deleteFont}
                  loadingSave={saveLoading}
                  loadingDelete={deleteLoading}
                  mutationError={deleteError || saveError}
                />
              )}
            </SaveFontFamily>
          )}
        </DeleteFontFamily>
      )}
    </ListFontsQuery>
  )
}

export default FontEditorWrapper

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/typography/FontFamilyList.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { FormattedMessage } from 'react-intl'
import { RouteComponentProps, withRouter } from 'react-router'

import { Button, Spinner } from 'vtex.styleguide'
import { ListFontsQueryResult } from '../queries/ListFontsQuery'
import ListFontsQuery from '../queries/ListFontsQuery'
import StyleEditorHeader from '../StyleEditorHeader'
import { EditorPath, IdParam } from '../StyleEditorRouter'
import FontFamilyEntry from './FontFamilyEntry'

interface Props extends RouteComponentProps, ListFontsQueryResult {}

const FontFamilyList: React.FunctionComponent<Props> = ({
  data,
  error,
  history,
  loading,
}) => {
  const title = (
    <FormattedMessage
      id="admin/pages.editor.styles.edit.font-family.title"
      defaultMessage="Font Family"
    />
  )

  if (loading) {
    return (
      <>
        <StyleEditorHeader title={title} />
        <div className="pv8 flex justify-center">
          <Spinner />
        </div>
      </>
    )
  }

  if (error != null || data == null) {
    // TODO: add error state
    return <div />
  }

  const addFontLabel = (
    <FormattedMessage
      id="admin/pages.editor.styles.edit.font-family.add-font"
      defaultMessage="Add Custom Font"
    />
  )

  const addFontOnClick = () =>
    history.push(EditorPath.customFontFile.replace(IdParam, ''))

  const addFontButton = (
    <Button collapseLeft variation="tertiary" onClick={addFontOnClick}>
      {addFontLabel}
    </Button>
  )

  const { listFonts } = data

  if (listFonts.length === 0) {
    return (
      <>
        <StyleEditorHeader title={title} />
        <div className="ph6 lh-copy">
          <p>
            <FormattedMessage
              id="admin/pages.editor.styles.edit.font-family.add-font-description"
              defaultMessage="You can add custom font families to your store, let’s add some?"
            />
          </p>
          {addFontButton}
        </div>
      </>
    )
  }

  return (
    <>
      <StyleEditorHeader
        title={title}
        buttonLabel={addFontLabel}
        onButtonClick={addFontOnClick}
      />
      <div className="ph6">
        {listFonts.map(fontFamily => (
          <FontFamilyEntry font={fontFamily} key={fontFamily.id} />
        ))}
      </div>
    </>
  )
}

const FontFamilyListWithRouter = withRouter(FontFamilyList)

const FontFamilyListWrapper: React.FunctionComponent = () => {
  return (
    <ListFontsQuery>
      {queryResult => <FontFamilyListWithRouter {...queryResult} />}
    </ListFontsQuery>
  )
}

export default FontFamilyListWrapper

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/typography/FileFontEditor.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Dispatch, SetStateAction, useCallback } from 'react'
import { DropzoneOptions, useDropzone } from 'react-dropzone'
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'
import {
  ActionMenu,
  ButtonWithIcon,
  Dropdown,
  IconOptionsDots,
  IconUpload,
  Input,
  ToastConsumerFunctions,
} from 'vtex.styleguide'

import { FontFileInput, FontFileUpload } from '../mutations/SaveFontFamily'
import {
  flavourToString,
  FONT_FILE_EXTENSIONS,
  FontFlavour,
  getFontFlavour,
  MEGA,
  stringToFlavour,
  STYLE_FLAVOUR_OPTIONS,
} from '../utils/typography'
import { FontFileAction } from './FontEditor'

const { pickStylePlaceholder, removeMessage } = defineMessages({
  pickStylePlaceholder: {
    defaultMessage: 'Pick a style',
    id: 'admin/pages.editor.styles.edit.font-family.style-dropdown-placeholder',
  },
  removeMessage: {
    defaultMessage: 'Remove',
    id: 'admin/pages.admin.redirects.form.button.remove',
  },
})

interface FileFontEditorProps {
  familyState: [string, Dispatch<SetStateAction<string>>]
  filesReducer: [FontFileInput[], Dispatch<FontFileAction>]
  showToast: ToastConsumerFunctions['showToast']
}

const FileFontEditor: React.FunctionComponent<FileFontEditorProps> = ({
  filesReducer,
  familyState,
  showToast,
}) => {
  const [files, dispatchFiles] = filesReducer
  const [family, setFamily] = familyState

  const onInputChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => setFamily(e.target.value),
    [setFamily]
  )

  const removeFile = (index: number) => dispatchFiles({ type: 'remove', index })

  const updateFile = (index: number, style: FontFlavour) =>
    dispatchFiles({ type: 'update', index, style })

  const onDrop = (acceptedFiles: File[], rejectedFiles: File[]): void => {
    if (rejectedFiles.length > 0) {
      showToast(
        'Uploaded files must be under 2MB and have one of the extensions: ' +
          FONT_FILE_EXTENSIONS.join(' ')
      )
    }

    if (acceptedFiles.length > 0) {
      dispatchFiles({
        files: acceptedFiles.map(fontFile => ({ fontFile })),
        type: 'append',
      })
    }
  }

  const isUpload = (file: FontFileInput): file is FontFileUpload =>
    (file as FontFileUpload).fontFile != null

  const getFileName = (file: FontFileInput): string =>
    isUpload(file) ? file.fontFile.name : file.filename

  return (
    <div className="mv6 w-100">
      <FormattedMessage
        id="admin/pages.editor.styles.edit.font-family.title"
        defaultMessage="Font Family"
      >
        {label => (
          <Input
            size="small"
            label={label}
            value={family}
            onChange={onInputChange}
          />
        )}
      </FormattedMessage>
      {files.map((file, index) => (
        <FontFileItem
          fileName={getFileName(file)}
          key={getFileName(file)}
          onRemove={() => removeFile(index)}
          onStyleUpdate={(style: FontFlavour) => updateFile(index, style)}
          style={getFontFlavour(files[index])}
        />
      ))}
      <FontFileUploadComponent onDrop={onDrop} />
    </div>
  )
}

interface FontFileItemProps {
  fileName: string
  onRemove: () => void
  onStyleUpdate: (style: FontFlavour) => void
  style?: FontFlavour
}

const FontFileItem: React.FunctionComponent<FontFileItemProps> = ({
  fileName,
  onRemove,
  onStyleUpdate,
  style,
}) => {
  const intl = useIntl()
  const actionMenuProps = {
    buttonProps: {
      icon: <IconOptionsDots color="currentColor" />,
      variation: 'tertiary',
    },
    options: [
      {
        label: intl.formatMessage(removeMessage),
        onClick: onRemove,
      },
    ],
  }

  const dropdownProps = {
    onChange: (_: React.ChangeEvent, value: string) =>
      onStyleUpdate(stringToFlavour(value)),
    options: STYLE_FLAVOUR_OPTIONS,
    placeholder: intl.formatMessage(pickStylePlaceholder),
    value: flavourToString(style),
    variation: 'inline',
  }

  return (
    <div className="mv5">
      <div className="flex items-center">
        <div className="mv3">{fileName}</div>
        <div className="flex-grow-1" />
        <ActionMenu {...actionMenuProps} />
      </div>
      <div className="flex items-center">
        <FormattedMessage
          id="admin/pages.editor.styles.edit.custom-font.style"
          defaultMessage="Style:"
        />
        <Dropdown {...dropdownProps} />
      </div>
    </div>
  )
}

interface FontFileUploadProps {
  onDrop: DropzoneOptions['onDrop']
}

const FontFileUploadComponent: React.FunctionComponent<FontFileUploadProps> = ({
  onDrop,
}) => {
  const { getRootProps, getInputProps } = useDropzone({
    accept: FONT_FILE_EXTENSIONS,
    maxSize: 2 * MEGA,
    onDrop,
  })
  return (
    <div className="mv6 w-100 flex">
      <p className="t-small">
        <FormattedMessage
          id="admin/pages.editor.styles.edit.custom-font.upload-description"
          defaultMessage="Upload your font family files in .ttf or .woff formats."
        />{' '}
        <span className="c-muted-2">
          <FormattedMessage
            id="admin/pages.editor.styles.edit.custom-font.upload-description-max-size"
            defaultMessage="(Maximum size 2MB)"
          />
        </span>
      </p>
      <div {...getRootProps()}>
        <ButtonWithIcon icon={<IconUpload />} variation="tertiary">
          <FormattedMessage
            id="admin/pages.editor.styles.edit.custom-font.upload"
            defaultMessage="Upload"
          />
          <input {...getInputProps()} />
        </ButtonWithIcon>
      </div>
    </div>
  )
}

export default FileFontEditor

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Styles/StyleEditor/typography/FontFamilyEntry.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { useKeydownFromClick } from 'keydown-from-click'
import React from 'react'
import { RouteComponentProps, withRouter } from 'react-router'

import { FontFamily } from '../queries/ListFontsQuery'
import { EditorPath, IdParam } from '../StyleEditorRouter'

interface Props extends RouteComponentProps {
  font: FontFamily
}

const FontFamilyEntry: React.FunctionComponent<Props> = ({ font, history }) => {
  const encodedId = React.useMemo(() => encodeURIComponent(font.id), [font.id])

  const isActive = React.useMemo(() => document.URL.includes(encodedId), [
    encodedId,
  ])

  const handleClick = React.useCallback(
    () => history.push(EditorPath.customFontFile.replace(IdParam, encodedId)),
    [encodedId, history]
  )

  const handleKeyDown = useKeydownFromClick(handleClick)

  return (
    <div
      aria-selected={isActive}
      className="pointer flex justify-between items-center pv6 bb b--muted-4 outline-0"
      onClick={handleClick}
      onKeyDown={handleKeyDown}
      role="option"
      tabIndex={0}
    >
      <span className="f4">{font.fontFamily}</span>
    </div>
  )
}

export default withRouter(FontFamilyEntry)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/DeviceSwitcher/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

import { useEditorContext } from '../../../EditorContext'

import { VIEWPORTS_BY_DEVICE } from './consts'
import DeviceItem from './DeviceItem'

import styles from './DeviceSwitcher.css'

interface Props {
  device: RenderContext['device']
}

const DeviceSwitcher: React.FC<Props> = ({ device }) => {
  const editor = useEditorContext()

  const handleClick = React.useCallback(
    ({ currentTarget }: Pick<React.MouseEvent, 'currentTarget'>) => {
      if (currentTarget && currentTarget instanceof HTMLElement) {
        editor.setViewport(currentTarget.dataset.type as Viewport)
      }
    },
    [editor]
  )

  const viewports = VIEWPORTS_BY_DEVICE[device === 'any' ? 'desktop' : device]

  return (
    <div className="flex">
      {viewports.map((deviceType, index) => {
        const isLast = index === viewports.length - 1

        return (
          <div className={!isLast ? styles['mr05'] : ''} key={deviceType}>
            <DeviceItem
              isActive={deviceType === editor.viewport}
              onClick={handleClick}
              position={index === 0 ? 'first' : isLast ? 'last' : 'middle'}
              type={deviceType}
            />
          </div>
        )
      })}
    </div>
  )
}

export default DeviceSwitcher

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/DeviceSwitcher/messages.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
export default {
  desktop: {
    defaultMessage: 'Desktop view',
    id: 'admin/pages.editor.topbar.button.device.desktop.tooltip',
  },
  mobile: {
    defaultMessage: 'Mobile view',
    id: 'admin/pages.editor.topbar.button.device.mobile.tooltip',
  },
  tablet: {
    defaultMessage: 'Tablet view',
    id: 'admin/pages.editor.topbar.button.device.tablet.tooltip',
  },
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/DeviceSwitcher/consts.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import IconDesktop from './icons/IconDesktop'
import IconMobile from './icons/IconMobile'
import IconTablet from './icons/IconTablet'

const AVAILABLE_VIEWPORTS: Viewport[] = ['mobile', 'tablet', 'desktop']

const AVAILABLE_MOBILE_VIEWPORTS: Viewport[] = ['mobile', 'tablet']

export const BORDER_BY_POSITION = {
  first: 'br2 br--left b--transparent',
  last: 'br2 br--right b--transparent',
  middle: 'b--transparent',
}

export const ICON_BY_VIEWPORT = {
  desktop: IconDesktop,
  mobile: IconMobile,
  tablet: IconTablet,
}

export const VIEWPORTS_BY_DEVICE: Record<
  RenderContext['device'],
  Viewport[]
> = {
  any: AVAILABLE_VIEWPORTS,
  desktop: AVAILABLE_VIEWPORTS,
  mobile: AVAILABLE_MOBILE_VIEWPORTS,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/DeviceSwitcher/DeviceItem.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { createKeydownFromClick } from 'keydown-from-click'
import React from 'react'
import { useIntl } from 'react-intl'
import { Tooltip } from 'vtex.styleguide'

import { useHover } from '../hooks'
import { BORDER_BY_POSITION, ICON_BY_VIEWPORT } from './consts'
import messages from './messages'

interface Props {
  isActive: boolean
  onClick: (e: Pick<React.MouseEvent, 'currentTarget'>) => void
  position: keyof typeof BORDER_BY_POSITION
  type: Viewport
}

const DeviceItem: React.FC<Props> = ({ onClick, position, isActive, type }) => {
  const handleKeyDown = createKeydownFromClick(onClick)

  const { handleMouseEnter, handleMouseLeave, hover } = useHover()
  const intl = useIntl()

  const Icon = ICON_BY_VIEWPORT[type]

  return (
    <Tooltip label={intl.formatMessage(messages[type])} position="bottom">
      <button
        className={`w2 h2 pointer flex justify-center items-center bg-white outline-0 ${
          isActive || hover ? 'c-action-primary' : 'c-on-disabled'
        } ${BORDER_BY_POSITION[position]}`}
        data-type={type}
        onClick={onClick}
        onKeyDown={handleKeyDown}
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
      >
        <Icon />
      </button>
    </Tooltip>
  )
}

export default DeviceItem

```