[
  {
    "path": ".babelrc",
    "content": "{\n  \"sourceType\": \"unambiguous\",\n  \"presets\": [\n    [\n      \"@babel/preset-env\",\n      {\n        \"shippedProposals\": true,\n        \"loose\": true\n      }\n    ],\n    \"@babel/preset-typescript\"\n  ],\n  \"plugins\": [\n    \"@babel/plugin-transform-shorthand-properties\",\n    \"@babel/plugin-transform-block-scoping\",\n    [\n      \"@babel/plugin-proposal-decorators\",\n      {\n        \"legacy\": true\n      }\n    ],\n    [\n      \"@babel/plugin-proposal-class-properties\",\n      {\n        \"loose\": true\n      }\n    ],\n    [\n      \"@babel/plugin-proposal-private-property-in-object\",\n      {\n        \"loose\": true\n      }\n    ],\n    [\n      \"@babel/plugin-proposal-private-methods\",\n      {\n        \"loose\": true\n      }\n    ],\n    \"@babel/plugin-proposal-export-default-from\",\n    \"@babel/plugin-syntax-dynamic-import\",\n    [\n      \"@babel/plugin-proposal-object-rest-spread\",\n      {\n        \"loose\": true,\n        \"useBuiltIns\": true\n      }\n    ],\n    \"@babel/plugin-transform-classes\",\n    \"@babel/plugin-transform-arrow-functions\",\n    \"@babel/plugin-transform-parameters\",\n    \"@babel/plugin-transform-destructuring\",\n    \"@babel/plugin-transform-spread\",\n    \"@babel/plugin-transform-for-of\",\n    \"babel-plugin-macros\",\n    \"@babel/plugin-proposal-optional-chaining\",\n    \"@babel/plugin-proposal-nullish-coalescing-operator\"\n  ]\n}\n"
  },
  {
    "path": ".codesandbox/ci.json",
    "content": "{\n  \"buildCommand\": \"build:prod\",\n  \"node\": \"16\",\n  \"sandboxes\": [\n    \"react95-template-xkfj0\"\n  ]\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = yes\ninsert_final_newline = yes\n"
  },
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  extends: [\n    'plugin:@typescript-eslint/recommended',\n    'airbnb',\n    'plugin:prettier/recommended',\n    'plugin:react-hooks/recommended'\n  ],\n  parser: '@typescript-eslint/parser',\n  plugins: ['react', 'prettier'],\n  env: {\n    browser: true,\n    es6: true,\n    jest: true\n  },\n  rules: {\n    '@typescript-eslint/explicit-module-boundary-types': 'off',\n    '@typescript-eslint/no-empty-function': 'off',\n    '@typescript-eslint/no-empty-interface': 'off',\n    '@typescript-eslint/no-use-before-define': 'off',\n    '@typescript-eslint/no-unused-vars': [\n      'error',\n      {\n        argsIgnorePattern: '^_\\\\d*$'\n      }\n    ],\n    'import/extensions': ['error', { js: 'never', ts: 'never', tsx: 'never' }],\n    'import/no-unresolved': [\n      'error',\n      // TODO: Remove ../../test/utils when TypeScript migration is complete\n      { ignore: ['react95', '../../test/utils'] }\n    ],\n    'import/no-extraneous-dependencies': ['error', { devDependencies: true }],\n    'import/prefer-default-export': 'off',\n    'jsx-a11y/label-has-associated-control': ['error', { assert: 'either' }],\n    'jsx-a11y/label-has-for': 'off',\n    'no-nested-ternary': 'off',\n    'prettier/prettier': 'error',\n    'react/forbid-prop-types': 'off',\n    'react/jsx-filename-extension': [\n      'warn',\n      { extensions: ['.js', '.jsx', '.tsx'] }\n    ],\n    'react/jsx-props-no-spreading': 'off',\n    'react/no-array-index-key': 'off',\n    'react/prop-types': 'off',\n    'react/require-default-props': 'off',\n    'react/static-property-placement': ['error', 'static public field']\n  },\n  overrides: [\n    {\n      files: ['*.spec.@(js|jsx|ts|tsx)', '*.stories.@(js|jsx|ts|tsx)'],\n      rules: {\n        'no-console': 'off'\n      }\n    },\n    {\n      files: ['*.@(ts|tsx)'],\n      rules: {\n        // This is handled by @typescript-eslint/no-unused-vars\n        'no-undef': 'off'\n      }\n    }\n  ],\n  settings: {\n    'import/parsers': {\n      '@typescript-eslint/parser': ['.ts', '.tsx']\n    },\n    'import/resolver': {\n      typescript: {}\n    }\n  }\n};\n"
  },
  {
    "path": ".firebaserc",
    "content": "{\n  \"projects\": {\n    \"default\": \"react95-storybook\"\n  }\n}\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: arturbien\npatreon: arturbien\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncustom: https://www.paypal.me/react95\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  pull_request:\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Git Checkout\n        uses: actions/checkout@v2\n\n      - name: Setup node\n        uses: actions/setup-node@v3\n        with:\n          node-version: 16\n\n      - name: Cache packages\n        uses: actions/cache@v3\n        with:\n          key: node_modules-v4-${{ hashFiles('yarn.lock') }}\n          path: |-\n            node_modules\n            */node_modules\n          restore-keys: 'node_modules-v4-'\n\n      - name: Yarn install\n        run: yarn install --ignore-optional --frozen-lockfile\n\n      - name: Lint\n        run: yarn run lint\n\n  type-check:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Git Checkout\n        uses: actions/checkout@v2\n\n      - name: Setup node\n        uses: actions/setup-node@v3\n        with:\n          node-version: 16\n\n      - name: Cache packages\n        uses: actions/cache@v3\n        with:\n          key: node_modules-v4-${{ hashFiles('yarn.lock') }}\n          path: |-\n            node_modules\n            */node_modules\n          restore-keys: 'node_modules-v4-'\n\n      - name: Yarn install\n        run: yarn install --ignore-optional --frozen-lockfile\n\n      - name: Type Check\n        run: yarn run typescript\n\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Git Checkout\n        uses: actions/checkout@v2\n\n      - name: Setup node\n        uses: actions/setup-node@v3\n        with:\n          node-version: 16\n\n      - name: Cache packages\n        uses: actions/cache@v3\n        with:\n          key: node_modules-v4-${{ hashFiles('yarn.lock') }}\n          path: |-\n            node_modules\n            */node_modules\n          restore-keys: 'node_modules-v4-'\n\n      - name: Yarn install\n        run: yarn install --ignore-optional --frozen-lockfile\n\n      - name: Test\n        run: yarn run test:ci\n\n  build-library:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Git Checkout\n        uses: actions/checkout@v2\n\n      - name: Setup node\n        uses: actions/setup-node@v3\n        with:\n          node-version: 16\n\n      - name: Cache packages\n        uses: actions/cache@v3\n        with:\n          key: node_modules-v4-${{ hashFiles('yarn.lock') }}\n          path: |-\n            node_modules\n            */node_modules\n          restore-keys: 'node_modules-v4-'\n\n      - name: Yarn install\n        run: yarn install --ignore-optional --frozen-lockfile\n\n      - name: Build library\n        run: yarn run build\n\n  build-storybook:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Git Checkout\n        uses: actions/checkout@v2\n\n      - name: Setup node\n        uses: actions/setup-node@v3\n        with:\n          node-version: 16\n\n      - name: Cache packages\n        uses: actions/cache@v3\n        with:\n          key: node_modules-v4-${{ hashFiles('yarn.lock') }}\n          path: |-\n            node_modules\n            */node_modules\n          restore-keys: 'node_modules-v4-'\n\n      - name: Yarn install\n        run: yarn install --ignore-optional --frozen-lockfile\n\n      - name: Build Storybook\n        run: yarn run build:storybook\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  push:\n    branches:\n      - master\n      - next\n      - beta\n      - alpha\n      - '*.x' # maintenance releases\n\njobs:\n  release-library:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Git Checkout\n        uses: actions/checkout@v2\n\n      - name: Setup node\n        uses: actions/setup-node@v3\n        with:\n          node-version: 16\n\n      - name: Cache packages\n        uses: actions/cache@v3\n        with:\n          key: node_modules-v4-${{ hashFiles('yarn.lock') }}\n          path: |-\n            node_modules\n            */node_modules\n          restore-keys: 'node_modules-v4-'\n\n      - name: Yarn install\n        run: yarn install --ignore-optional --frozen-lockfile\n\n      - name: Build library\n        run: yarn run build\n\n      - name: Deploy library\n        run: yarn run semantic-release || true\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n\n  release-storybook:\n    needs:\n      - release-library\n    if: github.ref == 'refs/heads/master'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Git Checkout\n        uses: actions/checkout@v2\n\n      - name: Setup node\n        uses: actions/setup-node@v3\n        with:\n          node-version: 16\n\n      - name: Cache packages\n        uses: actions/cache@v3\n        with:\n          key: node_modules-v4-${{ hashFiles('yarn.lock') }}\n          path: |-\n            node_modules\n            */node_modules\n          restore-keys: 'node_modules-v4-'\n\n      - name: Yarn install\n        run: yarn install --ignore-optional --frozen-lockfile\n\n      - name: Build Storybook\n        run: yarn run build:storybook\n\n      - name: Deploy Storybook\n        run: ./node_modules/.bin/firebase deploy --token=$FIREBASE_TOKEN\n        env:\n          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# library build\n/cjs\n/esm\n/themes\n/images\n/fonts\n/dist\n\n# storybook\n/storybook\n/.firebase\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# JetBrains IDEs\n.idea/*\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"arrowParens\": \"avoid\",\n  \"bracketSpacing\": true,\n  \"htmlWhitespaceSensitivity\": \"css\",\n  \"insertPragma\": false,\n  \"jsxBracketSameLine\": false,\n  \"jsxSingleQuote\": true,\n  \"printWidth\": 80,\n  \"proseWrap\": \"preserve\",\n  \"quoteProps\": \"as-needed\",\n  \"requirePragma\": false,\n  \"semi\": true,\n  \"singleQuote\": true,\n  \"tabWidth\": 2,\n  \"trailingComma\": \"none\",\n  \"useTabs\": false\n}\n"
  },
  {
    "path": ".storybook/decorators/withGlobalStyle.tsx",
    "content": "import { DecoratorFn } from '@storybook/react';\nimport React from 'react';\nimport { createGlobalStyle } from 'styled-components';\n\nimport ms_sans_serif from '../../src/assets/fonts/dist/ms_sans_serif.woff2';\nimport ms_sans_serif_bold from '../../src/assets/fonts/dist/ms_sans_serif_bold.woff2';\nimport styleReset from '../../src/common/styleReset';\n\nconst GlobalStyle = createGlobalStyle`\n  ${styleReset}\n  @font-face {\n    font-family: 'ms_sans_serif';\n    src: url('${ms_sans_serif}') format('woff2');\n    font-weight: 400;\n    font-style: normal\n  }\n  @font-face {\n    font-family: 'ms_sans_serif';\n    src: url('${ms_sans_serif_bold}') format(\"woff2\");\n    font-weight: bold;\n    font-style: normal\n  }\n  html, body, #root {\n   height: 100%;\n  }\n  #root > * {\n    height: 100%;\n    box-sizing: border-box;\n  }\n  body {\n    font-family: 'ms_sans_serif', 'sans-serif';\n  }\n`;\n\nexport const withGlobalStyle: DecoratorFn = story => (\n  <>\n    <GlobalStyle />\n    {story()}\n  </>\n);\n"
  },
  {
    "path": ".storybook/main.ts",
    "content": "import type { StorybookConfig } from '@storybook/react/types';\nimport type { PropItem } from 'react-docgen-typescript';\n\nconst path = require('path');\n\nconst storybookConfig: StorybookConfig = {\n  stories: ['../@(docs|src)/**/*.stories.@(tsx|mdx)'],\n  addons: [\n    {\n      name: '@storybook/addon-docs',\n      options: {\n        sourceLoaderOptions: {\n          injectStoryParameters: false\n        }\n      }\n    },\n    '@storybook/addon-storysource',\n    './theme-picker/register.ts'\n  ],\n  core: {\n    builder: 'webpack5'\n  },\n  features: {\n    babelModeV7: true,\n    storyStoreV7: true,\n    modernInlineRender: true,\n    postcss: false\n  },\n  typescript: {\n    check: false,\n    checkOptions: {},\n    reactDocgen: 'react-docgen-typescript',\n    reactDocgenTypescriptOptions: {\n      shouldExtractLiteralValuesFromEnum: true,\n      propFilter: (prop: PropItem) =>\n        prop.parent ? !/node_modules/.test(prop.parent.fileName) : true\n    }\n  },\n  webpackFinal: config => {\n    config.resolve = {\n      ...config.resolve,\n      alias: {\n        ...config.resolve?.alias,\n        react95: path.resolve(__dirname, '../src/index')\n      }\n    };\n\n    return config;\n  }\n};\n\nmodule.exports = storybookConfig;\n"
  },
  {
    "path": ".storybook/manager.css",
    "content": "/* Remove from the sidebar menu stories that contains \"unstable\" */\na[data-item-id$='-unstable'].sidebar-item,\na[data-item-id*='-unstable-'].sidebar-item,\nbutton[data-item-id$='-unstable'].sidebar-item,\nbutton[data-item-id$='-unstable-'].sidebar-item {\n  display: none !important;\n}\n"
  },
  {
    "path": ".storybook/manager.ts",
    "content": "import './manager.css';\n\nimport { addons } from '@storybook/addons';\nimport theme from './theme';\n\naddons.setConfig({\n  theme\n});\n"
  },
  {
    "path": ".storybook/preview.ts",
    "content": "import { DecoratorFn, Parameters } from '@storybook/react';\nimport { withGlobalStyle } from './decorators/withGlobalStyle';\nimport { withThemesProvider } from './theme-picker/ThemeProvider';\n\nexport const decorators: DecoratorFn[] = [withGlobalStyle, withThemesProvider];\n\nexport const parameters: Parameters = {\n  layout: 'fullscreen',\n  options: {\n    storySort: {\n      order: [\n        'Docs',\n        [\n          'Welcome to React95',\n          'Getting Started',\n          'Contributing',\n          'Submit your Project'\n        ],\n        'Controls',\n        'Environment',\n        'Layout',\n        'Typography',\n        'Other'\n      ]\n    }\n  }\n};\n"
  },
  {
    "path": ".storybook/theme-picker/ThemeButton.tsx",
    "content": "import React, { useCallback } from 'react';\nimport { ThemeProvider } from 'styled-components';\nimport { Button } from '../../src/Button/Button';\nimport { Theme } from '../../src/types';\n\nexport function ThemeButton({\n  active,\n  onChoose,\n  theme\n}: {\n  active: boolean;\n  onChoose: (themeName: string) => void;\n  theme: Theme;\n}) {\n  const handleClick = useCallback(() => {\n    onChoose(theme.name);\n  }, []);\n\n  return (\n    <ThemeProvider theme={theme}>\n      <Button active={active} onClick={handleClick}>\n        {theme.name}\n      </Button>\n    </ThemeProvider>\n  );\n}\n"
  },
  {
    "path": ".storybook/theme-picker/ThemeList.tsx",
    "content": "import { useAddonState } from '@storybook/api';\nimport React, { useCallback } from 'react';\nimport styled from 'styled-components';\n\nimport themes from '../../src/common/themes';\nimport { Theme } from '../../src/types';\nimport { THEMES_ID } from './constants';\nimport { ThemeButton } from './ThemeButton';\n\nconst {\n  original,\n  rainyDay,\n  vaporTeal,\n  theSixtiesUSA,\n  olive,\n  tokyoDark,\n  rose,\n  plum,\n  matrix,\n  travel,\n  ...otherThemes\n} = themes;\n\nconst themeList = [\n  original,\n  rainyDay,\n  vaporTeal,\n  theSixtiesUSA,\n  olive,\n  tokyoDark,\n  rose,\n  plum,\n  matrix,\n  travel,\n  ...Object.values(otherThemes)\n];\n\ntype ThemesProps = {\n  active?: boolean;\n};\n\nconst Wrapper = styled.div<{ theme: Theme }>`\n  display: grid;\n  padding: 1em;\n  gap: 1em;\n  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n  grid-template-rows: repeat(auto-fill, 40px);\n  background-color: ${({ theme }) => theme.material};\n`;\n\nexport function ThemeList({ active }: ThemesProps) {\n  const [themeName, setThemeName] = useAddonState(THEMES_ID, 'original');\n\n  const handleChoose = useCallback(\n    (newThemeName: string) => {\n      setThemeName(newThemeName);\n    },\n    [setThemeName]\n  );\n\n  if (!active) {\n    return <></>;\n  }\n\n  return (\n    <Wrapper key={THEMES_ID} theme={themes.original}>\n      {themeList.map(theme => (\n        <ThemeButton\n          active={themeName === theme.name}\n          key={theme.name}\n          onChoose={handleChoose}\n          theme={theme}\n        />\n      ))}\n    </Wrapper>\n  );\n}\n"
  },
  {
    "path": ".storybook/theme-picker/ThemeProvider.tsx",
    "content": "import { useAddonState } from '@storybook/client-api';\nimport { DecoratorFn } from '@storybook/react';\nimport React from 'react';\nimport { ThemeProvider } from 'styled-components';\n\nimport themes from '../../src/common/themes/index';\nimport { THEMES_ID } from './constants';\n\nexport const withThemesProvider: DecoratorFn = story => {\n  const [themeName] = useAddonState(THEMES_ID, 'original');\n\n  return (\n    <ThemeProvider theme={themes[themeName] ?? themes.original}>\n      {story()}\n    </ThemeProvider>\n  );\n};\n"
  },
  {
    "path": ".storybook/theme-picker/constants.ts",
    "content": "export const THEMES_ID = 'storybook/themes';\n"
  },
  {
    "path": ".storybook/theme-picker/register.ts",
    "content": "import addons, { makeDecorator, types } from '@storybook/addons';\nimport { THEMES_ID } from './constants';\nimport { ThemeList } from './ThemeList';\n\naddons.register(THEMES_ID, () => {\n  addons.addPanel(`${THEMES_ID}/panel`, {\n    title: 'Themes',\n    type: types.PANEL,\n    render: ThemeList\n  });\n});\n\nexport default makeDecorator({\n  name: 'withThemesProvider',\n  parameterName: 'theme',\n  wrapper: (getStory, context) => getStory(context)\n});\n"
  },
  {
    "path": ".storybook/theme.js",
    "content": "import { create } from '@storybook/theming';\n\nimport brandImage from './logo.png';\n\nexport default create({\n  base: 'light',\n  brandTitle: 'React95',\n  brandUrl: 'https://react95.io',\n  brandImage,\n  brandTarget: '_self',\n\n  // UI\n  appBg: '#dfdfdf',\n  appContentBg: '#ffffff',\n  appBorderColor: '#848584',\n  appBorderRadius: 0,\n\n  // Typography\n  fontBase: '\"ms_sans_serif\", sans-serif',\n  fontCode: 'monospace',\n\n  // Text colors\n  textColor: '#0a0a0a',\n  textInverseColor: 'rgba(255,255,255,0.9)',\n\n  // Toolbar default and active colors\n  barTextColor: '#c6c6c6',\n  barSelectedColor: '#fefefe',\n  barBg: '#060084',\n\n  // Form colors\n  inputBg: '#ffffff',\n  inputBorder: '#dfdfdf',\n  inputTextColor: '#0a0a0a',\n  inputBorderRadius: 0\n});\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Artur Bień\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">React95</h1>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/react95\"><img src=\"https://flat.badgen.net/npm/dt/react95\" alt=\"NPM\" /></a>\n  <a href=\"https://github.com/arturbien/React95/actions/workflows/release.yml\"><img src=\"https://github.com/arturbien/React95/actions/workflows/release.yml/badge.svg\" alt=\"release status\" /></a>\n  <a href=\"https://www.npmjs.com/package/react95\"><img src=\"https://flat.badgen.net/npm/v/react95\" alt=\"React95 version\" /></a>\n  <a href=\"https://www.npmjs.com/package/react95\"><img src=\"https://flat.badgen.net/npm/license/react95\" alt=\"React95 license\" /></a>\n  <a href=\"https://twitter.com/intent/follow?screen_name=react95_io\"><img src=\"https://img.shields.io/twitter/follow/react95_io\" alt=\"React95 license\" /></a>\n</p>\n<h3 align=\"center\">\n  <a href=\"https://storybook.react95.io/?path=/story/window--default\">Components</a> -\n  <a href=\"https://coins95.web.app/\">Demo app</a> -\n  <a href=\"https://github.com/react95-io/react95-native\">React Native</a> -\n  <a href=\"https://join.slack.com/t/react95/shared_invite/enQtOTA1NzEyNjAyNTc4LWYxZjU3NWRiMWJlMGJiMjhkNzE2MDA3ZmZjZDc1YmY0ODdlZjMwZDA1NWJiYWExYmY1NTJmNmE4OWVjNWFhMTE\">Slack</a> -\n  <a href=\"https://www.paypal.me/react95\">PayPal donation 💰</a>\n</h3>\n<p align=\"center\">\n  <b>Refreshed</b> Windows95 UI components for your modern React apps. <br /> Built with styled-components 💅</p>\n\n![hero](https://user-images.githubusercontent.com/28541613/81947711-28b05580-9601-11ea-964a-c3a6de998496.png)\n\n### Support\n\n- [Become a backer or sponsor on Patreon](https://www.patreon.com/arturbien)\n- [One-time donation via PayPal](https://www.paypal.me/react95)\n\n## Getting Started\n\nFirst, install component library and styled-components in your project directory:\n\n```sh\n# yarn\n$ yarn add react95 styled-components\n\n# npm\n$ npm install react95 styled-components\n```\n\nApply style reset, wrap your app with ThemeProvider with theme of your choice... and you are ready to go! 🚀\n\n```jsx\nimport React from 'react';\nimport { createGlobalStyle, ThemeProvider } from 'styled-components';\n\nimport { MenuList, MenuListItem, Separator, styleReset } from 'react95';\n// pick a theme of your choice\nimport original from 'react95/dist/themes/original';\n// original Windows95 font (optionally)\nimport ms_sans_serif from 'react95/dist/fonts/ms_sans_serif.woff2';\nimport ms_sans_serif_bold from 'react95/dist/fonts/ms_sans_serif_bold.woff2';\n\nconst GlobalStyles = createGlobalStyle`\n  ${styleReset}\n  @font-face {\n    font-family: 'ms_sans_serif';\n    src: url('${ms_sans_serif}') format('woff2');\n    font-weight: 400;\n    font-style: normal\n  }\n  @font-face {\n    font-family: 'ms_sans_serif';\n    src: url('${ms_sans_serif_bold}') format('woff2');\n    font-weight: bold;\n    font-style: normal\n  }\n  body {\n    font-family: 'ms_sans_serif';\n  }\n`;\n\nconst App = () => (\n  <div>\n    <GlobalStyles />\n    <ThemeProvider theme={original}>\n      <MenuList>\n        <MenuListItem>🎤 Sing</MenuListItem>\n        <MenuListItem>💃🏻 Dance</MenuListItem>\n        <Separator />\n        <MenuListItem disabled>😴 Sleep</MenuListItem>\n      </MenuList>\n    </ThemeProvider>\n  </div>\n);\n\nexport default App;\n```\n\n### Submit your project\n\nApps built with React95 will be featured on the official React95 [website](https://react95.io) 🤟🏻\n\n### Contributing\n\nAny help from UI / UX designers would be EXTREMELY appreciated. The challenge is to come up with new component designs / layouts that are broadly used in modern UIs, that weren't present back in 95.\n\nIf you want to help with the project, feel free to open pull requests and submit issues or component proposals. Let's bring this UI back to life ♥️\n"
  },
  {
    "path": "docs/Contributing.stories.mdx",
    "content": "import { Meta } from '@storybook/addon-docs';\n\n<Meta title='Docs/Contributing' />\n\n# Contributing\n\nAny help from UI/UX designers would be EXTREMELY appreciated. The challenge is\nto come up with new component designs/layouts that are broadly used in modern\nUIs, that weren't present back in 95.\n\nIf you want to help with the project, feel free to [open pull requests][1],\n[submit issues or component proposals][2] and join our [Slack channels][3]!\nLet's bring this UI back to life ♥️\n\n[1]: https://github.com/arturbien/react95/pulls\n[2]: https://github.com/arturbien/React95/issues\n[3]: https://join.slack.com/t/react95/shared_invite/enQtOTA1NzEyNjAyNTc4LWYxZjU3NWRiMWJlMGJiMjhkNzE2MDA3ZmZjZDc1YmY0ODdlZjMwZDA1NWJiYWExYmY1NTJmNmE4OWVjNWFhMTE\n"
  },
  {
    "path": "docs/Getting-Started.stories.mdx",
    "content": "import { Meta } from '@storybook/addon-docs';\n\n<Meta title='Docs/Getting Started' />\n\n# Installation\n\nReact95 is available as an [npm package](https://www.npmjs.com/package/react95).\n\n## npm\n\nTo install and save your `package.json` dependencies, run:\n\n```sh\n# yarn\nyarn add react95 styled-components\n\n# npm\nnpm install -S react95 styled-components\n```\n\nIn order to have `react95` working properly, you'll also need\n[styled-components 💅](https://github.com/styled-components/styled-components),\nthis way you can use custom themes and get the best of the library 🙂\n\n## Usage\n\nApply style reset, wrap your app content with ThemeProvider with theme of your\nchoice... and you are ready to go! 🚀\n\n```jsx\nimport React from 'react';\nimport { MenuList, MenuListItem, Separator, styleReset } from 'react95';\nimport { createGlobalStyle, ThemeProvider } from 'styled-components';\n\n/* Pick a theme of your choice */\nimport original from 'react95/dist/themes/original';\n\n/* Original Windows95 font (optional) */\nimport ms_sans_serif from 'react95/dist/fonts/ms_sans_serif.woff2';\nimport ms_sans_serif_bold from 'react95/dist/fonts/ms_sans_serif_bold.woff2';\n\nconst GlobalStyles = createGlobalStyle`\n  ${styleReset}\n  @font-face {\n    font-family: 'ms_sans_serif';\n    src: url('${ms_sans_serif}') format('woff2');\n    font-weight: 400;\n    font-style: normal\n  }\n  @font-face {\n    font-family: 'ms_sans_serif';\n    src: url('${ms_sans_serif_bold}') format('woff2');\n    font-weight: bold;\n    font-style: normal\n  }\n  body, input, select, textarea {\n    font-family: 'ms_sans_serif';\n  }\n`;\n\nconst App = () => (\n  <div>\n    <GlobalStyles />\n    <ThemeProvider theme={original}>\n      <MenuList>\n        <MenuListItem>🎤 Sing</MenuListItem>\n        <MenuListItem>💃🏻 Dance</MenuListItem>\n        <Separator />\n        <MenuListItem disabled>😴 Sleep</MenuListItem>\n      </MenuList>\n    </ThemeProvider>\n  </div>\n);\n\nexport default App;\n```\n"
  },
  {
    "path": "docs/Submit-your-Project.stories.mdx",
    "content": "import { Meta } from '@storybook/addon-docs';\n\n<Meta title='Docs/Submit your Project' />\n\n# Submit your Project\n\nApps built with React95 will be featured on the official React95 [website](https://react95.io) 🤟🏻\n\nIn order to submit your project, just drop a comment on [this issue](https://github.com/arturbien/React95/issues/25)!\n"
  },
  {
    "path": "docs/Welcome.stories.mdx",
    "content": "import { Meta } from '@storybook/addon-docs';\n\n<Meta title='Docs/Welcome to React95' />\n\n# Welcome to React95\n\n<a href='https://www.npmjs.com/package/react95'>\n  <img src='https://flat.badgen.net/npm/dt/react95' alt='NPM' />\n</a>\n&nbsp;\n<a href='https://www.npmjs.com/package/react95'>\n  <img src='https://flat.badgen.net/npm/v/react95' alt='React95 version' />\n</a>\n&nbsp;\n<a href='https://www.npmjs.com/package/react95'>\n  <img\n    src='https://flat.badgen.net/npm/license/react95'\n    alt='React95 license'\n  />\n</a>\n\n<h3>\n  <a href='?path=/story/controls-button--default'>Components</a>\n  &nbsp;-&nbsp;\n  <a href='https://coins95.web.app/'>Demo app</a>\n  &nbsp;-&nbsp;\n  <a href='https://react95.io/'>Website</a>\n  &nbsp;-&nbsp;\n  <a href='https://join.slack.com/t/react95/shared_invite/enQtOTA1NzEyNjAyNTc4LWYxZjU3NWRiMWJlMGJiMjhkNzE2MDA3ZmZjZDc1YmY0ODdlZjMwZDA1NWJiYWExYmY1NTJmNmE4OWVjNWFhMTE'>\n    Slack\n  </a>\n  &nbsp;-&nbsp;\n  <a href='https://www.paypal.me/react95'>PayPal donation 💰</a>\n</h3>\n\n**Refreshed** Windows 95 UI components for your modern React apps. Built with\n[styled-components](https://github.com/styled-components/styled-components) 💅.\n\n![hero](https://user-images.githubusercontent.com/28541613/81947711-28b05580-9601-11ea-964a-c3a6de998496.png)\n\n### Getting Started\n\nCheck out our [getting started](?path=/story/docs-getting-started--page) docs!\n\n### Motivation\n\nCreate modern mobile/web applications with the retro and old school Windows 95 style. Our goal is not to exactly recreate Windows95 components, but to provide a solid component library for current scenarios.\n\n### Support\n\n- [Become a backer or sponsor on Patreon](https://www.patreon.com/arturbien)\n- [One-time donation via PayPal](https://www.paypal.me/react95)\n"
  },
  {
    "path": "firebase.json",
    "content": "{\n  \"hosting\": {\n    \"public\": \"storybook\",\n    \"ignore\": [\n      \"firebase.json\",\n      \"**/.*\",\n      \"**/node_modules/**\"\n    ],\n    \"rewrites\": [\n      {\n        \"source\": \"**\",\n        \"destination\": \"/index.html\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  globals: {\n    'ts-jest': {\n      diagnostics: false,\n      isolatedModules: true\n    }\n  },\n  coverageReporters: ['text', 'html'],\n  preset: 'ts-jest/presets/default-esm',\n  setupFilesAfterEnv: ['<rootDir>/test/setup-test.ts'],\n  testEnvironment: 'jsdom'\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react95\",\n  \"version\": \"0.0.0-development\",\n  \"description\": \"Refreshed Windows95 UI components for modern web apps - React95\",\n  \"keywords\": [\n    \"react\",\n    \"styled-components\",\n    \"windows95\",\n    \"components\",\n    \"vaporwave\"\n  ],\n  \"author\": \"Artur Bień <artur.bien+react95@gmail.com> (https://www.linkedin.com/in/arturbien/)\",\n  \"funding\": [\n    {\n      \"type\": \"paypal\",\n      \"url\": \"https://www.paypal.me/react95\"\n    },\n    {\n      \"type\": \"patreon\",\n      \"url\": \"https://www.patreon.com/arturbien\"\n    }\n  ],\n  \"license\": \"MIT\",\n  \"repository\": \"git@github.com:arturbien/React95.git\",\n  \"homepage\": \"https://react95.io\",\n  \"main\": \"./dist/index.js\",\n  \"module\": \"./dist/index.mjs\",\n  \"types\": \"./dist/index.d.ts\",\n  \"files\": [\n    \"/dist\"\n  ],\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"start\": \"cross-env STORYBOOK_DISPLAY_WARNING=true DISPLAY_WARNING=true start-storybook -p 9009 --no-open\",\n    \"build:storybook\": \"cross-env STORYBOOK_DISPLAY_WARNING=true DISPLAY_WARNING=true build-storybook -o ./storybook\",\n    \"build\": \"rm -rf dist && yarn run build:prod\",\n    \"build:dev\": \"cross-env NODE_ENV=development rollup -c\",\n    \"build:prod\": \"cross-env NODE_ENV=production rollup -c\",\n    \"test\": \"jest ./src\",\n    \"test:ci\": \"jest ./src --maxWorkers=2\",\n    \"test:watch\": \"jest ./src --watch\",\n    \"test:coverage\": \"jest ./src --coverage\",\n    \"typescript\": \"tsc --noEmit\",\n    \"lint\": \"eslint --ext .js,.ts,.tsx src\",\n    \"lint:fix\": \"yarn run lint --fix\",\n    \"semantic-release\": \"semantic-release\",\n    \"cz\": \"git-cz\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">= 16.8.0\",\n    \"react-dom\": \">= 16.8.0\",\n    \"styled-components\": \">= 5.3.3\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.18.9\",\n    \"@babel/plugin-proposal-class-properties\": \"^7.18.6\",\n    \"@babel/plugin-proposal-decorators\": \"^7.18.10\",\n    \"@babel/plugin-proposal-export-default-from\": \"^7.18.10\",\n    \"@babel/plugin-proposal-nullish-coalescing-operator\": \"^7.18.6\",\n    \"@babel/plugin-proposal-object-rest-spread\": \"^7.18.9\",\n    \"@babel/plugin-proposal-optional-chaining\": \"^7.18.9\",\n    \"@babel/plugin-proposal-private-methods\": \"^7.18.6\",\n    \"@babel/plugin-proposal-private-property-in-object\": \"^7.18.6\",\n    \"@babel/plugin-syntax-dynamic-import\": \"^7.8.3\",\n    \"@babel/plugin-transform-arrow-functions\": \"^7.18.6\",\n    \"@babel/plugin-transform-block-scoping\": \"^7.18.9\",\n    \"@babel/plugin-transform-classes\": \"^7.18.9\",\n    \"@babel/plugin-transform-destructuring\": \"^7.18.9\",\n    \"@babel/plugin-transform-for-of\": \"^7.18.8\",\n    \"@babel/plugin-transform-parameters\": \"^7.18.8\",\n    \"@babel/plugin-transform-shorthand-properties\": \"^7.18.6\",\n    \"@babel/plugin-transform-spread\": \"^7.18.9\",\n    \"@babel/preset-env\": \"^7.18.10\",\n    \"@babel/preset-typescript\": \"^7.18.6\",\n    \"@rollup/plugin-typescript\": \"^8.3.4\",\n    \"@storybook/addon-docs\": \"6.5.10\",\n    \"@storybook/addon-storysource\": \"6.5.10\",\n    \"@storybook/builder-webpack5\": \"^6.5.10\",\n    \"@storybook/manager-webpack5\": \"^6.5.10\",\n    \"@storybook/react\": \"6.5.10\",\n    \"@testing-library/jest-dom\": \"^5.16.4\",\n    \"@testing-library/react\": \"^12.1.5\",\n    \"@testing-library/react-hooks\": \"^8.0.1\",\n    \"@types/jest\": \"^28.1.6\",\n    \"@types/react\": \"^18.0.15\",\n    \"@types/react-dom\": \"^18.0.6\",\n    \"@types/styled-components\": \"^5.1.25\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.32.0\",\n    \"@typescript-eslint/parser\": \"^5.32.0\",\n    \"babel-plugin-macros\": \"^3.1.0\",\n    \"babel-plugin-polyfill-corejs3\": \"^0.5.3\",\n    \"commitizen\": \"^4.2.5\",\n    \"cross-env\": \"^7.0.3\",\n    \"cz-conventional-changelog\": \"^3.3.0\",\n    \"esbuild\": \"^0.14.53\",\n    \"eslint\": \"^8.21.0\",\n    \"eslint-config-airbnb\": \"^19.0.4\",\n    \"eslint-config-prettier\": \"^8.5.0\",\n    \"eslint-import-resolver-typescript\": \"^3.4.0\",\n    \"eslint-plugin-import\": \"^2.26.0\",\n    \"eslint-plugin-jsx-a11y\": \"^6.6.1\",\n    \"eslint-plugin-prettier\": \"^4.2.1\",\n    \"eslint-plugin-react\": \"^7.30.1\",\n    \"eslint-plugin-react-hooks\": \"^4.6.0\",\n    \"firebase-tools\": \"^11.4.2\",\n    \"husky\": \"^8.0.1\",\n    \"jest\": \"^28.1.3\",\n    \"jest-environment-jsdom\": \"^28.1.3\",\n    \"jest-styled-components\": \"^7.0.8\",\n    \"lint-staged\": \"^13.0.3\",\n    \"prettier\": \"^2.7.1\",\n    \"react\": \"^17.0.2\",\n    \"react-docgen-typescript\": \"^2.2.2\",\n    \"react-dom\": \"^17.0.2\",\n    \"rimraf\": \"^3.0.2\",\n    \"rollup\": \"^2.77.2\",\n    \"rollup-plugin-copy\": \"^3.4.0\",\n    \"rollup-plugin-esbuild\": \"^4.9.1\",\n    \"rollup-plugin-replace\": \"^2.2.0\",\n    \"semantic-release\": \"^19.0.3\",\n    \"styled-components\": \"^5.3.5\",\n    \"ts-jest\": \"^28.0.7\",\n    \"typescript\": \"^4.7.4\",\n    \"webpack\": \"5\"\n  },\n  \"dependencies\": {},\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"lint-staged\"\n    }\n  },\n  \"lint-staged\": {\n    \"*.js\": [\n      \"eslint --fix\",\n      \"prettier --write\",\n      \"git add\"\n    ]\n  },\n  \"config\": {\n    \"commitizen\": {\n      \"path\": \"./node_modules/cz-conventional-changelog\"\n    }\n  }\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import typescript from '@rollup/plugin-typescript';\nimport copy from 'rollup-plugin-copy';\nimport esbuild from 'rollup-plugin-esbuild';\nimport replace from 'rollup-plugin-replace';\n\nconst NODE_ENV = process.env.NODE_ENV || 'development';\n\nconst baseBundle = {\n  external: id => !/^[./]/.test(id),\n  plugins: [\n    replace({\n      'process.env.NODE_ENV': JSON.stringify(NODE_ENV)\n    }),\n    esbuild()\n  ]\n};\n\nexport default [\n  {\n    ...baseBundle,\n    input: ['./src/index.ts', './src/types.ts'],\n    output: [\n      {\n        dir: 'dist',\n        entryFileNames: '[name].js',\n        exports: 'auto',\n        format: 'cjs',\n        preserveModules: true,\n        preserveModulesRoot: 'src'\n      },\n      {\n        dir: 'dist',\n        entryFileNames: '[name].mjs',\n        exports: 'auto',\n        format: 'es',\n        preserveModules: true,\n        preserveModulesRoot: 'src'\n      }\n    ],\n    plugins: [\n      ...baseBundle.plugins,\n      typescript({\n        tsconfig: './tsconfig.build.index.json',\n        declaration: true,\n        declarationDir: 'dist'\n      })\n    ]\n  },\n  {\n    ...baseBundle,\n    input: './src/common/themes/index.ts',\n    output: {\n      dir: 'dist/themes',\n      exports: 'default',\n      format: 'cjs',\n      preserveModules: true,\n      preserveModulesRoot: 'src/common/themes'\n    },\n    plugins: [\n      ...baseBundle.plugins,\n      copy({\n        targets: [\n          { src: './src/assets/fonts/dist/*', dest: './dist/fonts' },\n          { src: './src/assets/images/*', dest: './dist/images' }\n        ]\n      }),\n      typescript({\n        tsconfig: './tsconfig.build.themes.json',\n        declaration: true,\n        declarationDir: 'dist/themes'\n      })\n    ]\n  }\n];\n"
  },
  {
    "path": "src/Anchor/Anchor.spec.tsx",
    "content": "import React from 'react';\n\nimport { render } from '@testing-library/react';\n\nimport { Anchor } from './Anchor';\n\nconst defaultProps = {\n  children: '',\n  href: ''\n};\n\ndescribe('<Anchor />', () => {\n  it('should render href', () => {\n    const { container } = render(\n      <Anchor {...defaultProps} href='http://yoda.com' />\n    );\n    const anchorEl = container.firstChild;\n\n    expect(anchorEl).toHaveAttribute('href', 'http://yoda.com');\n  });\n\n  it('should render children', () => {\n    const { container } = render(\n      <Anchor {...defaultProps}>You shall pass</Anchor>\n    );\n    const anchorEl = container.firstChild;\n\n    expect(anchorEl).toHaveTextContent('You shall pass');\n  });\n\n  it('should render custom style', () => {\n    const { container } = render(\n      <Anchor {...defaultProps} style={{ color: 'papayawhip' }} />\n    );\n    const anchorEl = container.firstChild;\n\n    expect(anchorEl).toHaveAttribute('style', 'color: papayawhip;');\n  });\n\n  it('should render custom props', () => {\n    const customProps = { target: '_blank' };\n    const { container } = render(<Anchor {...defaultProps} {...customProps} />);\n    const anchorEl = container.firstChild;\n\n    expect(anchorEl).toHaveAttribute('target', '_blank');\n  });\n});\n"
  },
  {
    "path": "src/Anchor/Anchor.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Anchor } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.material};\n`;\n\nexport default {\n  title: 'Typography/Anchor',\n  component: Anchor,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Anchor>;\n\nexport function Default() {\n  return (\n    <h1>\n      Everybody likes{' '}\n      <Anchor href='https://expensive.toys' target='_blank'>\n        https://expensive.toys\n      </Anchor>\n    </h1>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/Anchor/Anchor.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\n\nimport { CommonStyledProps } from '../types';\n\ntype AnchorProps = {\n  children: React.ReactNode;\n  underline?: boolean;\n} & React.AnchorHTMLAttributes<HTMLAnchorElement> &\n  CommonStyledProps;\n\nconst StyledAnchor = styled.a<{ underline: boolean }>`\n  color: ${({ theme }) => theme.anchor};\n  font-size: inherit;\n  text-decoration: ${({ underline }) => (underline ? 'underline' : 'none')};\n  &:visited {\n    color: ${({ theme }) => theme.anchorVisited};\n  }\n`;\n\nconst Anchor = forwardRef<HTMLAnchorElement, AnchorProps>(\n  ({ children, underline = true, ...otherProps }: AnchorProps, ref) => {\n    return (\n      <StyledAnchor ref={ref} underline={underline} {...otherProps}>\n        {children}\n      </StyledAnchor>\n    );\n  }\n);\n\nAnchor.displayName = 'Anchor';\n\nexport { Anchor, AnchorProps };\n"
  },
  {
    "path": "src/AppBar/AppBar.spec.tsx",
    "content": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { AppBar } from './AppBar';\n\nconst defaultProps = { children: '' };\n\ndescribe('<AppBar />', () => {\n  it('should render header', () => {\n    const { container } = render(<AppBar {...defaultProps} />);\n    const headerEl = container.firstElementChild;\n\n    expect(headerEl && headerEl.tagName).toBe('HEADER');\n  });\n\n  it('should render children', () => {\n    const { container } = render(<AppBar>A nice app bar</AppBar>);\n    const headerEl = container.firstElementChild;\n\n    expect(headerEl).toHaveTextContent('A nice app bar');\n  });\n\n  it('should render fixed prop properly', () => {\n    const { container, rerender } = render(<AppBar {...defaultProps} fixed />);\n    const headerEl = container.firstElementChild;\n\n    expect(headerEl).toHaveStyleRule('position', 'fixed');\n\n    rerender(<AppBar {...defaultProps} fixed={false} />);\n\n    expect(headerEl).toHaveStyleRule('position', 'absolute');\n  });\n\n  it('should render position prop properly', () => {\n    const { container } = render(\n      <AppBar {...defaultProps} position='sticky' />\n    );\n    const headerEl = container.firstElementChild;\n\n    expect(headerEl).toHaveStyleRule('position', 'sticky');\n  });\n\n  it('should custom style', () => {\n    const { container } = render(\n      <AppBar {...defaultProps} style={{ backgroundColor: 'papayawhip' }} />\n    );\n    const headerEl = container.firstElementChild;\n\n    expect(headerEl).toHaveAttribute('style', 'background-color: papayawhip;');\n  });\n\n  it('should render custom props', () => {\n    const customProps = { title: 'cool-header' };\n    const { container } = render(<AppBar {...defaultProps} {...customProps} />);\n    const headerEl = container.firstElementChild;\n\n    expect(headerEl).toHaveAttribute('title', 'cool-header');\n  });\n});\n"
  },
  {
    "path": "src/AppBar/AppBar.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport {\n  AppBar,\n  Button,\n  MenuList,\n  MenuListItem,\n  Separator,\n  TextInput,\n  Toolbar\n} from 'react95';\nimport styled from 'styled-components';\nimport logoIMG from '../assets/images/logo.png';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n`;\n\nexport default {\n  title: 'Environment/AppBar',\n  component: AppBar,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof AppBar>;\n\nexport function Default() {\n  const [open, setOpen] = useState(false);\n\n  return (\n    <AppBar>\n      <Toolbar style={{ justifyContent: 'space-between' }}>\n        <div style={{ position: 'relative', display: 'inline-block' }}>\n          <Button\n            onClick={() => setOpen(!open)}\n            active={open}\n            style={{ fontWeight: 'bold' }}\n          >\n            <img\n              src={logoIMG}\n              alt='react95 logo'\n              style={{ height: '20px', marginRight: 4 }}\n            />\n            Start\n          </Button>\n          {open && (\n            <MenuList\n              style={{\n                position: 'absolute',\n                left: '0',\n                top: '100%'\n              }}\n              onClick={() => setOpen(false)}\n            >\n              <MenuListItem>\n                <span role='img' aria-label='👨‍💻'>\n                  👨‍💻\n                </span>\n                Profile\n              </MenuListItem>\n              <MenuListItem>\n                <span role='img' aria-label='📁'>\n                  📁\n                </span>\n                My account\n              </MenuListItem>\n              <Separator />\n              <MenuListItem disabled>\n                <span role='img' aria-label='🔙'>\n                  🔙\n                </span>\n                Logout\n              </MenuListItem>\n            </MenuList>\n          )}\n        </div>\n\n        <TextInput placeholder='Search...' width={150} />\n      </Toolbar>\n    </AppBar>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/AppBar/AppBar.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled, { CSSProperties } from 'styled-components';\n\nimport { createBorderStyles, createBoxStyles } from '../common';\nimport { CommonStyledProps } from '../types';\n\ntype AppBarProps = {\n  children: React.ReactNode;\n  /** @deprecated Use `position` instead */\n  fixed?: boolean;\n  position?: CSSProperties['position'];\n} & React.HTMLAttributes<HTMLElement> &\n  CommonStyledProps;\n\nconst StyledAppBar = styled.header<AppBarProps>`\n  ${createBorderStyles()};\n  ${createBoxStyles()};\n\n  position: ${props => props.position ?? (props.fixed ? 'fixed' : 'absolute')};\n  top: 0;\n  right: 0;\n  left: auto;\n  display: flex;\n  flex-direction: column;\n  width: 100%;\n`;\n\nconst AppBar = forwardRef<HTMLElement, AppBarProps>(\n  ({ children, fixed = true, position = 'fixed', ...otherProps }, ref) => {\n    return (\n      <StyledAppBar\n        fixed={fixed}\n        position={fixed !== false ? position : undefined}\n        ref={ref}\n        {...otherProps}\n      >\n        {children}\n      </StyledAppBar>\n    );\n  }\n);\n\nAppBar.displayName = 'AppBar';\n\nexport { AppBar, AppBarProps };\n"
  },
  {
    "path": "src/Avatar/Avatar.spec.tsx",
    "content": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { renderWithTheme, theme } from '../../test/utils';\n\nimport { Avatar } from './Avatar';\n\ndescribe('<Avatar />', () => {\n  it('should render component', () => {\n    const { container } = render(<Avatar />);\n\n    expect(container).toBeInTheDocument();\n  });\n\n  it('should render children', () => {\n    const { container } = render(<Avatar>Avatar children</Avatar>);\n    const avatarEl = container.firstElementChild;\n\n    expect(avatarEl && avatarEl.innerHTML).toBe('Avatar children');\n  });\n\n  it('should handle border properly', () => {\n    const { container, rerender } = renderWithTheme(\n      <Avatar noBorder={false} />\n    );\n    const avatarEl = container.firstElementChild;\n\n    expect(avatarEl).toHaveStyleRule(\n      'border-top',\n      `2px solid ${theme.borderDark}`\n    );\n\n    rerender(<Avatar noBorder />);\n\n    expect(avatarEl).not.toHaveStyleRule('border-top', '');\n  });\n\n  it('should handle square properly', () => {\n    const { container, rerender } = render(<Avatar square />);\n    const avatarEl = container.firstElementChild;\n\n    expect(avatarEl).toHaveStyleRule('border-radius', '0');\n\n    rerender(<Avatar square={false} />);\n\n    expect(avatarEl).toHaveStyleRule('border-radius', '50%');\n  });\n\n  it('should render with source', async () => {\n    const catGif = 'https://cdn2.thecatapi.com/images/1ac.gif';\n    const { findByAltText } = render(<Avatar src={catGif} alt='cat avatar' />);\n    const imageEl = (await findByAltText('cat avatar')) as HTMLImageElement;\n\n    expect(imageEl && imageEl.src).toBe(catGif);\n  });\n\n  it('should render source with priority over children', async () => {\n    const catGif = 'https://cdn2.thecatapi.com/images/1ac.gif';\n    const { queryByText } = render(\n      <Avatar src={catGif} alt='cat avatar'>\n        Cats are cool\n      </Avatar>\n    );\n    const content = await queryByText(/cats are cool/i);\n\n    expect(content).toBeNull();\n  });\n\n  describe('prop: size', () => {\n    it('should set proper size', () => {\n      const { container } = renderWithTheme(<Avatar size='85%' />);\n      const avatarEl = container.firstElementChild;\n\n      expect(avatarEl).toHaveStyleRule('width', '85%');\n      expect(avatarEl).toHaveStyleRule('height', '85%');\n    });\n\n    it('when passed a number, sets size in px', () => {\n      const { container } = renderWithTheme(<Avatar size={25} />);\n      const avatarEl = container.firstElementChild;\n\n      expect(avatarEl).toHaveStyleRule('width', '25px');\n      expect(avatarEl).toHaveStyleRule('height', '25px');\n    });\n  });\n});\n"
  },
  {
    "path": "src/Avatar/Avatar.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Avatar } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.material};\n  & > div > * {\n    margin-right: 1rem;\n  }\n`;\n\nexport default {\n  title: 'Other/Avatar',\n  component: Avatar,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Avatar>;\n\nexport function Default() {\n  return (\n    <div style={{ display: 'inline-flex' }}>\n      <Avatar size={50} src='https://placekitten.com/100/100' />\n      <Avatar noBorder size={50} src='https://placedog.net/100/100' />\n      <Avatar size={50} style={{ background: 'palevioletred' }}>\n        AK\n      </Avatar>\n      <Avatar square size={50}>\n        <span role='img' aria-label='🚀'>\n          🚀\n        </span>\n      </Avatar>\n    </div>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/Avatar/Avatar.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { getSize } from '../common/utils';\nimport { CommonStyledProps } from '../types';\n\ntype AvatarProps = {\n  alt?: string;\n  children?: React.ReactNode;\n  noBorder?: boolean;\n  size?: string | number;\n  square?: boolean;\n  src?: string;\n} & React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\nconst StyledAvatar = styled.div<\n  Pick<AvatarProps, 'noBorder' | 'square' | 'src'> & { size?: string }\n>`\n  display: inline-block;\n  box-sizing: border-box;\n  object-fit: contain;\n  ${({ size }) =>\n    `\n    height: ${size};\n    width: ${size};\n    `}\n  border-radius: ${({ square }) => (square ? 0 : '50%')};\n  overflow: hidden;\n  ${({ noBorder, theme }) =>\n    !noBorder &&\n    `\n    border-top: 2px solid ${theme.borderDark};\n    border-left: 2px solid ${theme.borderDark};\n    border-bottom: 2px solid ${theme.borderLightest};\n    border-right: 2px solid ${theme.borderLightest};\n    background: ${theme.material};\n  `}\n  ${({ src }) =>\n    !src &&\n    `\n    display: flex;\n    align-items: center;\n    justify-content: space-around;\n    font-weight: bold;\n    font-size: 1rem;\n  `}\n`;\n\nconst StyledAvatarImg = styled.img`\n  display: block;\n  object-fit: contain;\n  width: 100%;\n  height: 100%;\n`;\n\nconst Avatar = forwardRef<HTMLDivElement, AvatarProps>(\n  (\n    {\n      alt = '',\n      children,\n      noBorder = false,\n      size = 35,\n      square = false,\n      src,\n      ...otherProps\n    },\n    ref\n  ) => {\n    return (\n      <StyledAvatar\n        noBorder={noBorder}\n        ref={ref}\n        size={getSize(size)}\n        square={square}\n        src={src}\n        {...otherProps}\n      >\n        {src ? <StyledAvatarImg src={src} alt={alt} /> : children}\n      </StyledAvatar>\n    );\n  }\n);\n\nAvatar.displayName = 'Avatar';\n\nexport { Avatar, AvatarProps };\n"
  },
  {
    "path": "src/Button/Button.spec.tsx",
    "content": "import { fireEvent, render } from '@testing-library/react';\nimport React from 'react';\n\nimport { renderWithTheme, theme } from '../../test/utils';\nimport { blockSizes } from '../common/system';\n\nimport { Button } from './Button';\n\nconst defaultProps = {\n  children: 'click me'\n};\n\ndescribe('<Button />', () => {\n  it('should render button', () => {\n    const { getByRole } = render(<Button {...defaultProps} />);\n    const button = getByRole('button');\n\n    expect(button).toBeInTheDocument();\n    expect(button.tagName).toBe('BUTTON');\n  });\n\n  it('should handle different types', () => {\n    const { getByRole } = render(<Button {...defaultProps} type='submit' />);\n    const button = getByRole('button');\n\n    expect(button).toHaveAttribute('type', 'submit');\n  });\n\n  it('should handle click properly', () => {\n    const onButtonClick = jest.fn();\n    const { getByRole } = render(\n      <Button {...defaultProps} onClick={onButtonClick} />\n    );\n    const button = getByRole('button');\n\n    fireEvent.click(button);\n\n    expect(onButtonClick).toHaveBeenCalled();\n  });\n\n  it('should handle disabled for all variants', () => {\n    const { getByRole, rerender } = renderWithTheme(\n      <Button {...defaultProps} disabled />\n    );\n    const button = getByRole('button');\n    const disabledTextShadow = `1px 1px ${theme.materialTextDisabledShadow}`;\n\n    expect(button).toHaveStyleRule('color', theme.materialTextDisabled);\n    expect(button).toHaveStyleRule('text-shadow', disabledTextShadow);\n\n    rerender(<Button {...defaultProps} variant='menu' />);\n    expect(button).toHaveStyleRule('color', theme.materialTextDisabled);\n    expect(button).toHaveStyleRule('text-shadow', disabledTextShadow);\n\n    rerender(<Button {...defaultProps} variant='flat' />);\n    expect(button).toHaveStyleRule('color', theme.materialTextDisabled);\n    expect(button).toHaveStyleRule('text-shadow', disabledTextShadow);\n\n    rerender(<Button {...defaultProps} variant='thin' />);\n    expect(button).toHaveStyleRule('color', theme.materialTextDisabled);\n    expect(button).toHaveStyleRule('text-shadow', disabledTextShadow);\n  });\n\n  it('should handle fullWidth prop', () => {\n    const { getByRole, rerender } = render(\n      <Button {...defaultProps} fullWidth />\n    );\n    const button = getByRole('button');\n\n    expect(button).toHaveStyleRule('width', '100%');\n\n    rerender(<Button {...defaultProps} fullWidth={false} />);\n\n    expect(button).toHaveStyleRule('width', 'auto');\n  });\n\n  it('should handle button sizes properly', () => {\n    const { getByRole, rerender } = render(\n      <Button {...defaultProps} size='sm' />\n    );\n    const button = getByRole('button');\n\n    expect(button).toHaveStyleRule('height', blockSizes.sm);\n\n    rerender(<Button {...defaultProps} size='lg' />);\n\n    expect(button).toHaveStyleRule('height', blockSizes.lg);\n  });\n\n  it('should handle square prop', () => {\n    const { getByRole } = render(<Button {...defaultProps} square size='md' />);\n    const button = getByRole('button');\n\n    expect(button).toHaveStyleRule('padding', '0');\n    expect(button).toHaveStyleRule('width', blockSizes.md);\n  });\n\n  it('should render children', () => {\n    const { getByRole } = render(<Button {...defaultProps} />);\n    const button = getByRole('button');\n\n    expect(button.innerHTML).toBe('click me');\n  });\n\n  describe('prop: disabled', () => {\n    it('should render disabled', () => {\n      const { getByRole } = render(<Button {...defaultProps} disabled />);\n      const button = getByRole('button');\n\n      expect(button).toHaveAttribute('disabled');\n    });\n    it('should not fire click when disabled', () => {\n      const onButtonClick = jest.fn();\n      const { getByRole } = render(<Button {...defaultProps} disabled />);\n      const button = getByRole('button');\n\n      fireEvent.click(button);\n\n      expect(onButtonClick).not.toHaveBeenCalled();\n    });\n  });\n});\n"
  },
  {
    "path": "src/Button/Button.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport {\n  Button,\n  MenuList,\n  MenuListItem,\n  ScrollView,\n  Separator,\n  Toolbar,\n  Window,\n  WindowContent,\n  WindowHeader\n} from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.material};\n  #default-buttons button {\n    margin-bottom: 1rem;\n    margin-right: 1rem;\n  }\n\n  #cutout {\n    background: ${({ theme }) => theme.canvas};\n    padding: 1rem;\n    width: 300px;\n  }\n`;\n\nexport default {\n  title: 'Controls/Button',\n  component: Button,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Button>;\n\nexport function Default() {\n  return (\n    <div id='default-buttons'>\n      <Button>Default</Button>\n      <br />\n      <Button primary>Primary</Button>\n      <br />\n      <Button disabled>Disabled</Button>\n      <br />\n      <Button active>Active</Button>\n      <br />\n      <Button square>\n        <span role='img' aria-label='recycle'>\n          ♻︎\n        </span>\n      </Button>\n      <br />\n      <Button fullWidth>Full width</Button>\n      <br />\n      <Button size='sm'>Size small</Button>\n      <Button size='lg'>Size large</Button>\n    </div>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n\nexport function Raised() {\n  return (\n    <div id='default-buttons'>\n      <Button variant='raised'>Default</Button>\n      <br />\n      <Button variant='raised' primary>\n        Primary\n      </Button>\n      <br />\n      <Button variant='raised' disabled>\n        Disabled\n      </Button>\n      <br />\n      <Button variant='raised' active>\n        Active\n      </Button>\n      <br />\n      <Button variant='raised' square>\n        <span role='img' aria-label='recycle'>\n          ♻︎\n        </span>\n      </Button>\n      <br />\n      <Button variant='raised' fullWidth>\n        Full width\n      </Button>\n      <br />\n      <Button variant='raised' size='sm'>\n        Size small\n      </Button>\n      <Button variant='raised' size='lg'>\n        Size large\n      </Button>\n    </div>\n  );\n}\n\nRaised.story = {\n  name: 'raised'\n};\n\nexport function Flat() {\n  return (\n    <Window>\n      <WindowContent>\n        <ScrollView id='cutout'>\n          <p style={{ lineHeight: 1.3 }}>\n            When you want to use Buttons on a light background (like scrollable\n            content), just use the flat variant:\n          </p>\n          <div\n            style={{\n              marginTop: '1.5rem'\n            }}\n          >\n            <Toolbar>\n              <Button variant='flat' primary style={{ marginRight: '0.5rem' }}>\n                Primary\n              </Button>\n              <Button variant='flat' style={{ marginRight: '0.5rem' }}>\n                Regular\n              </Button>\n              <Button variant='flat' disabled>\n                Disabled\n              </Button>\n            </Toolbar>\n          </div>\n        </ScrollView>\n      </WindowContent>\n    </Window>\n  );\n}\n\nFlat.story = {\n  name: 'flat'\n};\n\nconst imageSrc =\n  'https://image.freepik.com/foto-gratuito/la-frutta-fresca-del-kiwi-tagliata-a-meta-con-la-decorazione-completa-del-pezzo-e-bella-sulla-tavola-di-legno_47436-1.jpg';\nexport function Thin() {\n  const [open, setOpen] = useState(false);\n\n  return (\n    <Window style={{ maxWidth: '250px' }}>\n      <WindowHeader>\n        <span role='img' aria-label='Kiwi'>\n          🥝\n        </span>\n        Kiwi.app\n      </WindowHeader>\n      <Toolbar noPadding>\n        <Button variant='thin' disabled>\n          Upload\n        </Button>\n        <Button variant='thin' disabled>\n          Save\n        </Button>\n        <div\n          style={{\n            position: 'relative',\n            display: 'inline-block',\n            alignSelf: 'left'\n          }}\n        >\n          <Button\n            variant='thin'\n            onClick={() => setOpen(!open)}\n            size='sm'\n            active={open}\n          >\n            Share\n          </Button>\n          {open && (\n            <MenuList\n              style={{\n                position: 'absolute',\n                right: '0',\n                top: '100%',\n                zIndex: '9999'\n              }}\n              onClick={() => setOpen(false)}\n            >\n              <MenuListItem size='sm'>Copy link</MenuListItem>\n              <Separator />\n              <MenuListItem size='sm'>Facebook</MenuListItem>\n              <MenuListItem size='sm'>Twitter</MenuListItem>\n              <MenuListItem size='sm'>Instagram</MenuListItem>\n              <Separator />\n              <MenuListItem size='sm' disabled>\n                MySpace\n              </MenuListItem>\n            </MenuList>\n          )}\n        </div>\n      </Toolbar>\n      <WindowContent style={{ padding: '0.25rem' }}>\n        <ScrollView>\n          <img\n            style={{ width: '100%', height: '1uto', display: 'block' }}\n            src={imageSrc}\n            alt='kiwi'\n          />\n        </ScrollView>\n      </WindowContent>\n    </Window>\n  );\n}\n\nThin.story = {\n  name: 'thin'\n};\n"
  },
  {
    "path": "src/Button/Button.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport {\n  createBorderStyles,\n  createBoxStyles,\n  createDisabledTextStyles,\n  createFlatBoxStyles,\n  createHatchedBackground,\n  focusOutline\n} from '../common';\nimport { blockSizes } from '../common/system';\nimport { noOp } from '../common/utils';\nimport { CommonStyledProps, Sizes } from '../types';\n\ntype ButtonProps = {\n  active?: boolean;\n  children?: React.ReactNode;\n  disabled?: boolean;\n  fullWidth?: boolean;\n  onClick?: React.ButtonHTMLAttributes<HTMLButtonElement>['onClick'];\n  onTouchStart?: React.ButtonHTMLAttributes<HTMLButtonElement>['onTouchStart'];\n  primary?: boolean;\n  size?: Sizes;\n  square?: boolean;\n  type?: string;\n} & (\n  | {\n      variant?: 'default' | 'raised' | 'flat' | 'thin';\n    }\n  | {\n      /** @deprecated Use `thin` */\n      variant?: 'menu';\n    }\n) &\n  Omit<\n    React.ButtonHTMLAttributes<HTMLButtonElement>,\n    'disabled' | 'onClick' | 'onTouchStart' | 'type'\n  > &\n  CommonStyledProps;\n\ntype StyledButtonProps = Pick<\n  ButtonProps,\n  | 'active'\n  | 'disabled'\n  | 'fullWidth'\n  | 'primary'\n  | 'size'\n  | 'square'\n  | 'variant'\n>;\n\nconst commonButtonStyles = css<StyledButtonProps>`\n  position: relative;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  height: ${({ size = 'md' }) => blockSizes[size]};\n  width: ${({ fullWidth, size = 'md', square }) =>\n    fullWidth ? '100%' : square ? blockSizes[size] : 'auto'};\n  padding: ${({ square }) => (square ? 0 : `0 10px`)};\n  font-size: 1rem;\n  user-select: none;\n  &:active {\n    padding-top: ${({ disabled }) => !disabled && '2px'};\n  }\n  padding-top: ${({ active, disabled }) => active && !disabled && '2px'};\n  &:after {\n    content: '';\n    position: absolute;\n    display: block;\n    top: 0;\n    left: 0;\n    height: 100%;\n    width: 100%;\n  }\n  &:not(:disabled) {\n    cursor: pointer;\n  }\n  font-family: inherit;\n`;\n\nexport const StyledButton = styled.button<StyledButtonProps>`\n  ${({ active, disabled, primary, theme, variant }) =>\n    variant === 'flat'\n      ? css`\n          ${createFlatBoxStyles()}\n          ${primary\n            ? `\n          border: 2px solid ${theme.checkmark};\n            outline: 2px solid ${theme.flatDark};\n            outline-offset: -4px;\n          `\n            : `\n          border: 2px solid ${theme.flatDark};\n            outline: 2px solid transparent;\n            outline-offset: -4px;\n          `}\n          &:focus:after, &:active:after {\n            ${!active && !disabled && focusOutline}\n            outline-offset: -4px;\n          }\n        `\n      : variant === 'menu' || variant === 'thin'\n      ? css`\n          ${createBoxStyles()};\n          border: 2px solid transparent;\n          &:hover,\n          &:focus {\n            ${!disabled &&\n            !active &&\n            createBorderStyles({ style: 'buttonThin' })}\n          }\n          &:active {\n            ${!disabled && createBorderStyles({ style: 'buttonThinPressed' })}\n          }\n          ${active && createBorderStyles({ style: 'buttonThinPressed' })}\n          ${disabled && createDisabledTextStyles()}\n        `\n      : css`\n          ${createBoxStyles()};\n          border: none;\n          ${disabled && createDisabledTextStyles()}\n          ${active\n            ? createHatchedBackground({\n                mainColor: theme.material,\n                secondaryColor: theme.borderLightest\n              })\n            : ''}\n          &:before {\n            box-sizing: border-box;\n            content: '';\n            position: absolute;\n            ${primary\n              ? css`\n                  left: 2px;\n                  top: 2px;\n                  width: calc(100% - 4px);\n                  height: calc(100% - 4px);\n                  outline: 2px solid ${theme.borderDarkest};\n                `\n              : css`\n                  left: 0;\n                  top: 0;\n                  width: 100%;\n                  height: 100%;\n                `}\n\n            ${active\n              ? createBorderStyles({\n                  style: variant === 'raised' ? 'window' : 'button',\n                  invert: true\n                })\n              : createBorderStyles({\n                  style: variant === 'raised' ? 'window' : 'button',\n                  invert: false\n                })}\n          }\n          &:active:before {\n            ${!disabled &&\n            createBorderStyles({\n              style: variant === 'raised' ? 'window' : 'button',\n              invert: true\n            })}\n          }\n          &:focus:after,\n          &:active:after {\n            ${!disabled && focusOutline}\n            outline-offset: -8px;\n          }\n          &:active:focus:after,\n          &:active:after {\n            top: ${active ? '0' : '1px'};\n          }\n        `}\n  ${commonButtonStyles}\n`;\n\nconst Button = forwardRef<HTMLButtonElement, ButtonProps>(\n  (\n    {\n      onClick,\n      disabled = false,\n      children,\n      type = 'button',\n      fullWidth = false,\n      size = 'md',\n      square = false,\n      active = false,\n      onTouchStart = noOp,\n      primary = false,\n      variant = 'default',\n      ...otherProps\n    },\n    ref\n  ) => {\n    return (\n      <StyledButton\n        active={active}\n        disabled={disabled}\n        $disabled={disabled}\n        fullWidth={fullWidth}\n        onClick={disabled ? undefined : onClick}\n        onTouchStart={onTouchStart}\n        primary={primary}\n        ref={ref}\n        size={size}\n        square={square}\n        type={type}\n        variant={variant}\n        {...otherProps}\n      >\n        {children}\n      </StyledButton>\n    );\n  }\n);\n\nButton.displayName = 'Button';\n\nexport { Button, ButtonProps };\n"
  },
  {
    "path": "src/Checkbox/Checkbox.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { Checkbox } from './Checkbox';\n\ndescribe('<Checkbox />', () => {\n  describe('label', () => {\n    it('renders', () => {\n      const labelText = 'Swag';\n      const { getByLabelText } = renderWithTheme(\n        <Checkbox label={labelText} />\n      );\n      expect(getByLabelText(labelText)).toBeInTheDocument();\n    });\n  });\n\n  describe('prop: onChange', () => {\n    it('should call onChange when uncontrolled', () => {\n      const handleChange = jest.fn(event => event.target.checked);\n      const { getByRole } = renderWithTheme(\n        <Checkbox onChange={handleChange} />\n      );\n\n      getByRole('checkbox').click();\n\n      expect(handleChange).toHaveBeenCalledTimes(1);\n      // event.target.check is true\n      expect(handleChange.mock.results[0].value).toBe(true);\n    });\n\n    it('should call onChange when controlled', () => {\n      const checked = true;\n      const handleChange = jest.fn(event => event.target.checked);\n\n      const { getByRole } = renderWithTheme(\n        <Checkbox onChange={handleChange} checked={checked} />\n      );\n\n      getByRole('checkbox').click();\n\n      expect(handleChange).toHaveBeenCalledTimes(1);\n\n      expect(handleChange.mock.results[0].value).toBe(!checked);\n    });\n  });\n\n  describe('prop: disabled', () => {\n    it('should disable checkbox', () => {\n      const handleChange = jest.fn();\n\n      const { getByRole } = renderWithTheme(\n        <Checkbox disabled onChange={handleChange} />\n      );\n      const checkbox = getByRole('checkbox');\n      expect(checkbox).toHaveAttribute('disabled');\n\n      checkbox.click();\n      expect(handleChange).not.toHaveBeenCalled();\n    });\n    it('should be overridden by props', () => {\n      const { getByRole, rerender } = renderWithTheme(<Checkbox disabled />);\n      rerender(<Checkbox disabled={false} />);\n      const checkbox = getByRole('checkbox');\n      expect(checkbox).not.toHaveAttribute('disabled');\n    });\n  });\n  describe('prop: indeterminate', () => {\n    it('renders indeterminate state', () => {\n      const { getByRole } = renderWithTheme(<Checkbox indeterminate />);\n      const checkbox = getByRole('checkbox');\n\n      // don't set native 'indeterminate' attribute because\n      // different browsers treat it differently\n      // instead we're setting 'data-indeterminate' attribute\n      expect(checkbox).toHaveAttribute('data-indeterminate');\n      expect(checkbox).not.toHaveAttribute('indeterminate');\n\n      expect(getByRole('presentation').firstChild).toHaveAttribute(\n        'data-testid',\n        'indeterminateIcon'\n      );\n    });\n    it('replaces checked icon', () => {\n      const { getByRole, rerender } = renderWithTheme(<Checkbox checked />);\n\n      expect(getByRole('checkbox')).toHaveAttribute(\n        'data-indeterminate',\n        'false'\n      );\n\n      rerender(<Checkbox checked indeterminate />);\n\n      expect(getByRole('checkbox')).toHaveAttribute(\n        'data-indeterminate',\n        'true'\n      );\n      expect(getByRole('presentation').firstChild).toHaveAttribute(\n        'data-testid',\n        'indeterminateIcon'\n      );\n    });\n  });\n  describe('controlled', () => {\n    it('should check the checkbox', () => {\n      const { getByRole, rerender } = renderWithTheme(\n        <Checkbox checked={false} />\n      );\n\n      rerender(<Checkbox checked />);\n      const checkbox = getByRole('checkbox') as HTMLInputElement;\n\n      expect(checkbox.checked).toBe(true);\n      expect(getByRole('checkbox')).toHaveAttribute('checked');\n      expect(getByRole('presentation').firstChild).toHaveAttribute(\n        'data-testid',\n        'checkmarkIcon'\n      );\n    });\n\n    it('should uncheck the checkbox', () => {\n      const { getByRole, rerender } = renderWithTheme(<Checkbox checked />);\n      rerender(<Checkbox checked={false} />);\n      const checkbox = getByRole('checkbox') as HTMLInputElement;\n\n      expect(checkbox.checked).toBe(false);\n      expect(getByRole('checkbox')).not.toHaveAttribute('checked');\n      expect(getByRole('presentation').firstChild).toBeNull();\n      //   check if proper icon was rendered\n    });\n  });\n\n  describe('uncontrolled', () => {\n    it('can change checked state uncontrolled starting from defaultChecked', () => {\n      const { getByRole } = renderWithTheme(<Checkbox defaultChecked />);\n      const checkbox = getByRole('checkbox') as HTMLInputElement;\n\n      expect(checkbox.checked).toBe(true);\n\n      checkbox.click();\n\n      expect(checkbox.checked).toBe(false);\n\n      checkbox.click();\n\n      expect(checkbox.checked).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "src/Checkbox/Checkbox.stories.tsx",
    "content": "import React, { useState } from 'react';\nimport styled from 'styled-components';\n\nimport { ComponentMeta } from '@storybook/react';\nimport { Checkbox, GroupBox, ScrollView } from 'react95';\n\nconst Wrapper = styled.div`\n  background: ${({ theme }) => theme.material};\n  padding: 5rem;\n\n  #cutout {\n    background: ${({ theme }) => theme.canvas};\n    padding: 1rem;\n    width: 250px;\n    display: flex;\n    align-items: center;\n    justify-content: space-around;\n  }\n`;\n\nexport default {\n  title: 'Controls/Checkbox',\n  component: Checkbox,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Checkbox>;\n\nexport function Default() {\n  const [state, setState] = useState({\n    cheese: true,\n    bacon: false,\n    broccoli: false\n  });\n\n  const { cheese, bacon, broccoli } = state;\n  const ingredientsArr = Object.values(state).map(val => (val ? 1 : 0));\n  const possibleIngredients = Object.keys(state).length;\n  const chosenIngredients = ingredientsArr.reduce<number>((a, b) => a + b, 0);\n\n  const isIndeterminate = ![0, possibleIngredients].includes(chosenIngredients);\n\n  const toggleAll = () => {\n    console.log(ingredientsArr);\n    if (isIndeterminate) {\n      setState({\n        cheese: true,\n        bacon: true,\n        broccoli: true\n      });\n    } else if (ingredientsArr[0] === 1) {\n      setState({\n        cheese: false,\n        bacon: false,\n        broccoli: false\n      });\n    } else {\n      setState({\n        cheese: true,\n        bacon: true,\n        broccoli: true\n      });\n    }\n  };\n\n  const toggleIngredient = (e: React.ChangeEvent<HTMLInputElement>) => {\n    const value = e.target.value as 'cheese' | 'bacon' | 'broccoli';\n    setState({\n      ...state,\n      [value]: !state[value]\n    });\n  };\n\n  return (\n    <div style={{ maxWidth: '250px' }}>\n      <GroupBox label='Pizza toppings'>\n        <Checkbox\n          name='allToppings'\n          label='All'\n          value='allToppings'\n          indeterminate={isIndeterminate}\n          checked={\n            !isIndeterminate && chosenIngredients === possibleIngredients\n          }\n          onChange={toggleAll}\n        />\n        <div style={{ paddingLeft: '1.5rem' }}>\n          <Checkbox\n            checked={!!cheese}\n            onChange={toggleIngredient}\n            value='cheese'\n            label='🧀 Extra cheese'\n            name='ingredients'\n          />\n          <br />\n          <Checkbox\n            checked={!!bacon}\n            onChange={toggleIngredient}\n            value='bacon'\n            label='🥓 Bacon'\n            name='ingredients'\n          />\n          <br />\n          <Checkbox\n            checked={!!broccoli}\n            onChange={toggleIngredient}\n            value='broccoli'\n            label='🥦 Broccoli'\n            name='ingredients'\n          />\n        </div>\n      </GroupBox>\n      <Checkbox\n        name='shipping'\n        value='shipping'\n        label='Free shipping'\n        defaultChecked\n        disabled\n        style={{ marginTop: '1rem' }}\n      />\n    </div>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n\nexport function Flat() {\n  const [state, setState] = useState({\n    cheese: true,\n    bacon: false,\n    broccoli: false\n  });\n\n  const { cheese, bacon, broccoli } = state;\n  const ingredientsArr = Object.values(state).map(val => (val ? 1 : 0));\n  const possibleIngredients = Object.keys(state).length;\n  const chosenIngredients = ingredientsArr.reduce<number>((a, b) => a + b, 0);\n\n  const isIndeterminate = ![0, possibleIngredients].includes(chosenIngredients);\n\n  const toggleAll = () => {\n    console.log(ingredientsArr);\n    if (isIndeterminate) {\n      setState({\n        cheese: true,\n        bacon: true,\n        broccoli: true\n      });\n    } else if (ingredientsArr[0] === 1) {\n      setState({\n        cheese: false,\n        bacon: false,\n        broccoli: false\n      });\n    } else {\n      setState({\n        cheese: true,\n        bacon: true,\n        broccoli: true\n      });\n    }\n  };\n\n  const toggleIngredient = (e: React.ChangeEvent<HTMLInputElement>) => {\n    const value = e.target.value as 'cheese' | 'bacon' | 'broccoli';\n    setState({\n      ...state,\n      [value]: !state[value]\n    });\n  };\n\n  return (\n    <ScrollView id='cutout'>\n      <div style={{ maxWidth: '250px' }}>\n        <GroupBox variant='flat' label='Pizza toppings'>\n          <Checkbox\n            variant='flat'\n            name='allToppings'\n            label='All'\n            value='allToppings'\n            indeterminate={isIndeterminate}\n            checked={\n              !isIndeterminate && chosenIngredients === possibleIngredients\n            }\n            onChange={toggleAll}\n          />\n          <div style={{ paddingLeft: '1.5rem' }}>\n            <Checkbox\n              variant='flat'\n              checked={!!cheese}\n              onChange={toggleIngredient}\n              value='cheese'\n              label='🧀 Extra cheese'\n              name='ingredients'\n            />\n            <br />\n            <Checkbox\n              variant='flat'\n              checked={!!bacon}\n              onChange={toggleIngredient}\n              value='bacon'\n              label='🥓 Bacon'\n              name='ingredients'\n            />\n            <br />\n            <Checkbox\n              variant='flat'\n              checked={!!broccoli}\n              onChange={toggleIngredient}\n              value='broccoli'\n              label='🥦 Broccoli'\n              name='ingredients'\n            />\n          </div>\n        </GroupBox>\n        <Checkbox\n          variant='flat'\n          name='shipping'\n          value='shipping'\n          label='Free shipping'\n          defaultChecked\n          disabled\n          style={{ marginTop: '1rem' }}\n        />\n      </div>\n    </ScrollView>\n  );\n}\n\nFlat.story = {\n  name: 'flat'\n};\n"
  },
  {
    "path": "src/Checkbox/Checkbox.tsx",
    "content": "import React, { forwardRef, useCallback } from 'react';\nimport styled, { css } from 'styled-components';\n\nimport { createHatchedBackground } from '../common';\nimport useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';\nimport {\n  LabelText,\n  size,\n  StyledInput,\n  StyledLabel\n} from '../common/SwitchBase';\nimport { noOp } from '../common/utils';\nimport { StyledScrollView } from '../ScrollView/ScrollView';\nimport { CommonThemeProps } from '../types';\n\ntype CheckboxProps = {\n  checked?: boolean;\n  className?: string;\n  defaultChecked?: boolean;\n  disabled?: boolean;\n  indeterminate?: boolean;\n  label?: number | string;\n  name?: string;\n  onChange?: React.ChangeEventHandler<HTMLInputElement>;\n  style?: React.CSSProperties;\n  value?: number | string;\n  variant?: 'default' | 'flat';\n} & Omit<\n  React.InputHTMLAttributes<HTMLInputElement>,\n  | 'checked'\n  | 'className'\n  | 'defaultChecked'\n  | 'disabled'\n  | 'label'\n  | 'name'\n  | 'onChange'\n  | 'style'\n  | 'value'\n>;\n\ntype CheckmarkProps = {\n  $disabled: boolean;\n  variant: 'default' | 'flat';\n};\n\nconst sharedCheckboxStyles = css`\n  width: ${size}px;\n  height: ${size}px;\n  display: flex;\n  align-items: center;\n  justify-content: space-around;\n  margin-right: 0.5rem;\n`;\nconst StyledCheckbox = styled(StyledScrollView)<CommonThemeProps>`\n  ${sharedCheckboxStyles}\n  width: ${size}px;\n  height: ${size}px;\n  background: ${({ $disabled, theme }) =>\n    $disabled ? theme.material : theme.canvas};\n  &:before {\n    box-shadow: none;\n  }\n`;\nconst StyledFlatCheckbox = styled.div<CommonThemeProps>`\n  position: relative;\n  box-sizing: border-box;\n  display: inline-block;\n  background: ${({ $disabled, theme }) =>\n    $disabled ? theme.flatLight : theme.canvas};\n  ${sharedCheckboxStyles}\n  width: ${size - 4}px;\n  height: ${size - 4}px;\n  outline: none;\n  border: 2px solid ${({ theme }) => theme.flatDark};\n  background: ${({ $disabled, theme }) =>\n    $disabled ? theme.flatLight : theme.canvas};\n`;\n\nconst CheckmarkIcon = styled.span.attrs(() => ({\n  'data-testid': 'checkmarkIcon'\n}))<CheckmarkProps>`\n  display: inline-block;\n  position: relative;\n  width: 100%;\n  height: 100%;\n  &:after {\n    content: '';\n    display: block;\n    position: absolute;\n    left: 50%;\n    top: calc(50% - 1px);\n    width: 3px;\n    height: 7px;\n\n    border: solid\n      ${({ $disabled, theme }) =>\n        $disabled ? theme.checkmarkDisabled : theme.checkmark};\n    border-width: 0 3px 3px 0;\n    transform: translate(-50%, -50%) rotate(45deg);\n\n    border-color: ${p =>\n      p.$disabled ? p.theme.checkmarkDisabled : p.theme.checkmark};\n  }\n`;\nconst IndeterminateIcon = styled.span.attrs(() => ({\n  'data-testid': 'indeterminateIcon'\n}))<CheckmarkProps>`\n  display: inline-block;\n  position: relative;\n\n  width: 100%;\n  height: 100%;\n\n  &:after {\n    content: '';\n    display: block;\n\n    width: 100%;\n    height: 100%;\n\n    ${({ $disabled, theme }) =>\n      createHatchedBackground({\n        mainColor: $disabled ? theme.checkmarkDisabled : theme.checkmark\n      })}\n    background-position: 0px 0px, 2px 2px;\n  }\n`;\n\nconst CheckboxComponents = {\n  flat: StyledFlatCheckbox,\n  default: StyledCheckbox\n};\n\nconst Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(\n  (\n    {\n      checked,\n      className = '',\n      defaultChecked = false,\n      disabled = false,\n      indeterminate = false,\n      label = '',\n      onChange = noOp,\n      style = {},\n      value,\n      variant = 'default',\n      ...otherProps\n    },\n    ref\n  ) => {\n    const [state, setState] = useControlledOrUncontrolled({\n      defaultValue: defaultChecked,\n      onChange,\n      readOnly: otherProps.readOnly ?? disabled,\n      value: checked\n    });\n\n    const handleChange = useCallback(\n      (e: React.ChangeEvent<HTMLInputElement>) => {\n        const newState = e.target.checked;\n        setState(newState);\n        onChange(e);\n      },\n      [onChange, setState]\n    );\n\n    const CheckboxComponent = CheckboxComponents[variant];\n\n    let Icon = null;\n    if (indeterminate) {\n      Icon = IndeterminateIcon;\n    } else if (state) {\n      Icon = CheckmarkIcon;\n    }\n\n    return (\n      <StyledLabel $disabled={disabled} className={className} style={style}>\n        <StyledInput\n          disabled={disabled}\n          onChange={disabled ? undefined : handleChange}\n          readOnly={disabled}\n          type='checkbox'\n          value={value}\n          checked={state}\n          data-indeterminate={indeterminate}\n          ref={ref}\n          {...otherProps}\n        />\n        <CheckboxComponent $disabled={disabled} role='presentation'>\n          {Icon && <Icon $disabled={disabled} variant={variant} />}\n        </CheckboxComponent>\n        {label && <LabelText>{label}</LabelText>}\n      </StyledLabel>\n    );\n  }\n);\n\nCheckbox.displayName = 'Checkbox';\n\nexport { Checkbox, CheckboxProps };\n"
  },
  {
    "path": "src/ColorInput/ColorInput.spec.tsx",
    "content": "import { fireEvent } from '@testing-library/react';\nimport React from 'react';\nimport { renderWithTheme } from '../../test/utils';\nimport { ColorInput } from './ColorInput';\n\nfunction rgb2hex(str: string) {\n  const rgb = str.match(/^rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)$/);\n  function hex(x: string) {\n    return `0${parseInt(x, 10).toString(16)}`.slice(-2);\n  }\n  return rgb ? `#${hex(rgb[1])}${hex(rgb[2])}${hex(rgb[3])}` : '';\n}\n\ndescribe('<ColorInput />', () => {\n  it('should call handlers', () => {\n    const color = '#f0f0dd';\n    const onChange = jest.fn();\n    const { container } = renderWithTheme(<ColorInput onChange={onChange} />);\n    const input = container.querySelector(`[type=\"color\"]`) as HTMLInputElement;\n    fireEvent.change(input, { target: { value: color } });\n    expect(onChange).toBeCalledTimes(1);\n  });\n\n  it('should properly pass value to input element', () => {\n    const color = '#f0f0dd';\n    const { container } = renderWithTheme(<ColorInput value={color} />);\n    const input = container.querySelector(`[type=\"color\"]`) as HTMLInputElement;\n\n    expect(input.value).toBe(color);\n  });\n  it('should display current color', () => {\n    const color = '#f0f0dd';\n    const { getByRole } = renderWithTheme(<ColorInput value={color} />);\n    const colorPreview = getByRole('presentation');\n    const displayedColor = window.getComputedStyle(\n      colorPreview,\n      null\n    ).background;\n\n    const displayedColorHex = rgb2hex(displayedColor);\n    expect(displayedColorHex).toBe(color);\n  });\n\n  describe('prop: disabled', () => {\n    it('should render disabled input', () => {\n      const { container } = renderWithTheme(<ColorInput disabled />);\n      const input = container.querySelector(`[type=\"color\"]`);\n\n      expect(input).toHaveAttribute('disabled');\n    });\n\n    it('should be overridden by props', () => {\n      const { container, rerender } = renderWithTheme(<ColorInput disabled />);\n      rerender(<ColorInput disabled={false} />);\n      const input = container.querySelector(`[type=\"color\"]`);\n\n      expect(input).not.toHaveAttribute('disabled');\n    });\n  });\n});\n"
  },
  {
    "path": "src/ColorInput/ColorInput.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport styled from 'styled-components';\n\nimport { ColorInput, ScrollView } from 'react95';\n\nconst Wrapper = styled.div`\n  background: ${({ theme }) => theme.material};\n  padding: 5rem;\n  & > span {\n    margin-left: 1rem;\n    margin-right: 0.5rem;\n  }\n  #cutout {\n    background: ${({ theme }) => theme.canvas};\n    display: inline-block;\n  }\n  .content {\n    padding: 1rem;\n    & > div {\n      margin: 1rem 0;\n    }\n\n    & > div > span {\n      margin-right: 0.5rem;\n    }\n  }\n`;\n\nexport default {\n  title: 'Controls/ColorInput',\n  component: ColorInput,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof ColorInput>;\n\nexport function Default() {\n  return (\n    <>\n      <span>enabled: </span>\n      <ColorInput defaultValue='#00f' />\n      <span>disabled: </span>\n      <ColorInput disabled defaultValue='#00f' />\n    </>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n\nexport function Flat() {\n  return (\n    <ScrollView id='cutout'>\n      <div className='content'>\n        <div>\n          <span>enabled: </span>\n          <ColorInput variant='flat' defaultValue='#00f' />\n        </div>\n        <div>\n          <span>disabled: </span>\n          <ColorInput variant='flat' disabled defaultValue='#00f' />\n        </div>\n      </div>\n    </ScrollView>\n  );\n}\n\nFlat.story = {\n  name: 'flat'\n};\n"
  },
  {
    "path": "src/ColorInput/ColorInput.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { StyledButton } from '../Button/Button';\nimport { focusOutline } from '../common';\nimport useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';\nimport { noOp } from '../common/utils';\nimport { Separator } from '../Separator/Separator';\nimport { CommonStyledProps } from '../types';\n\ntype ColorInputProps = {\n  defaultValue?: string;\n  disabled?: boolean;\n  onChange?: React.ChangeEventHandler<HTMLInputElement>;\n  value?: string;\n  variant?: 'default' | 'flat';\n} & Omit<\n  React.InputHTMLAttributes<HTMLInputElement>,\n  'defaultValue' | 'disabled' | 'onChange' | 'value'\n> &\n  CommonStyledProps;\n\nconst Trigger = styled(StyledButton)`\n  padding-left: 8px;\n`;\n\nconst StyledSeparator = styled(Separator)`\n  height: 21px;\n  position: relative;\n  top: 0;\n`;\n\nexport const StyledColorInput = styled.input`\n  box-sizing: border-box;\n  width: 100%;\n  height: 100%;\n  position: absolute;\n  left: 0;\n  top: 0;\n  opacity: 0;\n  z-index: 1;\n  cursor: pointer;\n  &:disabled {\n    cursor: default;\n  }\n`;\n\n// TODO replace with SVG icon\nconst ColorPreview = styled.div<{\n  color: string;\n  $disabled: boolean;\n}>`\n  box-sizing: border-box;\n  height: 19px;\n  display: inline-block;\n  width: 35px;\n  margin-right: 5px;\n\n  background: ${({ color }) => color};\n\n  ${({ $disabled }) =>\n    $disabled\n      ? css`\n          border: 2px solid ${({ theme }) => theme.materialTextDisabled};\n          filter: drop-shadow(\n            1px 1px 0px ${({ theme }) => theme.materialTextDisabledShadow}\n          );\n        `\n      : css`\n          border: 2px solid ${({ theme }) => theme.materialText};\n        `}\n  ${StyledColorInput}:focus:not(:active) + &:after {\n    content: '';\n    position: absolute;\n    left: 0;\n    top: 0;\n    width: 100%;\n    height: 100%;\n    ${focusOutline}\n    outline-offset: -8px;\n  }\n`;\n\nconst ChevronIcon = styled.span<\n  Required<Pick<ColorInputProps, 'variant'>> & {\n    $disabled: boolean;\n  }\n>`\n  width: 0px;\n  height: 0px;\n  border-left: 6px solid transparent;\n  border-right: 6px solid transparent;\n  display: inline-block;\n  margin-left: 6px;\n\n  ${({ $disabled }) =>\n    $disabled\n      ? css`\n          border-top: 6px solid ${({ theme }) => theme.materialTextDisabled};\n          filter: drop-shadow(\n            1px 1px 0px ${({ theme }) => theme.materialTextDisabledShadow}\n          );\n        `\n      : css`\n          border-top: 6px solid ${({ theme }) => theme.materialText};\n        `}\n  &:after {\n    content: '';\n    box-sizing: border-box;\n    position: absolute;\n    top: ${({ variant }) => (variant === 'flat' ? '6px' : '8px')};\n    right: 8px;\n    width: 16px;\n    height: 19px;\n  }\n`;\n\n// TODO make sure all aria and role attributes are in place\nconst ColorInput = forwardRef<HTMLInputElement, ColorInputProps>(\n  (\n    {\n      value,\n      defaultValue,\n      onChange = noOp,\n      disabled = false,\n      variant = 'default',\n      ...otherProps\n    },\n    ref\n  ) => {\n    const [valueDerived, setValueState] = useControlledOrUncontrolled({\n      defaultValue,\n      onChange,\n      readOnly: otherProps.readOnly ?? disabled,\n      value\n    });\n\n    const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n      const color = e.target.value;\n      setValueState(color);\n      onChange(e);\n    };\n\n    return (\n      //  we only need button styles, so we display\n      // it as a div and reset type attribute\n      <Trigger disabled={disabled} as='div' variant={variant} size='md'>\n        <StyledColorInput\n          onChange={handleChange}\n          readOnly={disabled}\n          disabled={disabled}\n          value={valueDerived ?? '#008080'}\n          type='color'\n          ref={ref}\n          {...otherProps}\n        />\n        <ColorPreview\n          $disabled={disabled}\n          color={valueDerived ?? '#008080'}\n          role='presentation'\n        />\n        {variant === 'default' && <StyledSeparator orientation='vertical' />}\n        <ChevronIcon $disabled={disabled} variant={variant} />\n      </Trigger>\n    );\n  }\n);\n\nColorInput.displayName = 'ColorInput';\n\nexport { ColorInput, ColorInputProps };\n"
  },
  {
    "path": "src/Counter/Counter.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Counter } from './Counter';\n\ndescribe('<Counter />', () => {\n  it('should render', () => {\n    const { container } = renderWithTheme(<Counter />);\n    const counter = container.firstElementChild;\n\n    expect(counter).toBeInTheDocument();\n  });\n\n  it('should handle custom style', () => {\n    const { container } = renderWithTheme(\n      <Counter style={{ backgroundColor: 'papayawhip' }} />\n    );\n    const counter = container.firstElementChild;\n\n    expect(counter).toHaveAttribute('style', 'background-color: papayawhip;');\n  });\n\n  it('should handle custom props', () => {\n    const customProps: React.HTMLAttributes<HTMLDivElement> = {\n      title: 'potatoe'\n    };\n    const { container } = renderWithTheme(<Counter {...customProps} />);\n    const counter = container.firstElementChild;\n\n    expect(counter).toHaveAttribute('title', 'potatoe');\n  });\n\n  describe('prop: minLength', () => {\n    it('renders correct number of digits', () => {\n      const { container } = renderWithTheme(\n        <Counter value={32} minLength={7} />\n      );\n      const counter = container.firstElementChild;\n\n      expect(counter && counter.childElementCount).toBe(7);\n    });\n\n    it('value length takes priority if bigger than minLength', () => {\n      const { container } = renderWithTheme(\n        <Counter value={1234} minLength={2} />\n      );\n      const counter = container.firstElementChild;\n\n      expect(counter && counter.childElementCount).toBe(4);\n    });\n  });\n});\n"
  },
  {
    "path": "src/Counter/Counter.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport { Button, Counter, Frame } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n  .counter-wrapper {\n    display: flex;\n    margin-top: 1rem;\n  }\n  .counter-wrapper button {\n    margin-left: 0.5rem;\n    height: 51px;\n  }\n  .wrapper {\n    padding: 1rem;\n  }\n`;\n\nexport default {\n  title: 'Other/Counter',\n  component: Counter,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Counter>;\n\nexport function Default() {\n  const [count, setCount] = useState(13);\n  const handleClick = () => setCount(count + 1);\n  return (\n    <Frame className='wrapper'>\n      <Counter value={123456789} minLength={11} size='lg' />\n\n      <div className='counter-wrapper'>\n        <Counter value={count} minLength={3} />\n        <Button onClick={handleClick}>Click!</Button>\n      </div>\n    </Frame>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/Counter/Counter.tsx",
    "content": "import React, { forwardRef, useMemo } from 'react';\nimport styled from 'styled-components';\n\nimport { createBorderStyles } from '../common';\nimport { CommonStyledProps, Sizes } from '../types';\nimport { Digit } from './Digit';\n\ntype CounterProps = {\n  minLength?: number;\n  size?: Sizes | 'xl';\n  value?: number;\n} & React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\nconst CounterWrapper = styled.div`\n  ${createBorderStyles({ style: 'status' })}\n  display: inline-flex;\n  background: #000000;\n`;\n\nconst pixelSizes = {\n  sm: 1,\n  md: 2,\n  lg: 3,\n  xl: 4\n};\n\nconst Counter = forwardRef<HTMLDivElement, CounterProps>(\n  ({ value = 0, minLength = 3, size = 'md', ...otherProps }, ref) => {\n    const digits = useMemo(\n      () => value.toString().padStart(minLength, '0').split(''),\n      [minLength, value]\n    );\n    return (\n      <CounterWrapper ref={ref} {...otherProps}>\n        {digits.map((digit, i) => (\n          <Digit digit={digit} pixelSize={pixelSizes[size]} key={i} />\n        ))}\n      </CounterWrapper>\n    );\n  }\n);\n\nCounter.displayName = 'Counter';\n\nexport { Counter, CounterProps };\n"
  },
  {
    "path": "src/Counter/Digit.tsx",
    "content": "import React from 'react';\nimport styled, { css } from 'styled-components';\n\nimport { createHatchedBackground } from '../common';\nimport { CommonStyledProps } from '../types';\n\ntype DigitProps = {\n  pixelSize?: number;\n  digit?: number | string;\n} & React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\nconst DigitWrapper = styled.div<Required<Pick<DigitProps, 'pixelSize'>>>`\n  position: relative;\n  --react95-digit-primary-color: #ff0102;\n  --react95-digit-secondary-color: #740201;\n  --react95-digit-bg-color: #000000;\n\n  ${({ pixelSize }) => css`\n    width: ${11 * pixelSize}px;\n    height: ${21 * pixelSize}px;\n    margin: ${pixelSize}px;\n\n    span,\n    span:before,\n    span:after {\n      box-sizing: border-box;\n      display: inline-block;\n      position: absolute;\n    }\n    span.active,\n    span.active:before,\n    span.active:after {\n      background: var(--react95-digit-primary-color);\n    }\n    span:not(.active),\n    span:not(.active):before,\n    span:not(.active):after {\n      ${createHatchedBackground({\n        mainColor: 'var(--react95-digit-bg-color)',\n        secondaryColor: 'var(--react95-digit-secondary-color)',\n        pixelSize\n      })}\n    }\n\n    span.horizontal,\n    span.horizontal:before,\n    span.horizontal:after {\n      height: ${pixelSize}px;\n      border-left: ${pixelSize}px solid var(--react95-digit-bg-color);\n      border-right: ${pixelSize}px solid var(--react95-digit-bg-color);\n    }\n    span.horizontal.active,\n    span.horizontal.active:before,\n    span.horizontal.active:after {\n      height: ${pixelSize}px;\n      border-left: ${pixelSize}px solid var(--react95-digit-primary-color);\n      border-right: ${pixelSize}px solid var(--react95-digit-primary-color);\n    }\n    span.horizontal {\n      left: ${pixelSize}px;\n      width: ${9 * pixelSize}px;\n    }\n    span.horizontal:before {\n      content: '';\n      width: 100%;\n      top: ${pixelSize}px;\n      left: ${0}px;\n    }\n    span.horizontal:after {\n      content: '';\n      width: calc(100% - ${pixelSize * 2}px);\n      top: ${2 * pixelSize}px;\n      left: ${pixelSize}px;\n    }\n    span.horizontal.top {\n      top: 0;\n    }\n    span.horizontal.bottom {\n      bottom: 0;\n      transform: rotateX(180deg);\n    }\n\n    span.center,\n    span.center:before,\n    span.center:after {\n      height: ${pixelSize}px;\n      border-left: ${pixelSize}px solid var(--react95-digit-bg-color);\n      border-right: ${pixelSize}px solid var(--react95-digit-bg-color);\n    }\n    span.center.active,\n    span.center.active:before,\n    span.center.active:after {\n      border-left: ${pixelSize}px solid var(--react95-digit-primary-color);\n      border-right: ${pixelSize}px solid var(--react95-digit-primary-color);\n    }\n    span.center {\n      top: 50%;\n      transform: translateY(-50%);\n      left: ${pixelSize}px;\n      width: ${9 * pixelSize}px;\n    }\n    span.center:before,\n    span.center:after {\n      content: '';\n      width: 100%;\n    }\n    span.center:before {\n      top: ${pixelSize}px;\n    }\n    span.center:after {\n      bottom: ${pixelSize}px;\n    }\n\n    span.vertical,\n    span.vertical:before,\n    span.vertical:after {\n      width: ${pixelSize}px;\n      border-top: ${pixelSize}px solid var(--react95-digit-bg-color);\n      border-bottom: ${pixelSize}px solid var(--react95-digit-bg-color);\n    }\n    span.vertical {\n      height: ${11 * pixelSize}px;\n    }\n    span.vertical.left {\n      left: 0;\n    }\n    span.vertical.right {\n      right: 0;\n      transform: rotateY(180deg);\n    }\n    span.vertical.top {\n      top: 0px;\n    }\n    span.vertical.bottom {\n      bottom: 0px;\n    }\n    span.vertical:before {\n      content: '';\n      height: 100%;\n      top: ${0}px;\n      left: ${pixelSize}px;\n    }\n    span.vertical:after {\n      content: '';\n      height: calc(100% - ${pixelSize * 2}px);\n      top: ${pixelSize}px;\n      left: ${pixelSize * 2}px;\n    }\n  `}\n`;\n\nconst segments = [\n  'horizontal top',\n  'center',\n  'horizontal bottom',\n  'vertical top left',\n  'vertical top right',\n  'vertical bottom left',\n  'vertical bottom right'\n];\n\nconst digitActiveSegments = [\n  [1, 0, 1, 1, 1, 1, 1], // 0\n  [0, 0, 0, 0, 1, 0, 1], // 1\n  [1, 1, 1, 0, 1, 1, 0], // 2\n  [1, 1, 1, 0, 1, 0, 1], // 3\n  [0, 1, 0, 1, 1, 0, 1], // 4\n  [1, 1, 1, 1, 0, 0, 1], // 5\n  [1, 1, 1, 1, 0, 1, 1], // 6\n  [1, 0, 0, 0, 1, 0, 1], // 7\n  [1, 1, 1, 1, 1, 1, 1], // 8\n  [1, 1, 1, 1, 1, 0, 1] // 9\n];\n\nfunction Digit({ digit = 0, pixelSize = 2, ...otherProps }: DigitProps) {\n  const segmentClasses = digitActiveSegments[Number(digit)].map((isActive, i) =>\n    isActive ? `${segments[i]} active` : segments[i]\n  );\n  return (\n    <DigitWrapper pixelSize={pixelSize} {...otherProps}>\n      {segmentClasses.map((className, i) => (\n        <span className={className} key={i} />\n      ))}\n    </DigitWrapper>\n  );\n}\n\nexport { Digit, DigitProps };\n"
  },
  {
    "path": "src/DatePicker/DatePicker.stories.tsx",
    "content": "/* eslint-disable camelcase, react/jsx-pascal-case */\nimport { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { DatePicker__UNSTABLE } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n`;\n\nexport default {\n  title: 'DatePicker__UNSTABLE',\n  component: DatePicker__UNSTABLE,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof DatePicker__UNSTABLE>;\n\nexport function Default() {\n  return <DatePicker__UNSTABLE onAccept={date => console.log(date)} />;\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/DatePicker/DatePicker.tsx",
    "content": "import React, { forwardRef, useCallback, useMemo, useState } from 'react';\nimport styled from 'styled-components';\n\nimport { Button } from '../Button/Button';\nimport { NumberInput } from '../NumberInput/NumberInput';\nimport { ScrollView } from '../ScrollView/ScrollView';\nimport { Select } from '../Select/Select';\nimport { Toolbar } from '../Toolbar/Toolbar';\nimport { Window, WindowContent, WindowHeader } from '../Window/Window';\n\ntype DatePickerProps = {\n  className?: string;\n  date?: string;\n  onAccept?: (chosenDate: string) => void;\n  onCancel?: React.MouseEventHandler<HTMLButtonElement>;\n  shadow?: boolean;\n};\n\nconst Calendar = styled(ScrollView)`\n  width: 234px;\n  margin: 1rem 0;\n  background: ${({ theme }) => theme.canvas};\n`;\n\nconst WeekDays = styled.div`\n  display: flex;\n  background: ${({ theme }) => theme.materialDark};\n  color: #dfe0e3;\n`;\n\nconst Dates = styled.div`\n  display: flex;\n  flex-wrap: wrap;\n`;\n\nconst DateItem = styled.div`\n  text-align: center;\n  height: 1.5em;\n  line-height: 1.5em;\n  width: 14.28%;\n`;\n\nconst DateItemContent = styled.span<{ active: boolean }>`\n  cursor: pointer;\n\n  background: ${({ active, theme }) =>\n    active ? theme.hoverBackground : 'transparent'};\n  color: ${({ active, theme }) =>\n    active ? theme.canvasTextInvert : theme.canvasText};\n\n  &:hover {\n    border: 2px dashed\n      ${({ theme, active }) => (active ? 'none' : theme.materialDark)};\n  }\n`;\n\nconst months = [\n  { value: 0, label: 'January' },\n  { value: 1, label: 'February' },\n  { value: 2, label: 'March' },\n  { value: 3, label: 'April' },\n  { value: 4, label: 'May' },\n  { value: 5, label: 'June' },\n  { value: 6, label: 'July' },\n  { value: 7, label: 'August' },\n  { value: 8, label: 'September' },\n  { value: 9, label: 'October' },\n  { value: 10, label: 'November' },\n  { value: 11, label: 'December' }\n];\n\nfunction daysInMonth(year: number, month: number) {\n  return new Date(year, month + 1, 0).getDate();\n}\n\nfunction dayIndex(year: number, month: number, day: number) {\n  return new Date(year, month, day).getDay();\n}\n\nfunction convertDateToState(stringDate: string) {\n  const date = new Date(Date.parse(stringDate));\n  const day = date.getUTCDate();\n  const month = date.getUTCMonth();\n  const year = date.getUTCFullYear();\n\n  return { day, month, year };\n}\n\nconst DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(\n  (\n    {\n      className,\n      date: initialDate = new Date().toISOString(),\n      onAccept,\n      onCancel,\n      shadow = true\n    },\n    ref\n  ) => {\n    const [date, setDate] = useState(() => convertDateToState(initialDate));\n    const { year, month, day } = date;\n\n    const handleMonthSelect = useCallback(\n      ({ value: monthSelected }: { value: number }) => {\n        setDate(currentDate => ({ ...currentDate, month: monthSelected }));\n      },\n      []\n    );\n\n    const handleYearSelect = useCallback((yearSelected: number) => {\n      setDate(currentDate => ({ ...currentDate, year: yearSelected }));\n    }, []);\n\n    const handleDaySelect = useCallback((daySelected: number) => {\n      setDate(currentDate => ({ ...currentDate, day: daySelected }));\n    }, []);\n\n    const handleAccept = useCallback(() => {\n      const chosenDate = [date.year, date.month + 1, date.day]\n        .map(part => String(part).padStart(2, '0'))\n        .join('-');\n\n      onAccept?.(chosenDate);\n    }, [date.day, date.month, date.year, onAccept]);\n\n    const dayPickerItems = useMemo(() => {\n      const items: React.ReactNode[] = Array.from({ length: 42 });\n      const firstDayIndex = dayIndex(year, month, 1);\n      let itemDay = day;\n\n      const daysNumber = daysInMonth(year, month);\n      itemDay = itemDay < daysNumber ? itemDay : daysNumber;\n      items.forEach((_, i) => {\n        if (i >= firstDayIndex && i < daysNumber + firstDayIndex) {\n          const dayNumber = i - firstDayIndex + 1;\n\n          items[i] = (\n            <DateItem\n              key={i}\n              onClick={() => {\n                handleDaySelect(dayNumber);\n              }}\n            >\n              <DateItemContent active={dayNumber === itemDay}>\n                {dayNumber}\n              </DateItemContent>\n            </DateItem>\n          );\n        } else {\n          items[i] = <DateItem key={i} />;\n        }\n      });\n      return items;\n    }, [day, handleDaySelect, month, year]);\n\n    return (\n      <Window\n        className={className}\n        ref={ref}\n        shadow={shadow}\n        style={{ margin: 20 }}\n      >\n        <WindowHeader>\n          <span role='img' aria-label='📆'>\n            📆\n          </span>\n          Date\n        </WindowHeader>\n        <WindowContent>\n          <Toolbar noPadding style={{ justifyContent: 'space-between' }}>\n            <Select\n              options={months}\n              value={month}\n              onChange={handleMonthSelect}\n              width={128}\n              menuMaxHeight={200}\n            />\n            <NumberInput value={year} onChange={handleYearSelect} width={100} />\n          </Toolbar>\n          <Calendar>\n            <WeekDays>\n              <DateItem>S</DateItem>\n              <DateItem>M</DateItem>\n              <DateItem>T</DateItem>\n              <DateItem>W</DateItem>\n              <DateItem>T</DateItem>\n              <DateItem>F</DateItem>\n              <DateItem>S</DateItem>\n            </WeekDays>\n            <Dates>{dayPickerItems}</Dates>\n          </Calendar>\n          <Toolbar noPadding style={{ justifyContent: 'space-between' }}>\n            <Button fullWidth onClick={onCancel} disabled={!onCancel}>\n              Cancel\n            </Button>\n            <Button\n              fullWidth\n              onClick={onAccept ? handleAccept : undefined}\n              disabled={!onAccept}\n            >\n              OK\n            </Button>\n          </Toolbar>\n        </WindowContent>\n      </Window>\n    );\n  }\n);\n\nDatePicker.displayName = 'DatePicker';\n\n// eslint-disable-next-line camelcase\nexport { DatePicker as DatePicker__UNSTABLE, DatePickerProps };\n"
  },
  {
    "path": "src/Frame/Frame.spec.tsx",
    "content": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { Frame } from './Frame';\n\ndescribe('<Frame />', () => {\n  it('should render frame', () => {\n    const { container } = render(<Frame />);\n    const frame = container.firstElementChild;\n\n    expect(frame).toBeInTheDocument();\n  });\n\n  it('should render custom styles', () => {\n    const { container } = render(\n      <Frame style={{ backgroundColor: 'papayawhip' }} />\n    );\n    const frame = container.firstElementChild;\n\n    expect(frame).toHaveAttribute('style', 'background-color: papayawhip;');\n  });\n\n  it('should render children', async () => {\n    const { findByText } = render(\n      <Frame>\n        <span>Cool frame</span>\n      </Frame>\n    );\n    const content = await findByText(/cool frame/i);\n\n    expect(content).toBeInTheDocument();\n  });\n\n  it('should render custom props', () => {\n    const customProps = { title: 'frame' };\n    const { container } = render(<Frame {...customProps} />);\n    const frame = container.firstElementChild;\n\n    expect(frame).toHaveAttribute('title', 'frame');\n  });\n});\n"
  },
  {
    "path": "src/Frame/Frame.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Frame } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.material};\n  #default-buttons button {\n    margin-bottom: 1rem;\n    margin-right: 1rem;\n  }\n\n  #cutout {\n    background: ${({ theme }) => theme.canvas};\n    padding: 1rem;\n    width: 300px;\n  }\n`;\n\nexport default {\n  title: 'Layout/Frame',\n  component: Frame,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Frame>;\n\nexport function Default() {\n  return (\n    <Frame\n      variant='outside'\n      shadow\n      style={{ padding: '0.5rem', lineHeight: '1.5', width: 600 }}\n    >\n      <p style={{ padding: '0.5rem' }}>\n        This is a frame of the &apos;window&apos; variant, the default. Notice\n        the subtle difference in borders. The lightest border is not on the edge\n        of this frame.\n      </p>\n      <Frame variant='inside' style={{ margin: '1rem', padding: '1rem' }}>\n        This frame of the &apos;button&apos; variant on the other hand has the\n        lightest border on the edge. Use this frame inside &apos;window&apos;\n        frames.\n        <br />\n        <Frame\n          variant='field'\n          style={{\n            marginTop: '1rem',\n            padding: '1rem',\n            height: 200,\n            width: 100\n          }}\n        >\n          A field frame variant is used to display content.\n        </Frame>\n      </Frame>\n      <Frame\n        variant='well'\n        style={{ marginTop: '1rem', padding: '0.1rem 0.25rem', width: '100%' }}\n      >\n        The &apos;status&apos; variant of a frame is often used as a status bar\n        at the end of the window.\n      </Frame>\n    </Frame>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/Frame/Frame.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { createBorderStyles, createBoxStyles } from '../common';\nimport { CommonStyledProps } from '../types';\n\ntype FrameProps = {\n  children?: React.ReactNode;\n  shadow?: boolean;\n} & (\n  | {\n      variant?: 'window' | 'button' | 'field' | 'status';\n    }\n  | {\n      /** @deprecated Use 'window', 'button' or 'status' */\n      variant?: 'outside' | 'inside' | 'well';\n    }\n) &\n  React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\nconst createFrameStyles = (variant: FrameProps['variant']) => {\n  switch (variant) {\n    case 'status':\n    case 'well':\n      return css`\n        ${createBorderStyles({ style: 'status' })}\n      `;\n    case 'window':\n    case 'outside':\n      return css`\n        ${createBorderStyles({ style: 'window' })}\n      `;\n    case 'field':\n      return css`\n        ${createBorderStyles({ style: 'field' })}\n      `;\n    default:\n      return css`\n        ${createBorderStyles()}\n      `;\n  }\n};\n\nconst StyledFrame = styled.div<Required<Pick<FrameProps, 'variant'>>>`\n  position: relative;\n  font-size: 1rem;\n  ${({ variant }) => createFrameStyles(variant)}\n  ${({ variant }) =>\n    createBoxStyles(\n      variant === 'field'\n        ? { background: 'canvas', color: 'canvasText' }\n        : undefined\n    )}\n`;\n\nconst Frame = forwardRef<HTMLDivElement, FrameProps>(\n  ({ children, shadow = false, variant = 'window', ...otherProps }, ref) => {\n    return (\n      <StyledFrame ref={ref} shadow={shadow} variant={variant} {...otherProps}>\n        {children}\n      </StyledFrame>\n    );\n  }\n);\n\nFrame.displayName = 'Frame';\n\nexport { Frame, FrameProps };\n"
  },
  {
    "path": "src/GroupBox/GroupBox.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme, theme } from '../../test/utils';\n\nimport { GroupBox } from './GroupBox';\n\ndescribe('<GroupBox />', () => {\n  it('renders GroupBox', () => {\n    const { container } = renderWithTheme(<GroupBox />);\n    const groupBox = container.firstChild as HTMLFieldSetElement;\n\n    expect(groupBox).toBeInTheDocument();\n  });\n  it('renders children', () => {\n    const textContent = 'Hi there!';\n    const { getByText } = renderWithTheme(\n      <GroupBox>\n        <span>{textContent}</span>\n      </GroupBox>\n    );\n    expect(getByText(textContent)).toBeInTheDocument();\n  });\n\n  describe('prop: label', () => {\n    it('renders Label', () => {\n      const labelText = 'Name:';\n      const { container } = renderWithTheme(<GroupBox label={labelText} />);\n      const groupBox = container.firstChild as HTMLFieldSetElement;\n      const legend = groupBox.querySelector('legend');\n      expect(legend?.textContent).toBe(labelText);\n    });\n    it('when not provided, <legend /> element is not rendered', () => {\n      const { container } = renderWithTheme(<GroupBox />);\n      const groupBox = container.firstChild as HTMLFieldSetElement;\n      const legend = groupBox.querySelector('legend');\n      expect(legend).not.toBeInTheDocument();\n    });\n  });\n  describe('prop: disabled', () => {\n    it('renders with disabled text content', () => {\n      const { container } = renderWithTheme(<GroupBox disabled />);\n      const groupBox = container.firstChild as HTMLFieldSetElement;\n\n      expect(groupBox).toHaveAttribute('aria-disabled', 'true');\n\n      expect(groupBox).toHaveStyleRule('color', theme.materialTextDisabled);\n      expect(groupBox).toHaveStyleRule(\n        'text-shadow',\n        `1px 1px ${theme.materialTextDisabledShadow}`\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "src/GroupBox/GroupBox.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport { Checkbox, GroupBox, ScrollView, Window, WindowContent } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n`;\n\nexport default {\n  title: 'Controls/GroupBox',\n  component: GroupBox,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof GroupBox>;\n\nexport function Default() {\n  return (\n    <Window>\n      <WindowContent>\n        <GroupBox label='Label here'>\n          Some content here\n          <span role='img' aria-label='😍'>\n            😍\n          </span>\n        </GroupBox>\n        <br />\n        <GroupBox label='Disabled' disabled>\n          Some content here\n          <span role='img' aria-label='😍'>\n            😍\n          </span>\n        </GroupBox>\n      </WindowContent>\n    </Window>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n\nexport function Flat() {\n  return (\n    <Window>\n      <WindowContent>\n        <ScrollView\n          style={{ padding: '1rem', background: 'white', width: '300px' }}\n        >\n          <GroupBox variant='flat' label='Label here'>\n            Some content here\n            <span role='img' aria-label='😍'>\n              😍\n            </span>\n          </GroupBox>\n          <br />\n          <GroupBox variant='flat' label='Disabled' disabled>\n            Some content here\n            <span role='img' aria-label='😍'>\n              😍\n            </span>\n          </GroupBox>\n        </ScrollView>\n      </WindowContent>\n    </Window>\n  );\n}\n\nFlat.story = {\n  name: 'flat'\n};\n\nexport function ToggleExample() {\n  const [state, setState] = useState(true);\n  return (\n    <Window>\n      <WindowContent>\n        <GroupBox\n          disabled={state}\n          label={\n            <Checkbox\n              style={{ margin: 0 }}\n              label='Enable'\n              checked={!state}\n              onChange={() => setState(!state)}\n            />\n          }\n        >\n          Some content here\n          <span role='img' aria-label='emoji in love'>\n            😍\n          </span>\n        </GroupBox>\n      </WindowContent>\n    </Window>\n  );\n}\n\nToggleExample.story = {\n  name: 'toggle example'\n};\n"
  },
  {
    "path": "src/GroupBox/GroupBox.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { createDisabledTextStyles } from '../common';\nimport { CommonStyledProps } from '../types';\n\ntype GroupBoxProps = {\n  label?: React.ReactNode;\n  children?: React.ReactNode;\n  disabled?: boolean;\n  variant?: 'default' | 'flat';\n} & React.FieldsetHTMLAttributes<HTMLFieldSetElement> &\n  CommonStyledProps;\n\nconst StyledFieldset = styled.fieldset<\n  Pick<GroupBoxProps, 'variant'> & { $disabled: boolean }\n>`\n  position: relative;\n  border: 2px solid\n    ${({ theme, variant }) =>\n      variant === 'flat' ? theme.flatDark : theme.borderLightest};\n  padding: 16px;\n  margin-top: 8px;\n  font-size: 1rem;\n  color: ${({ theme }) => theme.materialText};\n  ${({ variant }) =>\n    variant !== 'flat' &&\n    css`\n      box-shadow: -1px -1px 0 1px ${({ theme }) => theme.borderDark},\n        inset -1px -1px 0 1px ${({ theme }) => theme.borderDark};\n    `}\n  ${props => props.$disabled && createDisabledTextStyles()}\n`;\n\nconst StyledLegend = styled.legend<Pick<GroupBoxProps, 'variant'>>`\n  display: flex;\n  position: absolute;\n  top: 0;\n  left: 8px;\n  transform: translateY(calc(-50% - 2px));\n  padding: 0 8px;\n\n  font-size: 1rem;\n  background: ${({ theme, variant }) =>\n    variant === 'flat' ? theme.canvas : theme.material};\n`;\n\nconst GroupBox = forwardRef<HTMLFieldSetElement, GroupBoxProps>(\n  (\n    { label, disabled = false, variant = 'default', children, ...otherProps },\n    ref\n  ) => {\n    return (\n      <StyledFieldset\n        aria-disabled={disabled}\n        $disabled={disabled}\n        variant={variant}\n        ref={ref}\n        {...otherProps}\n      >\n        {label && <StyledLegend variant={variant}>{label}</StyledLegend>}\n        {children}\n      </StyledFieldset>\n    );\n  }\n);\n\nGroupBox.displayName = 'GroupBox';\n\nexport { GroupBox, GroupBoxProps };\n"
  },
  {
    "path": "src/Handle/Handle.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Handle } from './Handle';\n\ndescribe('<Handle />', () => {\n  it('should render bar', () => {\n    const { container } = renderWithTheme(<Handle />);\n    const barEl = container.firstChild;\n\n    expect(barEl).toBeInTheDocument();\n  });\n\n  it('should handle custom style', () => {\n    const { container } = renderWithTheme(\n      <Handle style={{ backgroundColor: 'papayawhip' }} />\n    );\n    const barEl = container.firstChild;\n\n    expect(barEl).toHaveAttribute('style', 'background-color: papayawhip;');\n  });\n\n  it('should handle custom props', () => {\n    const customProps = { title: 'potatoe' };\n    const { container } = renderWithTheme(<Handle {...customProps} />);\n    const barEl = container.firstChild;\n\n    expect(barEl).toHaveAttribute('title', 'potatoe');\n  });\n\n  describe('prop: size', () => {\n    it('should set proper size', () => {\n      const { container } = renderWithTheme(<Handle size='85%' />);\n      const barEl = container.firstChild;\n\n      expect(barEl).toHaveStyleRule('height', '85%');\n    });\n\n    it('when passed a number, sets size in px', () => {\n      const { container } = renderWithTheme(<Handle size={25} />);\n      const barEl = container.firstChild;\n\n      expect(barEl).toHaveStyleRule('height', '25px');\n    });\n  });\n});\n"
  },
  {
    "path": "src/Handle/Handle.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { AppBar, Button, Handle, Toolbar } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n`;\n\nexport default {\n  title: 'Controls/Handle',\n  component: Handle,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Handle>;\n\nexport function Default() {\n  return (\n    <AppBar>\n      <Toolbar>\n        <Handle size={35} />\n        <Button variant='menu'>Edit</Button>\n        <Button variant='menu' disabled>\n          Save\n        </Button>\n        <Handle size={35} />\n      </Toolbar>\n    </AppBar>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/Handle/Handle.tsx",
    "content": "import React from 'react';\nimport styled from 'styled-components';\nimport { CommonStyledProps } from '../types';\nimport { getSize } from '../common/utils';\n\ntype HandleProps = {\n  size?: string | number;\n} & React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\n// TODO: add horizontal variant\n// TODO: allow user to specify number of bars (like 3 horizontal bars for drag handle)\nconst Handle = styled.div<HandleProps>`\n  ${({ theme, size = '100%' }) => `\n  display: inline-block;\n  box-sizing: border-box;\n  height: ${getSize(size)};\n  width: 5px;\n  border-top: 2px solid ${theme.borderLightest};\n  border-left: 2px solid ${theme.borderLightest};\n  border-bottom: 2px solid ${theme.borderDark};\n  border-right: 2px solid ${theme.borderDark};\n  background: ${theme.material};\n`}\n`;\n\nHandle.displayName = 'Handle';\n\nexport { Handle, HandleProps };\n"
  },
  {
    "path": "src/Hourglass/Hourglass.spec.tsx",
    "content": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { Hourglass } from './Hourglass';\n\ndescribe('<Hourglass />', () => {\n  it('should render hourglass', () => {\n    const { container } = render(<Hourglass />);\n    const hourglass = container.firstElementChild;\n\n    expect(hourglass).toBeInTheDocument();\n  });\n\n  it('should render correct size', () => {\n    const { container } = render(<Hourglass size='66px' />);\n    const hourglass = container.firstElementChild;\n\n    const computedStyles = hourglass\n      ? window.getComputedStyle(hourglass)\n      : null;\n    expect(computedStyles?.width).toBe('66px');\n    expect(computedStyles?.height).toBe('66px');\n  });\n\n  it('should handle custom props', () => {\n    const customProps: React.HTMLAttributes<HTMLSpanElement> = {\n      title: 'hourglass'\n    };\n    const { container } = render(<Hourglass {...customProps} />);\n    const hourglass = container.firstElementChild;\n\n    expect(hourglass).toHaveAttribute('title', 'hourglass');\n  });\n});\n"
  },
  {
    "path": "src/Hourglass/Hourglass.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Hourglass } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n`;\n\nexport default {\n  title: 'Other/Hourglass',\n  component: Hourglass,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Hourglass>;\n\nexport function Default() {\n  return <Hourglass size={32} style={{ margin: 20 }} />;\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/Hourglass/Hourglass.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { getSize } from '../common/utils';\nimport { CommonStyledProps } from '../types';\nimport base64hourglass from './base64hourglass';\n\ntype HourglassProps = {\n  size?: string | number;\n} & React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\nconst StyledContainer = styled.div<Required<Pick<HourglassProps, 'size'>>>`\n  display: inline-block;\n  height: ${({ size }) => getSize(size)};\n  width: ${({ size }) => getSize(size)};\n`;\n\nconst StyledHourglass = styled.span`\n  display: block;\n  background: ${base64hourglass};\n  background-size: cover;\n  width: 100%;\n  height: 100%;\n`;\n\nconst Hourglass = forwardRef<HTMLSpanElement, HourglassProps>(\n  ({ size = 30, ...otherProps }, ref) => {\n    return (\n      <StyledContainer size={size} ref={ref} {...otherProps}>\n        <StyledHourglass />\n      </StyledContainer>\n    );\n  }\n);\n\nHourglass.displayName = 'Hourglass';\n\nexport { Hourglass, HourglassProps };\n"
  },
  {
    "path": "src/Hourglass/base64hourglass.tsx",
    "content": "const base64hourglass =\n  \"url('data:image/gif;base64,R0lGODlhPAA8APQAADc3N6+vr4+Pj05OTvn5+V1dXZ+fn29vby8vLw8PD/X19d/f37S0tJSUlLq6und3d39/f9XV1c/Pz+bm5qamphkZGWZmZsbGxr+/v+rq6tra2u/v7yIiIv///wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBAAfACH+I1Jlc2l6ZWQgb24gaHR0cHM6Ly9lemdpZi5jb20vcmVzaXplACwAAAAAPAA8AAAF/+AnjmRpnmiqrmzrvnAsz3Rt37jr7Xzv/8BebhQsGn1D0XFZTH6YUGQySvU4fYKAdsvtdi1Cp3In6ZjP6HTawBMTyWbFYk6v18/snXvsKXciUApmeVZ7PH6ATIIdhHtPcB0TDQ1gQBCTBINthpBnAUEaa5tuh2mfQKFojZx9aRMSEhA7FLAbonqsfmoUOxFqmriknWm8Hr6/q8IeCAAAx2cTERG2aBTNHMGOj8a/v8WF2m/c3cSj4SQ8C92n4Ocm6evm7ui9CosdBPbs8yo8E2YO5PE74Q+gwIElCnYImA3hux3/Fh50yCciw3YUt2GQtiiDtGQO4f3al1GkGpIDeXlg0KDhXpoMLBtMVPaMnJlv/HjUtIkzHA8HEya4tLkhqICGV4bZVAMyaaul3ZpOUQoVz8wbpaoyvWojq1ZVXGt4/QoM49SnZMs6GktW6hC2X93mgKtVbtceWbzo9VIJKdYqUJwCPiJ4cJOzhg+/TWwko+PHkCNLdhgCACH5BAUEAB8ALAAAAAABAAEAAAUD4BcCACH5BAUEAB8ALBYADAAQAA0AAAVFYCeOZPmVaKqimeO+MPxFXv3d+F17Cm3nuJ1ic7lAdroapUjABZCfnQb4ef6k1OHGULtsNk3qjVKLiIFkj/mMIygU4VwIACH5BAUEAB8ALAAAAAABAAEAAAUD4BcCACH5BAUEAB8ALBkAIwAKAAcAAAUp4CdehrGI6Ed5XpSKa4teguBoGlVPAXuJBpam5/l9gh7NZrFQiDJMRQgAIfkEBQQAHwAsAAAAAAEAAQAABQPgFwIAIfkEBQQAHwAsFgAPABAAIQAABVBgJ45kaZ5oakZB67bZ+M10bd94ru987//AoHBILNYYAsGlR/F4IkwnlLeZTBQ9UlaWwzweERHjuzAKFZkMYYZWm4mOw0ETfdanO8Vms7aFAAAh+QQFBAAfACwAAAAAAQABAAAFA+AXAgAh+QQFBAAfACwZABIACgAeAAAFUGAnjmRpnij5rerqtu4Hx3Rt33iu758iZrUZa1TDCASLGsXjiSiZzmFnM5n4TNJSdmREElfL5lO8cgwGACbgrAkwPat3+x1naggKRS+f/4QAACH5BAUEAB8ALAAAAAABAAEAAAUD4BcCACH5BAUEAB8ALBYAIwAQAA0AAAVE4CeOXdmNaGqeabu27SUIC5xSnifZKK7zl8djkCsIaylGziNaakaEzcbH/Cwl0k9kuWxyPYptzrZULA7otFpNIK1eoxAAIfkEBQQAHwAsAAAAAAEAAQAABQPgFwIAIfkEBQQAHwAsAAAAAAEAAQAABQPgFwIAIfkEBQQAHwAsAAAAAAEAAQAABQPgFwIAIfkEBQQAHwAsAAAAAAEAAQAABQPgFwIAIfkEBQQAHwAsAAAAAAEAAQAABQPgFwIAIfkEBQQAHwAsAAAAAAEAAQAABQPgFwIAIfkEBQQAHwAsAAAAAAEAAQAABQPgFwIAIfkEBQQAHwAsAAAAAAEAAQAABQPgFwIAIfkEBQQAHwAsAAAAAAEAAQAABQPgFwIAIfkECQQAHwAsDgAEACAANAAABTHgJ45kaZ5oqq5s675wLM90bd94ru987//AoHBILBqPyKRyyWw6n9CodEqtWq/Y7CoEACH5BAUEAB8ALAAAAAA8ADwAAAX/4CeOZGmeaKqubFt6biy3Xj3fuFjveU/vPJ/wBAQOj6RiEClUGpk9IMAJxQEdmQK1Grt2OhutkvurOb7f8JaM8qLT4iKbuDu/0erxfOS+4+NPex9mfn55coIfCAuFhoBLbDUAjI1vh4FkOxSVd5eQXB4GnI5rXAAbo6R6VTUFqKmWjzasNaKwsaVIHhAEt3cLTjBQA6++XwoHuUM1vMYdyMorwoN8wkC2t9A8s102204Wxana3DNAAQO1FjUCEDXhvuTT5nUdEwOiGxa8BBDwXxKaLTiAKoMFRvJy9CmmoFcHAgrQSEiwKwICDwU0pAMQIdmnboR8TfwWrJyMPrAiz1DkNs2aSRbe6hnr99LEvDJ9IB5DQ8Dhm36glNh5COGBAmQNHrbz+WXBFChOTqFx5+GBxwYCmL1ZcPHmMiWuvkTgECzBBUvrvH4tErbDWCcYDB2IBPbV2yJJ72SZ46TtXSB5v2RIp1ZXXbFkgWxCc68mk752E3tY/OZeIsiIaxi9o+BBokGH3SZ+4FPbZ8yiPQxNeDl0hNUeHWcKjYb1Zx20bd/GzRaV7t28gRSYELvw7pIfgVcLplwF8+bOo0Ffjmm6zerWrxvPzoe79w8hAAAh+QQJBAAfACwBAAEAOgA6AAAFRuAnjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgsGo/IpHLJbDqf0Kh0Sq1ar9isdsvter/gsHhMLpvP6LR6zW673/D4MgQAIfkEBQQAHwAsAAAAADwAPAAABf/gJ45kaZ5oqq5s675wLM90bd94ru987//AoHBILBqPyJxnyTQym6nn0ilVSa9XGHY7jXKx2m/WK36Gy1CUVCBpu9+OtNqDeNslgip5Gej4/4ATcidLAICHHQF6c0x9iH+CXV6Gj36KZnsejgsREQSACp0Yg0ydEZWWi4RPjgdLG48apEuogJeDJVKtr7GzHrV/t5KrjX6uHhQMF4cKCwujTxHOwKmYjHzGTw+VEVIK1MGqJrrZTNuP3U/f4IniuazlSwMUFMugE/j47NW4JOQdx9bsoybMgxV4ALEIGAis4MFiCZkUaLPgUAYHGDF+Yucw0y5z3Lzt63hNUzwP5xCRpWOyDhxJYtgiStBQEVCGAAEM6MLp0p0/hMdgIZI17AOTntZgmowo9BBRgz9/EfQ54h8BBS39bKDXwBc9CrVejkNYKRLUSWGpivhXtt9PSpXEvmNiwYDdu3jzFB3LAa9fAxbUGkXjtmSZh4TPJM4kRgbhvVEL9xhTEongJJgza97MubPnz6BDix5NurTp0yJCAAAh+QQJBAAfACwEAA4ANAAgAAAFMeAnjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgsGo/IpHLJbDqf0Kh0Sq1ar9jsKgQAIfkEBQQAHwAsAAAAADwAPAAABf/gJ45kaZ5oqq5s6bVwLHu0bN8uXeM8rP+9YOoHFBpHRN1xmSwue02A82lrFjaOKbVl3XQ6WeWWm7x+v+HdeFj2ntHaNbL9jUAI5/RLTurWOR53eXFbfh0RgB4PCm9hfCKGiDSLb18Bjx+RiR4HjG8TA3trmkSdZxuhalSkRA2VBqpPrD+ulR0Go3SHmz8CeG8bFqJMupJNHr5nCsKxQccTg4oUNA0YCYG/HQQQYsSlnmCUFLUXgm8EAsPeP6Zf2baV2+rEmTrt8PDyzS7O9uD4b5YV2VGjGw52/wB+CaYjlQcpNBAQioHwy4QMCxe4i3BKGIQN3K7AArBATz8anUDADcgQDMGCbQkknDKAh4ABNxQ0gpnoQ8eDVAUO0ADAzUNMhbZMQiG4R4mOo0gb8eTCQgeEqJVM7juCDWvWJnI4ev2aZIwHl2PfZIBIZBXKtAsLgC1kJu0GuWXNaoB7d67ZlWP75jVLw4JXwW35PNSJFPFUrmIb402smFNCW44N5kJ5+dTkx+vuAfus+VHF0X4xzeHsObXq1ZY7ZN76mt0C0rRf1zuWW/du175PHAu+YjhxFcCPm6CsHHnv5kig6w4BACH5BAkEAB8ALAEAAQA6ADoAAAVG4CeOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSCwaj8ikcslsOp/QqHRKrVqv2Kx2y+16v+CweEwum8/otHrNbrvf8PgyBAAh+QQFBAAfACwAAAAAPAA8AAAF/+AnjmRpnmiqrmzrvnAsz3Rt37jr7Xzv/8BebhQsGn1D0XFZTH6YUGQySvU4fYKAdsvtdi1Cp3In6ZjP6HTawBMTyWbFYk6v18/snXvsKXciUApmeVZ7PH6ATIIdhHtPcB0TDQ1gQBCTBINthpBnAUEaa5tuh2mfQKFojZx9aRMSEhA7FLAbonqsfmoUOxFqmriknWm8Hr6/q8IeCAAAx2cTERG2aBTNHMGOj8a/v8WF2m/c3cSj4SQ8C92n4Ocm6evm7ui9CosdBPbs8yo8E2YO5PE74Q+gwIElCnYImA3hux3/Fh50yCciw3YUt2GQtiiDtGQO4f3al1GkGpIDeXlg0KDhXpoMLBtMVPaMnJlv/HjUtIkzHA8HEya4tLkhqICGV4bZVAMyaaul3ZpOUQoVz8wbpaoyvWojq1ZVXGt4/QoM49SnZMs6GktW6hC2X93mgKtVbtceWbzo9VIJKdYqUJwCPiJ4cJOzhg+/TWwko+PHkCNLdhgCACH5BAUEAB8ALAAAAAABAAEAAAUD4BcCADs=')\";\nexport default base64hourglass;\n"
  },
  {
    "path": "src/MenuList/MenuList.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { MenuList } from './MenuList';\n\ndescribe('<MenuList />', () => {\n  it('renders MenuList', () => {\n    const { container } = renderWithTheme(<MenuList />);\n    const menuList = container.firstElementChild;\n\n    expect(menuList).toBeInTheDocument();\n  });\n  it('is an ul', () => {\n    const { container } = renderWithTheme(<MenuList />);\n    const menuList = container.firstElementChild;\n\n    expect(menuList?.tagName).toBe('UL');\n  });\n  it('renders children', () => {\n    const textContent = 'Hi there!';\n    const { getByText } = renderWithTheme(\n      <MenuList>\n        <span>{textContent}</span>\n      </MenuList>\n    );\n    expect(getByText(textContent)).toBeInTheDocument();\n  });\n\n  describe('prop: inline', () => {\n    it('renders inline', () => {\n      const { container } = renderWithTheme(<MenuList inline />);\n      const menuList = container.firstElementChild;\n\n      expect(menuList).toHaveStyleRule('display', 'inline-flex');\n      expect(menuList).toHaveStyleRule('align-items', 'center');\n    });\n  });\n  describe('prop: fullWidth', () => {\n    it('has 100% width', () => {\n      const { container } = renderWithTheme(<MenuList fullWidth />);\n      const menuList = container.firstElementChild;\n\n      expect(menuList).toHaveStyleRule('width', '100%');\n    });\n  });\n});\n"
  },
  {
    "path": "src/MenuList/MenuList.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Handle, MenuList, MenuListItem, Separator } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n  display: flex;\n  align-items: center;\n  & > * {\n    margin-right: 1rem;\n  }\n`;\n\nexport default {\n  title: 'Controls/MenuList',\n  component: MenuList,\n  subcomponents: { MenuListItem },\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof MenuList>;\n\nexport function Default() {\n  return (\n    <>\n      <MenuList>\n        <MenuListItem primary>Photos</MenuListItem>\n        <MenuListItem\n          as='a'\n          // TODO: Come up with a more elegant way to allow props when `as` is used\n          // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n          // @ts-ignore\n          href='https://expensive.toys'\n          target='_blank'\n        >\n          Link\n        </MenuListItem>\n        <MenuListItem disabled>Other</MenuListItem>\n      </MenuList>\n      <MenuList inline>\n        <MenuListItem square disabled>\n          <span role='img' aria-label='🌿'>\n            🌿\n          </span>\n        </MenuListItem>\n        <Handle size={38} />\n        <MenuListItem>Tackle</MenuListItem>\n        <MenuListItem>Growl</MenuListItem>\n        <MenuListItem disabled>Razor Leaf</MenuListItem>\n      </MenuList>\n      <MenuList>\n        <MenuListItem primary size='sm'>\n          View\n        </MenuListItem>\n        <Separator />\n        <MenuListItem size='sm'>Paste</MenuListItem>\n        <MenuListItem size='sm'>Paste Shortcut</MenuListItem>\n        <MenuListItem size='sm'>Undo Copy</MenuListItem>\n        <Separator />\n        <MenuListItem size='sm'>Properties</MenuListItem>\n      </MenuList>\n      <MenuList>\n        <MenuListItem square>\n          <span role='img' aria-label='😎'>\n            😎\n          </span>\n        </MenuListItem>\n        <MenuListItem square>\n          <span role='img' aria-label='🤖'>\n            🤖\n          </span>\n        </MenuListItem>\n        <MenuListItem square>\n          <span role='img' aria-label='🎁'>\n            🎁\n          </span>\n        </MenuListItem>\n      </MenuList>\n    </>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/MenuList/MenuList.tsx",
    "content": "import React from 'react';\n\nimport styled from 'styled-components';\nimport { createBorderStyles, createBoxStyles } from '../common';\nimport { CommonStyledProps } from '../types';\n\ntype MenuListProps = React.HTMLAttributes<HTMLUListElement> & {\n  fullWidth?: boolean;\n  shadow?: boolean;\n  inline?: boolean;\n} & CommonStyledProps;\n\n// TODO keyboard controls\nconst MenuList = styled.ul.attrs(() => ({\n  role: 'menu'\n}))<MenuListProps>`\n  box-sizing: border-box;\n  width: ${props => (props.fullWidth ? '100%' : 'auto')};\n  padding: 4px;\n  ${createBorderStyles({ style: 'window' })}\n  ${createBoxStyles()}\n  ${props =>\n    props.inline &&\n    `\n    display: inline-flex;\n    align-items: center;\n  `}\n  list-style: none;\n  position: relative;\n`;\n\nMenuList.displayName = 'MenuList';\n\nexport * from './MenuListItem';\n\nexport { MenuList, MenuListProps };\n"
  },
  {
    "path": "src/MenuList/MenuListItem.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme, theme } from '../../test/utils';\nimport { blockSizes } from '../common/system';\nimport { MenuListItem } from './MenuListItem';\n\nconst defaultSize = 'lg';\ndescribe('<MenuListItem />', () => {\n  it('renders MenuListItem', () => {\n    const { getByRole } = renderWithTheme(<MenuListItem />);\n    const menuListItem = getByRole('menuitem');\n    expect(menuListItem).toBeInTheDocument();\n    expect(menuListItem).not.toHaveAttribute('aria-disabled');\n  });\n  it('renders children', () => {\n    const textContent = 'Hi there!';\n    const { getByText } = renderWithTheme(\n      <MenuListItem>\n        <span>{textContent}</span>\n      </MenuListItem>\n    );\n    expect(getByText(textContent)).toBeInTheDocument();\n  });\n  it('should have a default role of menuitem', () => {\n    const { getByRole } = renderWithTheme(<MenuListItem />);\n    const menuListItem = getByRole('menuitem');\n    expect(menuListItem).toHaveAttribute('role', 'menuitem');\n  });\n\n  it('should render with custom role', () => {\n    const { getByRole } = renderWithTheme(<MenuListItem role='option' />);\n    const menuListItem = getByRole('option');\n    expect(menuListItem).toHaveAttribute('role', 'option');\n  });\n\n  //   it('should have a tabIndex of -1 by default', () => {\n  //     const { getByRole } = renderWithTheme(<MenuListItem role='option' />);\n  //     const menuListItem = getByRole('menuitem');\n  //     expect(menuListItem).toHaveAttribute('tabIndex', '-1');\n  //   });\n  describe('prop: disabled', () => {\n    it('should not trigger onClick callback', () => {\n      const clickHandler = jest.fn();\n      const { getByRole } = renderWithTheme(\n        <MenuListItem disabled onClick={clickHandler} />\n      );\n      const menuListItem = getByRole('menuitem') as HTMLElement;\n      menuListItem.click();\n      expect(clickHandler).not.toBeCalled();\n      expect(menuListItem).toHaveAttribute('aria-disabled', 'true');\n    });\n    it('renders with disabled styles ', () => {\n      const { getByRole } = renderWithTheme(<MenuListItem disabled />);\n      const menuListItem = getByRole('menuitem');\n      expect(menuListItem).toHaveStyleRule('pointer-events', 'none');\n      expect(menuListItem).toHaveStyleRule('color', theme.materialTextDisabled);\n      expect(menuListItem).toHaveStyleRule(\n        'text-shadow',\n        `1px 1px ${theme.materialTextDisabledShadow}`\n      );\n    });\n  });\n  describe('prop: onClick', () => {\n    it('should be called when clicked', () => {\n      const clickHandler = jest.fn();\n      const { getByRole } = renderWithTheme(\n        <MenuListItem onClick={clickHandler} />\n      );\n      const menuListItem = getByRole('menuitem') as HTMLElement;\n      menuListItem.click();\n      expect(clickHandler).toHaveBeenCalledTimes(1);\n    });\n  });\n  describe('prop: square', () => {\n    it('should render square MenuListItem', () => {\n      const { getByRole } = renderWithTheme(<MenuListItem square />);\n      const menuListItem = getByRole('menuitem');\n\n      expect(menuListItem).toHaveStyleRule('width', blockSizes[defaultSize]);\n      expect(menuListItem).toHaveStyleRule('height', blockSizes[defaultSize]);\n    });\n  });\n  describe('prop: size', () => {\n    it('should define MenuListItem height', () => {\n      const size = 'sm';\n      const { getByRole } = renderWithTheme(<MenuListItem size={size} />);\n      const menuListItem = getByRole('menuitem');\n\n      expect(menuListItem).toHaveStyleRule('height', blockSizes[size]);\n    });\n  });\n});\n"
  },
  {
    "path": "src/MenuList/MenuListItem.tsx",
    "content": "import React, { forwardRef } from 'react';\n\nimport styled from 'styled-components';\nimport { createDisabledTextStyles } from '../common';\nimport { blockSizes } from '../common/system';\nimport { CommonStyledProps, Sizes } from '../types';\n\ntype MenuListItemProps = {\n  disabled?: boolean;\n  square?: boolean;\n  primary?: boolean;\n  size?: Sizes;\n} & React.HTMLAttributes<HTMLLIElement> &\n  CommonStyledProps;\n\nexport const StyledMenuListItem = styled.li<{\n  $disabled?: boolean;\n  square?: boolean;\n  primary?: boolean;\n  size: Sizes;\n}>`\n  box-sizing: border-box;\n\n  display: flex;\n  align-items: center;\n  position: relative;\n  height: ${props => blockSizes[props.size]};\n  width: ${props => (props.square ? blockSizes[props.size] : 'auto')};\n  padding: 0 8px;\n  font-size: 1rem;\n  white-space: nowrap;\n  justify-content: ${props =>\n    props.square ? 'space-around' : 'space-between'};\n  text-align: center;\n  line-height: ${props => blockSizes[props.size]};\n  color: ${({ theme }) => theme.materialText};\n  pointer-events: ${({ $disabled }) => ($disabled ? 'none' : 'auto')};\n  font-weight: ${({ primary }) => (primary ? 'bold' : 'normal')};\n  &:hover {\n    ${({ theme, $disabled }) =>\n      !$disabled &&\n      `\n        color: ${theme.materialTextInvert};\n        background: ${theme.hoverBackground};\n      `}\n\n    cursor: default;\n  }\n  ${props => props.$disabled && createDisabledTextStyles()}\n`;\n\nconst MenuListItem = forwardRef<HTMLLIElement, MenuListItemProps>(\n  (\n    {\n      size = 'lg',\n      disabled,\n      // tabIndex: tabIndexProp,\n      square,\n      children,\n      onClick,\n      primary,\n      ...otherProps\n    },\n    ref\n  ) => {\n    // let tabIndex;\n    // if (!disabled) {\n    //   tabIndex = tabIndexProp !== undefined ? tabIndexProp : -1;\n    // }\n\n    return (\n      <StyledMenuListItem\n        $disabled={disabled}\n        size={size}\n        square={square}\n        onClick={disabled ? undefined : onClick}\n        primary={primary}\n        // tabIndex={tabIndex}\n        role='menuitem'\n        ref={ref}\n        aria-disabled={disabled}\n        {...otherProps}\n      >\n        {children}\n      </StyledMenuListItem>\n    );\n  }\n);\n\nMenuListItem.displayName = 'MenuListItem';\n\nexport { MenuListItem, MenuListItemProps };\n"
  },
  {
    "path": "src/Monitor/Monitor.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Monitor } from './Monitor';\n\ndescribe('<Monitor />', () => {\n  it('should render', () => {\n    const { container } = renderWithTheme(<Monitor />);\n    const monitorElement = container.firstElementChild;\n\n    expect(monitorElement).toBeInTheDocument();\n  });\n\n  it('should handle custom props', () => {\n    const customProps: React.HTMLAttributes<HTMLDivElement> = {\n      title: 'potatoe'\n    };\n    const { container } = renderWithTheme(<Monitor {...customProps} />);\n    const monitorElement = container.firstElementChild;\n\n    expect(monitorElement).toHaveAttribute('title', 'potatoe');\n  });\n\n  describe('prop: backgroundStyles', () => {\n    it('should forward styles to background element', () => {\n      const { getByTestId } = renderWithTheme(\n        <Monitor backgroundStyles={{ backgroundColor: 'papayawhip' }} />\n      );\n      const backgroundElement = getByTestId('background');\n\n      expect(backgroundElement).toHaveAttribute(\n        'style',\n        'background-color: papayawhip;'\n      );\n    });\n  });\n\n  describe('prop: children', () => {\n    it('children should be rendered in background element', () => {\n      const { getByTestId } = renderWithTheme(<Monitor>Hi!</Monitor>);\n      const backgroundElement = getByTestId('background');\n\n      expect(backgroundElement.innerHTML).toBe('Hi!');\n    });\n  });\n});\n"
  },
  {
    "path": "src/Monitor/Monitor.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Monitor } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n`;\n\nexport default {\n  title: 'Other/Monitor',\n  component: Monitor,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Monitor>;\n\nexport function Default() {\n  return <Monitor backgroundStyles={{ background: 'blue' }} />;\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/Monitor/Monitor.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\n\nimport { StyledScrollView } from '../ScrollView/ScrollView';\n\ntype MonitorProps = {\n  backgroundStyles?: React.CSSProperties;\n  children?: React.ReactNode;\n};\n\nconst Wrapper = styled.div`\n  position: relative;\n  display: inline-block;\n  padding-bottom: 26px;\n`;\n\nconst Inner = styled.div`\n  position: relative;\n`;\n\nconst MonitorBody = styled.div`\n  position: relative;\n  z-index: 1;\n  box-sizing: border-box;\n  width: 195px;\n  height: 155px;\n  padding: 12px;\n  background: ${({ theme }) => theme.material};\n  border-top: 4px solid ${({ theme }) => theme.borderLightest};\n  border-left: 4px solid ${({ theme }) => theme.borderLightest};\n  border-bottom: 4px solid ${({ theme }) => theme.borderDark};\n  border-right: 4px solid ${({ theme }) => theme.borderDark};\n\n  outline: 1px dotted ${({ theme }) => theme.material};\n  outline-offset: -3px;\n  &:before {\n    content: '';\n    position: absolute;\n    left: 0;\n    top: 0;\n    width: 100%;\n    height: 100%;\n    outline: 1px dotted ${({ theme }) => theme.material};\n  }\n  box-shadow: 1px 1px 0 1px ${({ theme }) => theme.borderDarkest};\n\n  &:after {\n    content: '';\n    display: inline-block;\n    position: absolute;\n    bottom: 4px;\n    right: 12px;\n    width: 10px;\n    border-top: 2px solid #4d9046;\n    border-bottom: 2px solid #07ff00;\n  }\n`;\n\nconst Background = styled(StyledScrollView).attrs(() => ({\n  'data-testid': 'background'\n}))`\n  width: 100%;\n  height: 100%;\n`;\n\nconst Stand = styled.div`\n  box-sizing: border-box;\n  position: absolute;\n  top: calc(100% + 2px);\n  left: 50%;\n  transform: translateX(-50%);\n  height: 10px;\n  width: 50%;\n  background: ${({ theme }) => theme.material};\n  border-left: 2px solid ${({ theme }) => theme.borderLightest};\n  border-bottom: 2px solid ${({ theme }) => theme.borderDarkest};\n  border-right: 2px solid ${({ theme }) => theme.borderDarkest};\n  box-shadow: inset 0px 0px 0px 2px ${({ theme }) => theme.borderDark};\n\n  &:before {\n    content: '';\n    position: absolute;\n    top: calc(100% + 2px);\n    left: 50%;\n    transform: translateX(-50%);\n    width: 80%;\n    height: 8px;\n    background: ${({ theme }) => theme.material};\n    border-left: 2px solid ${({ theme }) => theme.borderLightest};\n    border-right: 2px solid ${({ theme }) => theme.borderDarkest};\n    box-shadow: inset 0px 0px 0px 2px ${({ theme }) => theme.borderDark};\n  }\n  &:after {\n    content: '';\n    position: absolute;\n    top: calc(100% + 8px);\n    left: 50%;\n    transform: translateX(-50%);\n    width: 150%;\n    height: 4px;\n    background: ${({ theme }) => theme.material};\n    border: 2px solid ${({ theme }) => theme.borderDark};\n    border-bottom: none;\n    box-shadow: inset 1px 1px 0px 1px ${({ theme }) => theme.borderLightest},\n      1px 1px 0 1px ${({ theme }) => theme.borderDarkest};\n  }\n`;\n\nconst Monitor = forwardRef<HTMLDivElement, MonitorProps>(\n  ({ backgroundStyles, children, ...otherProps }, ref) => {\n    return (\n      <Wrapper ref={ref} {...otherProps}>\n        <Inner>\n          <MonitorBody>\n            <Background style={backgroundStyles}>{children}</Background>\n          </MonitorBody>\n          <Stand />\n        </Inner>\n      </Wrapper>\n    );\n  }\n);\n\nMonitor.displayName = 'Monitor';\n\nexport { Monitor, MonitorProps };\n"
  },
  {
    "path": "src/NumberInput/NumberInput.spec.tsx",
    "content": "import { fireEvent } from '@testing-library/react';\nimport React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { NumberInput } from './NumberInput';\n\n// TODO: should we pass number or string to callbacks?\ndescribe('<NumberInput />', () => {\n  it('should call onChange on spin buttons click', () => {\n    const handleChange = jest.fn();\n\n    const { getByTestId } = renderWithTheme(\n      <NumberInput onChange={handleChange} defaultValue={2} />\n    );\n    const spinButton = getByTestId('increment');\n    spinButton.click();\n    expect(handleChange).toHaveBeenCalledTimes(1);\n    expect(handleChange).toHaveBeenCalledWith(3);\n  });\n\n  it('should call onChange on blur after keyboard input', () => {\n    const handleChange = jest.fn();\n    const { container } = renderWithTheme(\n      <NumberInput onChange={handleChange} defaultValue={0} />\n    );\n    const input = container.querySelector('input') as HTMLInputElement;\n    input.focus();\n    fireEvent.change(input, { target: { value: '777' } });\n\n    expect(handleChange).toHaveBeenCalledTimes(0);\n    input.blur();\n\n    expect(handleChange).toHaveBeenCalledTimes(1);\n    expect(handleChange).toHaveBeenCalledWith(777);\n  });\n\n  // TODO: this test passes even tho it fails in real-life\n  it('should not call onChange on blur, when clicked element is one of the spin buttons', () => {\n    const handleChange = jest.fn();\n\n    const { getByTestId, container } = renderWithTheme(\n      <NumberInput onChange={handleChange} value={0} />\n    );\n    const input = container.querySelector('input') as HTMLInputElement;\n    const incrementButton = getByTestId('increment');\n\n    input.focus();\n    fireEvent.keyDown(document.activeElement as HTMLInputElement, {\n      key: '2'\n    });\n    incrementButton.click();\n    expect(handleChange).toHaveBeenCalledTimes(1);\n  });\n\n  it('should give correct result after user changes input value and then clicks increment button', () => {\n    const handleChange = jest.fn();\n    const { container, getByTestId } = renderWithTheme(\n      <NumberInput onChange={handleChange} defaultValue={0} />\n    );\n    const input = container.querySelector('input') as HTMLInputElement;\n    const incrementButton = getByTestId('increment');\n\n    fireEvent.change(input, { target: { value: '2' } });\n    incrementButton.click();\n\n    expect(handleChange).toHaveBeenCalledWith(3);\n  });\n\n  it('should reach max value', () => {\n    const { getByTestId, container } = renderWithTheme(\n      <NumberInput defaultValue={90} min={0} max={100} step={10} />\n    );\n    const input = container.querySelector('input') as HTMLInputElement;\n    const incrementButton = getByTestId('increment');\n    incrementButton.click();\n\n    expect(input.value).toBe('100');\n  });\n\n  it('should reach min value', () => {\n    const { getByTestId, container } = renderWithTheme(\n      <NumberInput defaultValue={10} min={0} max={100} step={10} />\n    );\n    const input = container.querySelector('input') as HTMLInputElement;\n    const decrementButton = getByTestId('decrement');\n    decrementButton.click();\n\n    expect(input.value).toBe('0');\n  });\n\n  describe('prop: step', () => {\n    it('should be 1 by default', () => {\n      const { getByTestId, container } = renderWithTheme(\n        <NumberInput defaultValue={0} />\n      );\n      const input = container.querySelector('input') as HTMLInputElement;\n      const incrementButton = getByTestId('increment');\n      incrementButton.click();\n\n      expect(input.value).toBe('1');\n    });\n\n    it('should change value by specified step', () => {\n      const { getByTestId, container } = renderWithTheme(\n        <NumberInput defaultValue={10} step={3} />\n      );\n      const input = container.querySelector('input') as HTMLInputElement;\n      const decrementButton = getByTestId('decrement');\n      decrementButton.click();\n\n      expect(input.value).toBe('7');\n    });\n\n    it('should handle decimal step', () => {\n      const { getByTestId, container } = renderWithTheme(\n        <NumberInput defaultValue={10} step={0.3} />\n      );\n      const input = container.querySelector('input') as HTMLInputElement;\n      const decrementButton = getByTestId('decrement');\n      decrementButton.click();\n\n      expect(input.value).toBe('9.7');\n    });\n  });\n\n  describe('prop: disabled', () => {\n    it('should render disabled', () => {\n      const { getByTestId, container } = renderWithTheme(\n        <NumberInput defaultValue={10} disabled />\n      );\n      const input = container.querySelector('input') as HTMLInputElement;\n      const incrementButton = getByTestId('increment');\n      const decrementButton = getByTestId('decrement');\n\n      expect(input).toHaveAttribute('disabled');\n      expect(incrementButton).toHaveAttribute('disabled');\n      expect(decrementButton).toHaveAttribute('disabled');\n    });\n\n    it('should not react to button clicks', () => {\n      const { getByTestId, container } = renderWithTheme(\n        <NumberInput defaultValue={10} disabled />\n      );\n      const input = container.querySelector('input') as HTMLInputElement;\n      const incrementButton = getByTestId('increment');\n      const decrementButton = getByTestId('decrement');\n\n      incrementButton.click();\n      expect(input.value).toBe('10');\n\n      decrementButton.click();\n      expect(input.value).toBe('10');\n    });\n  });\n\n  describe('prop: width', () => {\n    it('should render component of specified width', () => {\n      const { container } = renderWithTheme(\n        <NumberInput defaultValue={10} disabled width={93} />\n      );\n      expect(\n        getComputedStyle(container.firstElementChild as HTMLInputElement).width\n      ).toBe('93px');\n    });\n\n    it('should handle %', () => {\n      const { container } = renderWithTheme(\n        <NumberInput defaultValue={10} disabled width='93%' />\n      );\n      expect(\n        getComputedStyle(container.firstElementChild as HTMLInputElement).width\n      ).toBe('93%');\n    });\n  });\n});\n"
  },
  {
    "path": "src/NumberInput/NumberInput.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { ScrollView, NumberInput } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  background: ${({ theme }) => theme.material};\n  padding: 5rem;\n  & > * {\n    margin-bottom: 1rem;\n  }\n\n  #cutout {\n    background: ${({ theme }) => theme.canvas};\n    padding: 2rem;\n    width: 300px;\n    & > div > * {\n      margin-bottom: 1rem;\n    }\n  }\n`;\n\nexport default {\n  title: 'Controls/NumberInput',\n  component: NumberInput,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof NumberInput>;\n\nexport function Default() {\n  return (\n    <>\n      <NumberInput defaultValue={3} step={1.5} min={1.5} max={9} width={130} />\n      <br />\n      <NumberInput defaultValue={1995} width={130} />\n      <br />\n      <NumberInput disabled defaultValue={2020} width={130} />\n    </>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n\nexport function Flat() {\n  return (\n    <ScrollView id='cutout'>\n      <p>\n        When you want to use NumberInput on a light background (like scrollable\n        content), just use the flat variant:\n      </p>\n      <NumberInput\n        variant='flat'\n        defaultValue={1.5}\n        min={0}\n        max={9}\n        width='130px'\n      />\n      <br />\n      <NumberInput variant='flat' defaultValue={1995} width='130px' />\n      <br />\n      <NumberInput variant='flat' disabled defaultValue={2020} width='130px' />\n    </ScrollView>\n  );\n}\n\nFlat.story = {\n  name: 'flat'\n};\n"
  },
  {
    "path": "src/NumberInput/NumberInput.tsx",
    "content": "import React, { forwardRef, useCallback } from 'react';\nimport styled, { css } from 'styled-components';\n\nimport { Button } from '../Button/Button';\nimport useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';\nimport { blockSizes } from '../common/system';\nimport { clamp, getSize } from '../common/utils';\nimport { TextInput } from '../TextInput/TextInput';\nimport { CommonStyledProps } from '../types';\n\ntype NumberInputProps = {\n  className?: string;\n  defaultValue?: number;\n  disabled?: boolean;\n  max?: number;\n  min?: number;\n  readOnly?: boolean;\n  step?: number;\n  onChange?: (value: number) => void;\n  style?: React.CSSProperties;\n  value?: number;\n  variant?: 'default' | 'flat';\n  width?: string | number;\n} & CommonStyledProps;\n\nconst StyledNumberInputWrapper = styled.div`\n  display: inline-flex;\n  align-items: center;\n`;\n\nconst StyledButton = styled(Button)`\n  width: 30px;\n  padding: 0;\n  flex-shrink: 0;\n\n  ${({ variant }) =>\n    variant === 'flat'\n      ? css`\n          height: calc(50% - 1px);\n        `\n      : css`\n          height: 50%;\n        `}\n`;\n\nconst StyledButtonWrapper = styled.div<Pick<NumberInputProps, 'variant'>>`\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: space-between;\n\n  ${({ variant }) =>\n    variant === 'flat'\n      ? css`\n          height: calc(${blockSizes.md} - 4px);\n        `\n      : css`\n          height: ${blockSizes.md};\n          margin-left: 2px;\n        `}\n`;\n\nconst StyledButtonIcon = styled.span<{ invert?: boolean }>`\n  width: 0px;\n  height: 0px;\n  display: inline-block;\n  ${({ invert }) =>\n    invert\n      ? css`\n          border-left: 4px solid transparent;\n          border-right: 4px solid transparent;\n          border-bottom: 4px solid ${({ theme }) => theme.materialText};\n        `\n      : css`\n          border-left: 4px solid transparent;\n          border-right: 4px solid transparent;\n          border-top: 4px solid ${({ theme }) => theme.materialText};\n        `}\n  ${StyledButton}:disabled & {\n    filter: drop-shadow(\n      1px 1px 0px ${({ theme }) => theme.materialTextDisabledShadow}\n    );\n    ${({ invert }) =>\n      invert\n        ? css`\n            border-bottom-color: ${({ theme }) => theme.materialTextDisabled};\n          `\n        : css`\n            border-top-color: ${({ theme }) => theme.materialTextDisabled};\n          `}\n  }\n`;\nconst NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(\n  (\n    {\n      className,\n      defaultValue,\n      disabled = false,\n      max,\n      min,\n      onChange,\n      readOnly,\n      step = 1,\n      style,\n      value,\n      variant = 'default',\n      width,\n      ...otherProps\n    },\n    ref\n  ) => {\n    const [valueDerived, setValueState] = useControlledOrUncontrolled({\n      defaultValue,\n      onChange,\n      readOnly,\n      value\n    });\n\n    const handleInputChange = useCallback(\n      (e: React.ChangeEvent<HTMLInputElement>) => {\n        const newValue = parseFloat(e.target.value);\n        setValueState(newValue);\n      },\n      [setValueState]\n    );\n\n    const handleClick = useCallback(\n      (delta: number) => {\n        const newValue = clamp(\n          parseFloat(((valueDerived ?? 0) + delta).toFixed(2)),\n          min ?? null,\n          max ?? null\n        );\n\n        setValueState(newValue);\n\n        onChange?.(newValue);\n      },\n      [max, min, onChange, setValueState, valueDerived]\n    );\n\n    const onBlur = useCallback(() => {\n      if (valueDerived !== undefined) {\n        onChange?.(valueDerived);\n      }\n    }, [onChange, valueDerived]);\n\n    const stepUp = useCallback(() => {\n      handleClick(step);\n    }, [handleClick, step]);\n\n    const stepDown = useCallback(() => {\n      handleClick(-step);\n    }, [handleClick, step]);\n\n    const buttonVariant = variant === 'flat' ? 'flat' : 'raised';\n    return (\n      <StyledNumberInputWrapper\n        className={className}\n        style={{\n          ...style,\n          width: width !== undefined ? getSize(width) : 'auto'\n        }}\n        {...otherProps}\n      >\n        <TextInput\n          value={valueDerived}\n          variant={variant}\n          onChange={handleInputChange}\n          disabled={disabled}\n          type='number'\n          readOnly={readOnly}\n          ref={ref}\n          fullWidth\n          onBlur={onBlur}\n        />\n        <StyledButtonWrapper variant={variant}>\n          <StyledButton\n            data-testid='increment'\n            variant={buttonVariant}\n            disabled={disabled || readOnly}\n            onClick={stepUp}\n          >\n            <StyledButtonIcon invert />\n          </StyledButton>\n          <StyledButton\n            data-testid='decrement'\n            variant={buttonVariant}\n            disabled={disabled || readOnly}\n            onClick={stepDown}\n          >\n            <StyledButtonIcon />\n          </StyledButton>\n        </StyledButtonWrapper>\n      </StyledNumberInputWrapper>\n    );\n  }\n);\n\nNumberInput.displayName = 'NumberInput';\n\nexport { NumberInput, NumberInputProps };\n"
  },
  {
    "path": "src/ProgressBar/ProgressBar.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { ProgressBar } from './ProgressBar';\n\ndescribe('<ProgressBar />', () => {\n  it('renders ProgressBar', () => {\n    const value = 32;\n    const { getByRole } = renderWithTheme(<ProgressBar value={value} />);\n\n    const progressBar = getByRole('progressbar');\n\n    expect(progressBar).toBeInTheDocument();\n    expect(progressBar).toHaveAttribute('aria-valuenow', value.toString());\n  });\n\n  describe('prop: variant', () => {\n    describe('variant: \"default\"', () => {\n      it('displays current percentage value', () => {\n        const value = 32;\n        const { queryByTestId } = renderWithTheme(\n          <ProgressBar value={value} />\n        );\n\n        expect(queryByTestId('defaultProgress1')?.textContent).toBe(\n          `${value}%`\n        );\n        expect(queryByTestId('defaultProgress2')?.textContent).toBe(\n          `${value}%`\n        );\n\n        expect(queryByTestId('defaultProgress2')).toHaveStyleRule(\n          'clip-path',\n          `polygon( 0 0, ${value}% 0, ${value}% 100%, 0 100% )`\n        );\n\n        expect(queryByTestId('indeterminateProgress')).not.toBeInTheDocument();\n      });\n    });\n\n    describe('variant: \"tile\"', () => {\n      it('Renders \"tile\" progress', () => {\n        const { queryByTestId } = renderWithTheme(\n          <ProgressBar variant='tile' />\n        );\n        expect(queryByTestId('defaultProgress1')).not.toBeInTheDocument();\n        expect(queryByTestId('defaultProgress2')).not.toBeInTheDocument();\n        expect(queryByTestId('tileProgress')).toBeInTheDocument();\n      });\n\n      // it('Renders correct number of tiles', () => {\n      //   const value = 34;\n      //   const { queryByTestId } = renderWithTheme(\n      //     <Progress variant='tile' value={value} />\n      //   );\n      //   const tileProgress = queryByTestId('tileProgress');\n      //   const tileProgressWidth = tileProgress.getBoundingClientRect().width;\n      //   const tile = tileProgress.firstChild;\n      //   const tileWidth = tile.getBoundingClientRect().width;\n\n      //   const targetTileNumber = Math.floor(\n      //     ((value / 100) * tileProgressWidth) / tileWidth\n      //   );\n      //   expect(tileProgress.childElementCount).toBe(targetTileNumber);\n      // });\n    });\n  });\n\n  describe('prop: hideValue', () => {\n    it('renders progress bars, but does not show value', () => {\n      const value = 32;\n      const { queryByTestId } = renderWithTheme(\n        <ProgressBar hideValue value={value} />\n      );\n      expect(queryByTestId('defaultProgress1')).toBeInTheDocument();\n      expect(queryByTestId('defaultProgress2')).toBeInTheDocument();\n      expect(queryByTestId('defaultProgress1')).toBeEmptyDOMElement();\n      expect(queryByTestId('defaultProgress2')).toBeEmptyDOMElement();\n    });\n  });\n});\n"
  },
  {
    "path": "src/ProgressBar/ProgressBar.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React, { useEffect, useState } from 'react';\nimport { ProgressBar } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  background: ${({ theme }) => theme.material};\n  padding: 5rem;\n`;\n\nexport default {\n  title: 'Controls/ProgressBar',\n  component: ProgressBar,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof ProgressBar>;\n\nexport function Default() {\n  const [percent, setPercent] = useState(0);\n\n  useEffect(() => {\n    const timer = setInterval(() => {\n      setPercent(previousPercent => {\n        if (previousPercent === 100) {\n          return 0;\n        }\n        const diff = Math.random() * 10;\n        return Math.min(previousPercent + diff, 100);\n      });\n    }, 500);\n    return () => {\n      clearInterval(timer);\n    };\n  }, []);\n\n  return <ProgressBar value={Math.floor(percent)} />;\n}\n\nDefault.story = {\n  name: 'default'\n};\n\nexport function Tile() {\n  const [percent, setPercent] = useState(0);\n\n  useEffect(() => {\n    const timer = setInterval(() => {\n      setPercent(previousPercent => {\n        if (previousPercent === 100) {\n          return 0;\n        }\n        const diff = Math.random() * 10;\n        return Math.min(previousPercent + diff, 100);\n      });\n    }, 500);\n    return () => {\n      clearInterval(timer);\n    };\n  }, []);\n\n  return <ProgressBar variant='tile' value={Math.floor(percent)} />;\n}\n\nTile.story = {\n  name: 'tile'\n};\n\nexport function HideValue() {\n  const [percent, setPercent] = useState(0);\n\n  useEffect(() => {\n    const timer = setInterval(() => {\n      setPercent(previousPercent => {\n        if (previousPercent === 100) {\n          return 0;\n        }\n        const diff = Math.random() * 10;\n        return Math.min(previousPercent + diff, 100);\n      });\n    }, 500);\n    return () => {\n      clearInterval(timer);\n    };\n  }, []);\n\n  return <ProgressBar hideValue value={Math.floor(percent)} />;\n}\n\nHideValue.story = {\n  name: 'hide value'\n};\n"
  },
  {
    "path": "src/ProgressBar/ProgressBar.tsx",
    "content": "import React, {\n  forwardRef,\n  useCallback,\n  useEffect,\n  useRef,\n  useState\n} from 'react';\nimport styled, { css } from 'styled-components';\n\nimport { blockSizes } from '../common/system';\nimport { StyledScrollView } from '../ScrollView/ScrollView';\nimport { CommonStyledProps } from '../types';\n\ntype ProgressBarProps = {\n  hideValue?: boolean;\n  shadow?: boolean;\n  value?: number;\n  variant?: 'default' | 'tile';\n} & React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\nconst Wrapper = styled.div<Required<Pick<ProgressBarProps, 'variant'>>>`\n  display: inline-block;\n  height: ${blockSizes.md};\n  width: 100%;\n`;\n\nconst ProgressCutout = styled(StyledScrollView)<\n  Required<Pick<ProgressBarProps, 'variant'>>\n>`\n  width: 100%;\n  height: 100%;\n  position: relative;\n  text-align: center;\n  padding: 0;\n  overflow: hidden;\n  &:before {\n    z-index: 1;\n  }\n`;\nconst commonBarStyles = css`\n  width: calc(100% - 4px);\n  height: calc(100% - 4px);\n\n  display: flex;\n  align-items: center;\n  justify-content: space-around;\n`;\nconst WhiteBar = styled.div`\n  position: relative;\n  top: 4px;\n  ${commonBarStyles}\n  background: ${({ theme }) => theme.canvas};\n  color: #000;\n  margin-left: 2px;\n  margin-top: -2px;\n  color: ${({ theme }) => theme.materialText};\n`;\n\nconst BlueBar = styled.div<Pick<ProgressBarProps, 'value'>>`\n  position: absolute;\n  top: 2px;\n  left: 2px;\n  ${commonBarStyles}\n  color: ${({ theme }) => theme.materialTextInvert};\n  background: ${({ theme }) => theme.progress};\n  clip-path: polygon(\n    0 0,\n    ${({ value = 0 }) => value}% 0,\n    ${({ value = 0 }) => value}% 100%,\n    0 100%\n  );\n  transition: 0.4s linear clip-path;\n`;\n\nconst TilesWrapper = styled.div`\n  width: calc(100% - 6px);\n  height: calc(100% - 8px);\n  position: absolute;\n  left: 3px;\n  top: 4px;\n  box-sizing: border-box;\n  display: inline-flex;\n`;\nconst tileWidth = 17;\nconst Tile = styled.span`\n  display: inline-block;\n  width: ${tileWidth}px;\n  box-sizing: border-box;\n  height: 100%;\n  background: ${({ theme }) => theme.progress};\n  border-color: ${({ theme }) => theme.material};\n  border-width: 0px 1px;\n  border-style: solid;\n`;\n\nconst ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>(\n  (\n    {\n      hideValue = false,\n      shadow = true,\n      value,\n      variant = 'default',\n      ...otherProps\n    },\n    ref\n  ) => {\n    const displayValue = hideValue ? null : `${value}%`;\n\n    const tilesWrapperRef = useRef<HTMLDivElement | null>(null);\n    const [tiles, setTiles] = useState([]);\n\n    // TODO debounce this function\n    const updateTilesNumber = useCallback(() => {\n      if (!tilesWrapperRef.current || value === undefined) {\n        return;\n      }\n      const progressWidth =\n        tilesWrapperRef.current.getBoundingClientRect().width;\n      const newTilesNumber = Math.round(\n        ((value / 100) * progressWidth) / tileWidth\n      );\n      setTiles(Array.from({ length: newTilesNumber }));\n    }, [value]);\n\n    useEffect(() => {\n      updateTilesNumber();\n\n      window.addEventListener('resize', updateTilesNumber);\n      return () => window.removeEventListener('resize', updateTilesNumber);\n    }, [updateTilesNumber]);\n\n    return (\n      <Wrapper\n        aria-valuenow={value !== undefined ? Math.round(value) : undefined}\n        ref={ref}\n        role='progressbar'\n        variant={variant}\n        {...otherProps}\n      >\n        <ProgressCutout variant={variant} shadow={shadow}>\n          {variant === 'default' ? (\n            <>\n              <WhiteBar data-testid='defaultProgress1'>{displayValue}</WhiteBar>\n              <BlueBar data-testid='defaultProgress2' value={value}>\n                {displayValue}\n              </BlueBar>\n            </>\n          ) : (\n            <TilesWrapper ref={tilesWrapperRef} data-testid='tileProgress'>\n              {tiles.map((_, index) => (\n                <Tile key={index} />\n              ))}\n            </TilesWrapper>\n          )}\n        </ProgressCutout>\n      </Wrapper>\n    );\n  }\n);\n\nProgressBar.displayName = 'ProgressBar';\n\nexport { ProgressBar, ProgressBarProps };\n"
  },
  {
    "path": "src/Radio/Radio.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { Radio } from './Radio';\n\ndescribe('<Radio />', () => {\n  describe('label', () => {\n    it('renders', () => {\n      const labelText = 'Swag';\n      const { getByLabelText } = renderWithTheme(<Radio label={labelText} />);\n      expect(getByLabelText(labelText)).toBeInTheDocument();\n    });\n  });\n\n  describe('prop: onChange', () => {\n    it('should be called when Radio is clicked', () => {\n      const handleChange = jest.fn(event => event.target.checked);\n\n      const { getByRole } = renderWithTheme(\n        <Radio onChange={handleChange} value='swag' />\n      );\n\n      getByRole('radio').click();\n\n      expect(handleChange).toHaveBeenCalledTimes(1);\n    });\n  });\n\n  describe('prop: disabled', () => {\n    it('should disable radio', () => {\n      const handleChange = jest.fn();\n\n      const { getByRole } = renderWithTheme(<Radio disabled />);\n      const checkbox = getByRole('radio');\n      expect(checkbox).toHaveAttribute('disabled');\n\n      checkbox.click();\n      expect(handleChange).not.toHaveBeenCalled();\n    });\n    it('should be overridden by props', () => {\n      const { getByRole, rerender } = renderWithTheme(<Radio disabled />);\n      rerender(<Radio disabled={false} />);\n      const checkbox = getByRole('radio');\n      expect(checkbox).not.toHaveAttribute('disabled');\n    });\n  });\n\n  describe('controlled', () => {\n    it('should check the radio', () => {\n      const { getByRole, rerender } = renderWithTheme(\n        <Radio checked={false} readOnly />\n      );\n\n      rerender(<Radio checked readOnly />);\n      const checkbox = getByRole('radio') as HTMLInputElement;\n\n      expect(checkbox.checked).toBe(true);\n      expect(getByRole('radio')).toHaveAttribute('checked');\n      expect(getByRole('presentation').firstChild).toHaveAttribute(\n        'data-testid',\n        'checkmarkIcon'\n      );\n    });\n\n    it('should uncheck the checkbox', () => {\n      const { getByRole, rerender } = renderWithTheme(\n        <Radio checked readOnly />\n      );\n      rerender(<Radio checked={false} readOnly />);\n      const checkbox = getByRole('radio') as HTMLInputElement;\n\n      expect(checkbox.checked).toBe(false);\n      expect(getByRole('radio')).not.toHaveAttribute('checked');\n      expect(getByRole('presentation').firstChild).toBeNull();\n    });\n  });\n});\n"
  },
  {
    "path": "src/Radio/Radio.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport { GroupBox, Radio, ScrollView, Window, WindowContent } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n  #cutout {\n    background: ${({ theme }) => theme.canvas};\n    color: ${({ theme }) => theme.materialText};\n    padding: 1rem;\n    width: 300px;\n    & p {\n      margin-bottom: 2rem;\n    }\n  }\n`;\n\nexport default {\n  title: 'Controls/Radio',\n  component: Radio,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Radio>;\n\nexport function Default() {\n  const [state, setState] = useState('Pear');\n  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) =>\n    setState(e.target.value);\n\n  return (\n    <Window>\n      <WindowContent>\n        <GroupBox label='Fruits'>\n          <Radio\n            checked={state === 'Pear'}\n            onChange={handleChange}\n            value='Pear'\n            label='🍐 Pear'\n            name='fruits'\n          />\n          <br />\n          <Radio\n            checked={state === 'Orange'}\n            onChange={handleChange}\n            value='Orange'\n            label='🍊 Orange'\n            name='fruits'\n          />\n          <br />\n          <Radio\n            checked={state === 'Kiwi'}\n            onChange={handleChange}\n            value='Kiwi'\n            label='🥝 Kiwi'\n            name='fruits'\n          />\n          <br />\n          <Radio\n            checked={state === 'Grape'}\n            onChange={handleChange}\n            value='Grape'\n            label='🍇 Grape'\n            name='fruits'\n            disabled\n          />\n        </GroupBox>\n      </WindowContent>\n    </Window>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n\nexport function Flat() {\n  const [state, setState] = useState('Pear');\n  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) =>\n    setState(e.target.value);\n\n  return (\n    <Window>\n      <WindowContent>\n        <ScrollView id='cutout'>\n          <p>\n            When you want to use radio buttons on a light background (like\n            scrollable content), just use the flat variant:\n          </p>\n\n          <GroupBox variant='flat' label='Fruits'>\n            <Radio\n              variant='flat'\n              checked={state === 'Pear'}\n              onChange={handleChange}\n              value='Pear'\n              label='🍐 Pear'\n              name='fruits'\n            />\n            <br />\n            <Radio\n              variant='flat'\n              checked={state === 'Orange'}\n              onChange={handleChange}\n              value='Orange'\n              label='🍊 Orange'\n              name='fruits'\n            />\n            <br />\n            <Radio\n              variant='flat'\n              checked={state === 'Kiwi'}\n              onChange={handleChange}\n              value='Kiwi'\n              label='🥝 Kiwi'\n              name='fruits'\n            />\n            <br />\n            <Radio\n              variant='flat'\n              checked={state === 'Grape'}\n              onChange={handleChange}\n              value='Grape'\n              label='🍇 Grape'\n              name='fruits'\n              disabled\n            />\n          </GroupBox>\n        </ScrollView>\n      </WindowContent>\n    </Window>\n  );\n}\n\nFlat.story = {\n  name: 'flat'\n};\n"
  },
  {
    "path": "src/Radio/Radio.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled, { css, CSSProperties } from 'styled-components';\n\nimport { createFlatBoxStyles } from '../common';\nimport {\n  LabelText,\n  size,\n  StyledInput,\n  StyledLabel\n} from '../common/SwitchBase';\nimport { StyledScrollView } from '../ScrollView/ScrollView';\nimport { CommonStyledProps } from '../types';\n\ntype RadioVariant = 'default' | 'flat';\n\ntype RadioProps = {\n  checked?: boolean;\n  className?: string;\n  disabled?: boolean;\n  label?: string | number;\n  name?: string;\n  onChange?: React.ChangeEventHandler<HTMLInputElement>;\n  style?: CSSProperties;\n  value?: string | number | boolean;\n  variant?: RadioVariant;\n} & Omit<\n  React.InputHTMLAttributes<HTMLInputElement>,\n  'checked' | 'className' | 'disabled' | 'name' | 'onChange' | 'style' | 'value'\n> &\n  CommonStyledProps;\n\nconst sharedCheckboxStyles = css`\n  width: ${size}px;\n  height: ${size}px;\n  border-radius: 50%;\n  display: flex;\n  align-items: center;\n  justify-content: space-around;\n  margin-right: 0.5rem;\n`;\n\ntype StyledCheckboxProps = {\n  $disabled: boolean;\n};\n\nconst StyledCheckbox = styled(StyledScrollView)<StyledCheckboxProps>`\n  ${sharedCheckboxStyles}\n  background: ${({ $disabled, theme }) =>\n    $disabled ? theme.material : theme.canvas};\n\n  &:before {\n    content: '';\n    position: absolute;\n    left: 0px;\n    top: 0px;\n    width: calc(100% - 4px);\n    height: calc(100% - 4px);\n    border-radius: 50%;\n    box-shadow: none;\n  }\n`;\nconst StyledFlatCheckbox = styled.div<StyledCheckboxProps>`\n  ${createFlatBoxStyles()}\n  ${sharedCheckboxStyles}\n  outline: none;\n  background: ${({ $disabled, theme }) =>\n    $disabled ? theme.flatLight : theme.canvas};\n  &:before {\n    content: '';\n    display: inline-block;\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: calc(100% - 4px);\n    height: calc(100% - 4px);\n    border: 2px solid ${({ theme }) => theme.flatDark};\n    border-radius: 50%;\n  }\n`;\n\ntype IconProps = {\n  'data-testid': 'checkmarkIcon';\n  $disabled: boolean;\n  variant: RadioVariant;\n};\n\nconst Icon = styled.span.attrs(() => ({\n  'data-testid': 'checkmarkIcon'\n}))<IconProps>`\n  position: absolute;\n  content: '';\n  display: inline-block;\n  top: 50%;\n  left: 50%;\n  width: 6px;\n  height: 6px;\n  transform: translate(-50%, -50%);\n  border-radius: 50%;\n  background: ${p =>\n    p.$disabled ? p.theme.checkmarkDisabled : p.theme.checkmark};\n`;\n\nconst CheckboxComponents = {\n  flat: StyledFlatCheckbox,\n  default: StyledCheckbox\n};\n\nconst Radio = forwardRef<HTMLInputElement, RadioProps>(\n  (\n    {\n      checked,\n      className = '',\n      disabled = false,\n      label = '',\n      onChange,\n      style = {},\n      variant = 'default',\n      ...otherProps\n    },\n    ref\n  ) => {\n    const CheckboxComponent = CheckboxComponents[variant];\n\n    return (\n      <StyledLabel $disabled={disabled} className={className} style={style}>\n        <CheckboxComponent $disabled={disabled} role='presentation'>\n          {checked && <Icon $disabled={disabled} variant={variant} />}\n        </CheckboxComponent>\n        <StyledInput\n          disabled={disabled}\n          onChange={disabled ? undefined : onChange}\n          readOnly={disabled}\n          type='radio'\n          checked={checked}\n          ref={ref}\n          {...otherProps}\n        />\n        {label && <LabelText>{label}</LabelText>}\n      </StyledLabel>\n    );\n  }\n);\n\nRadio.displayName = 'Radio';\n\nexport { Radio, RadioProps };\n"
  },
  {
    "path": "src/ScrollView/ScrollView.spec.tsx",
    "content": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { ScrollView } from './ScrollView';\n\ndescribe('<ScrollView />', () => {\n  it('should render scrollview', () => {\n    const { container } = render(<ScrollView />);\n    const scrollView = container.firstElementChild;\n\n    expect(scrollView).toBeInTheDocument();\n  });\n\n  it('should render custom styles', () => {\n    const { container } = render(\n      <ScrollView style={{ backgroundColor: 'papayawhip' }} />\n    );\n    const scrollView = container.firstElementChild;\n\n    expect(scrollView).toHaveAttribute(\n      'style',\n      'background-color: papayawhip;'\n    );\n  });\n\n  it('should render children', async () => {\n    const { findByText } = render(\n      <ScrollView>\n        <span>Cool ScrollView</span>\n      </ScrollView>\n    );\n    const content = await findByText(/cool scrollview/i);\n\n    expect(content).toBeInTheDocument();\n  });\n\n  it('should render custom props', () => {\n    const customProps = { title: 'scrollview' };\n    const { container } = render(<ScrollView {...customProps} />);\n    const scrollView = container.firstElementChild;\n\n    expect(scrollView).toHaveAttribute('title', 'scrollview');\n  });\n});\n"
  },
  {
    "path": "src/ScrollView/ScrollView.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { ScrollView, Window, WindowContent } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n`;\n\nexport default {\n  title: 'Layout/ScrollView',\n  component: ScrollView,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof ScrollView>;\n\nexport function Default() {\n  return (\n    <Window>\n      <WindowContent>\n        <ScrollView style={{ width: '300px', height: '200px' }}>\n          <div>\n            <p style={{ width: 400 }}>\n              React95 is the best UI library ever created\n            </p>\n            <p>React95 is the best UI library ever created</p>\n            <p>React95 is the best UI library ever created</p>\n            <p>React95 is the best UI library ever created</p>\n            <p>React95 is the best UI library ever created</p>\n            <p>React95 is the best UI library ever created</p>\n            <p>React95 is the best UI library ever created</p>\n            <p>React95 is the best UI library ever created</p>\n            <p>React95 is the best UI library ever created</p>\n          </div>\n        </ScrollView>\n      </WindowContent>\n    </Window>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/ScrollView/ScrollView.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { insetShadow, createScrollbars } from '../common';\nimport { CommonStyledProps } from '../types';\n\ntype ScrollViewProps = {\n  children?: React.ReactNode;\n  shadow?: boolean;\n} & React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\nexport const StyledScrollView = styled.div<Pick<ScrollViewProps, 'shadow'>>`\n  position: relative;\n  box-sizing: border-box;\n  padding: 2px;\n  font-size: 1rem;\n  border-style: solid;\n  border-width: 2px;\n  border-left-color: ${({ theme }) => theme.borderDark};\n  border-top-color: ${({ theme }) => theme.borderDark};\n  border-right-color: ${({ theme }) => theme.borderLightest};\n  border-bottom-color: ${({ theme }) => theme.borderLightest};\n  line-height: 1.5;\n  &:before {\n    position: absolute;\n    left: 0;\n    top: 0;\n    content: '';\n    width: calc(100% - 4px);\n    height: calc(100% - 4px);\n\n    border-style: solid;\n    border-width: 2px;\n    border-left-color: ${({ theme }) => theme.borderDarkest};\n    border-top-color: ${({ theme }) => theme.borderDarkest};\n    border-right-color: ${({ theme }) => theme.borderLight};\n    border-bottom-color: ${({ theme }) => theme.borderLight};\n\n    pointer-events: none;\n    ${props => props.shadow && `box-shadow:${insetShadow};`}\n  }\n`;\n\nconst Content = styled.div`\n  box-sizing: border-box;\n  width: 100%;\n  height: 100%;\n  padding: 4px;\n  overflow: auto;\n  ${createScrollbars()}\n`;\n\nconst ScrollView = forwardRef<HTMLDivElement, ScrollViewProps>(\n  ({ children, shadow = true, ...otherProps }, ref) => {\n    return (\n      <StyledScrollView ref={ref} shadow={shadow} {...otherProps}>\n        <Content>{children}</Content>\n      </StyledScrollView>\n    );\n  }\n);\n\nScrollView.displayName = 'ScrollView';\n\nexport { ScrollView, ScrollViewProps };\n"
  },
  {
    "path": "src/Select/Select.spec.tsx",
    "content": "import { fireEvent, screen, waitFor } from '@testing-library/react';\nimport React from 'react';\nimport { renderWithTheme } from '../../test/utils';\nimport { noOp } from '../common/utils';\nimport { Select } from './Select';\nimport { SelectOption, SelectRef } from './Select.types';\n\nconst options: SelectOption<number>[] = [\n  { label: 'ten', value: 10 },\n  { label: 'twenty', value: 20 },\n  { label: 'thirty', value: 30 }\n];\n\ndescribe('<Select />', () => {\n  it('should be able to mount the component', () => {\n    const { container } = renderWithTheme(\n      <Select defaultValue={10} options={options} />\n    );\n\n    const input = container.querySelector('input') as HTMLInputElement;\n    expect(input.value).toBe('10');\n  });\n\n  it('renders dropdown button with icon', () => {\n    renderWithTheme(<Select defaultValue={10} options={options} />);\n\n    const button = screen.getByTestId('select-button');\n    expect(button).toBeInTheDocument();\n    // we render styled.button, but as='div'\n    // because it's used only for aesthetic purposes\n    expect(button.tagName).not.toBe('BUTTON');\n    expect(button.firstChild).toHaveAttribute('data-testid', 'select-icon');\n  });\n\n  it('the trigger is in tab order', () => {\n    renderWithTheme(<Select defaultValue={10} options={options} />);\n    expect(screen.getByRole('button')).toHaveProperty('tabIndex', 1);\n  });\n\n  it('should accept null child', () => {\n    renderWithTheme(<Select defaultValue={10} options={[...options, null]} />);\n  });\n\n  it('should have an input with [type=\"hidden\"] and string value by default', () => {\n    const { container } = renderWithTheme(\n      <Select defaultValue={10} options={options} />\n    );\n    const input = container.querySelector('input');\n    expect(input).toHaveAttribute('type', 'hidden');\n    expect(input).toHaveAttribute('value', '10');\n  });\n\n  it('passes through the blur event when menu is closed', () => {\n    const handleBlur = jest.fn();\n    renderWithTheme(\n      <Select\n        onBlur={handleBlur}\n        options={[\n          { label: 'ten', value: '10' },\n          { label: 'none', value: '' }\n        ]}\n      />\n    );\n\n    const trigger = screen.getByRole('button');\n    fireEvent.focus(trigger);\n    fireEvent.blur(trigger);\n    expect(handleBlur).toHaveBeenCalledTimes(1);\n  });\n\n  it('should ignore onBlur when the menu opens', () => {\n    // mousedown calls focus while click opens moving the focus to an item\n    // this means the trigger is blurred immediately\n    const handleBlur = jest.fn();\n    renderWithTheme(\n      <Select\n        onBlur={handleBlur}\n        defaultValue=''\n        onMouseDown={event => {\n          // simulating certain platforms that focus on mousedown\n          if (event.defaultPrevented === false) {\n            event.currentTarget.focus();\n          }\n        }}\n        options={[\n          { label: 'ten', value: '10' },\n          { label: 'none', value: '' }\n        ]}\n      />\n    );\n    const trigger = screen.getByRole('button');\n    fireEvent.mouseDown(trigger);\n    expect(handleBlur).toHaveBeenCalledTimes(0);\n    expect(screen.getByRole('listbox')).toBeInTheDocument();\n    const o = screen.getAllByRole('option');\n    fireEvent.mouseDown(o[0]);\n    o[0].click();\n    expect(handleBlur).toHaveBeenCalledTimes(0);\n    expect(screen.queryByRole('listbox', { hidden: false })).toBe(null);\n  });\n\n  it('options should have a data-value attribute', () => {\n    renderWithTheme(\n      <Select defaultValue={10} onOpen={noOp} open options={options} />\n    );\n    const o = screen.getAllByRole('option');\n    expect(o[0]).toHaveAttribute('data-value', '10');\n    expect(o[1]).toHaveAttribute('data-value', '20');\n  });\n\n  it('should call onClose when user clicks outside of component', async () => {\n    const handleClose = jest.fn();\n    renderWithTheme(\n      <div>\n        <Select onClose={handleClose} options={options} />\n        <div data-testid='el'>swag</div>\n      </div>\n    );\n    expect(handleClose).toHaveBeenCalledTimes(0);\n    fireEvent.mouseDown(screen.getByRole('button'));\n    fireEvent.mouseDown(screen.getByText('swag'));\n    expect(handleClose).toHaveBeenCalledTimes(1);\n  });\n\n  it('should open and close on mouseDown', async () => {\n    renderWithTheme(<Select options={options} />);\n\n    fireEvent.mouseDown(screen.getByRole('button'));\n    expect(screen.getByRole('listbox')).toBeInTheDocument();\n\n    fireEvent.mouseDown(screen.getByRole('button'));\n    expect(screen.queryByRole('listbox')).not.toBeInTheDocument();\n  });\n\n  describe('prop: inputProps', () => {\n    it('should apply additional props to trigger element', () => {\n      renderWithTheme(\n        <Select\n          defaultValue={10}\n          inputProps={{ 'data-testid': 'SelectInput' }}\n          options={options}\n        />\n      );\n      expect(screen.getByTestId('SelectInput')).toHaveProperty(\n        'tagName',\n        'INPUT'\n      );\n    });\n  });\n\n  describe('prop: menuMaxHeight', () => {\n    it('sets max-height to dropdown', () => {\n      renderWithTheme(\n        <Select\n          defaultValue={10}\n          onOpen={noOp}\n          open\n          options={options}\n          menuMaxHeight={220}\n        />\n      );\n\n      const listbox = screen.getByRole('listbox') as HTMLElement;\n      expect(\n        listbox.getAttribute('style')?.includes('max-height: 220px')\n      ).toBeTruthy();\n    });\n  });\n\n  describe('prop: onClose, onFocus, onKeyDown, onOpen', () => {\n    it('passes event through', () => {\n      const handler = jest.fn();\n\n      renderWithTheme(\n        <>\n          <Select\n            defaultValue={10}\n            onClose={handler}\n            onFocus={handler}\n            onKeyDown={handler}\n            onOpen={handler}\n            options={options}\n          />\n          <div>outside</div>\n        </>\n      );\n\n      const button = screen.getByRole('button');\n\n      fireEvent.focus(button);\n      expect(handler).toHaveBeenCalledWith(\n        expect.objectContaining({ type: 'focus' })\n      );\n      handler.mockClear();\n\n      fireEvent.keyDown(button);\n      expect(handler).toHaveBeenCalledWith(\n        expect.objectContaining({ type: 'keydown' })\n      );\n      handler.mockClear();\n\n      fireEvent.mouseDown(button);\n      expect(handler).toHaveBeenCalledWith({\n        fromEvent: expect.objectContaining({ type: 'mousedown' })\n      });\n      handler.mockClear();\n\n      fireEvent.mouseDown(screen.getByText('outside'));\n      expect(handler).toHaveBeenCalledWith({\n        fromEvent: expect.objectContaining({ type: 'mousedown' })\n      });\n      handler.mockClear();\n    });\n  });\n\n  describe('prop: onChange', () => {\n    it('should get selected option from arguments', () => {\n      const onChange = jest.fn();\n      renderWithTheme(\n        <Select onChange={onChange} defaultValue={0} options={options} />\n      );\n      fireEvent.mouseDown(screen.getByRole('button'));\n\n      const option = screen.getAllByRole('option')[1];\n      fireEvent.mouseEnter(option);\n      fireEvent.click(option);\n\n      expect(onChange).toHaveBeenCalledTimes(1);\n      expect(onChange).toHaveBeenCalledWith(options[1], {\n        fromEvent: expect.anything()\n      });\n    });\n  });\n\n  describe('prop: readOnly', () => {\n    it('should not trigger any event with readOnly', () => {\n      renderWithTheme(<Select readOnly defaultValue={10} options={options} />);\n      screen.getByRole('button').focus();\n      const focusedButton = document.activeElement as HTMLElement;\n      fireEvent.keyDown(focusedButton, { key: 'ArrowDown' });\n      expect(screen.queryByRole('listbox')).not.toBeInTheDocument();\n      fireEvent.keyUp(focusedButton, { key: 'ArrowDown' });\n      expect(screen.queryByRole('listbox')).not.toBeInTheDocument();\n    });\n  });\n\n  describe('prop: value', () => {\n    it('should select the option based on the value', () => {\n      renderWithTheme(\n        <Select defaultValue={20} options={options} open onOpen={noOp} />\n      );\n      const o = screen.getAllByRole('option');\n      expect(o[0]).not.toHaveAttribute('aria-selected');\n      expect(o[1]).toHaveAttribute('aria-selected', 'true');\n      expect(o[2]).not.toHaveAttribute('aria-selected');\n    });\n\n    it('should select only the option that matches the object', () => {\n      const obj1 = { id: 1 };\n      const obj2 = { id: 2 };\n      renderWithTheme(\n        <Select\n          open\n          onOpen={noOp}\n          defaultValue={obj1}\n          options={[\n            { label: '1', value: obj1 },\n            { label: '2', value: obj2 }\n          ]}\n        />\n      );\n      const o = screen.getAllByRole('option');\n      expect(o[0]).toHaveAttribute('aria-selected', 'true');\n      expect(o[1]).not.toHaveAttribute('aria-selected');\n    });\n\n    it('should be able to use an object', () => {\n      const value = {};\n      renderWithTheme(\n        <Select\n          open\n          onOpen={noOp}\n          defaultValue={value}\n          options={[...options, { value, label: 'object-label' }]}\n        />\n      );\n      expect(screen.getByRole('button')).toHaveTextContent('object-label');\n    });\n  });\n\n  describe('prop: open (controlled)', () => {\n    // TODO add more tests\n    it('should be open when initially true', () => {\n      renderWithTheme(<Select open onOpen={noOp} options={options} />);\n      expect(screen.getByRole('listbox')).toBeInTheDocument();\n    });\n\n    it('open only with the left mouse button click', () => {\n      // Right/middle mouse click shouldn't open the Select\n      renderWithTheme(<Select defaultValue={10} options={options} />);\n      const trigger = screen.getByRole('button');\n      // If clicked by the right/middle mouse button, no options list should be opened\n      fireEvent.mouseDown(trigger, { button: 1 });\n      expect(screen.queryByRole('listbox')).not.toBeInTheDocument();\n      fireEvent.mouseDown(trigger, { button: 2 });\n      expect(screen.queryByRole('listbox')).not.toBeInTheDocument();\n    });\n  });\n\n  describe('prop: formatDisplay', () => {\n    it('should use the prop to render the value', () => {\n      const formatDisplay = (x: SelectOption<number>) =>\n        `0b${Number(x.value).toString(2)}`;\n      renderWithTheme(\n        <Select\n          formatDisplay={formatDisplay}\n          options={[{ value: 2, label: '2' }]}\n        />\n      );\n      expect(screen.getByRole('button')).toHaveTextContent('0b10');\n    });\n  });\n\n  describe('prop: ref', () => {\n    it('should be able to return the input node via a ref object', () => {\n      const ref = React.createRef<SelectRef>();\n      renderWithTheme(<Select ref={ref} defaultValue='' />);\n      expect(ref.current?.node).toHaveProperty('tagName', 'INPUT');\n    });\n\n    it('should be able focus the trigger imperatively', () => {\n      const ref = React.createRef<SelectRef>();\n      renderWithTheme(<Select ref={ref} defaultValue='' />);\n      ref.current?.focus();\n      expect(screen.getByRole('button')).toHaveFocus();\n    });\n  });\n\n  describe('spread props', () => {\n    it('should apply additional props to trigger element', () => {\n      renderWithTheme(\n        <Select data-test='SelectDisplay' defaultValue={10} options={options} />\n      );\n      expect(screen.getByRole('button')).toHaveAttribute(\n        'data-test',\n        'SelectDisplay'\n      );\n    });\n  });\n\n  describe('keyboard', () => {\n    it.each(['Space', 'ArrowUp', 'ArrowDown', 'Home', 'End'])(\n      `should open menu when pressed %s key on select`,\n      code => {\n        renderWithTheme(\n          <Select defaultValue='' options={[{ label: 'none', value: '' }]} />\n        );\n        screen.getByRole('button').focus();\n        const focusedButton = document.activeElement as HTMLButtonElement;\n        fireEvent.keyDown(focusedButton, { code });\n        expect(\n          screen.getByRole('listbox', { hidden: false })\n        ).toBeInTheDocument();\n        fireEvent.keyUp(focusedButton, { code });\n        expect(\n          screen.getByRole('listbox', { hidden: false })\n        ).toBeInTheDocument();\n      }\n    );\n\n    it('closes menu when pressing Escape', async () => {\n      const onClose = jest.fn();\n      renderWithTheme(\n        <Select defaultValue={10} options={options} onClose={onClose} />\n      );\n      const button = screen.getByRole('button');\n      fireEvent.mouseDown(button);\n\n      const listbox = screen.getByRole('listbox');\n\n      expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();\n\n      fireEvent.keyDown(listbox, { code: 'ArrowDown' });\n      expect(screen.getByRole('option', { name: 'twenty' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'Escape' });\n\n      await waitFor(() => {\n        expect(onClose).toHaveBeenCalled();\n      });\n      expect(listbox).not.toBeInTheDocument();\n\n      expect(button).toHaveFocus();\n    });\n\n    it.each(['Enter', 'Space', 'Tab'])(\n      'selects the active option by pressing %s, closes menu and maintains focus',\n      async keyCode => {\n        const onClose = jest.fn();\n        const onKeyDown = jest.fn();\n        renderWithTheme(\n          // eslint-disable-next-line jsx-a11y/no-static-element-interactions\n          <div onKeyDown={onKeyDown}>\n            <Select defaultValue={10} options={options} onClose={onClose} />\n          </div>\n        );\n        const button = screen.getByRole('button');\n        fireEvent.mouseDown(button);\n\n        const listbox = screen.getByRole('listbox');\n\n        expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();\n\n        fireEvent.keyDown(listbox, { code: 'ArrowDown' });\n        expect(screen.getByRole('option', { name: 'twenty' })).toHaveFocus();\n\n        fireEvent.keyDown(listbox, { code: keyCode });\n\n        await waitFor(() => {\n          expect(onClose).toHaveBeenCalled();\n        });\n        expect(listbox).not.toBeInTheDocument();\n\n        expect(button).toHaveFocus();\n        expect(onKeyDown).toHaveBeenCalledWith(\n          expect.objectContaining({ defaultPrevented: true })\n        );\n      }\n    );\n\n    it('passes through Enter, Escape, Tab and Shift + Tab when closed', () => {\n      const onKeyDown = jest.fn();\n      renderWithTheme(\n        // eslint-disable-next-line jsx-a11y/no-static-element-interactions\n        <div onKeyDown={onKeyDown}>\n          <Select defaultValue={10} options={options} />\n        </div>\n      );\n\n      const button = screen.getByRole('button');\n\n      const eventOptions = [\n        { code: 'Enter' },\n        { code: 'Escape' },\n        { code: 'Tab' },\n        { code: 'Tab', shiftKey: true }\n      ];\n      eventOptions.forEach(eventOption => {\n        fireEvent.keyDown(button, eventOption);\n        expect(onKeyDown).toHaveBeenCalledWith(\n          expect.objectContaining({ defaultPrevented: false })\n        );\n      });\n    });\n\n    it('passes through keyDown events when modifier keys are pressed', () => {\n      const onKeyDown = jest.fn();\n      renderWithTheme(\n        // eslint-disable-next-line jsx-a11y/no-static-element-interactions\n        <div onKeyDown={onKeyDown}>\n          <Select defaultValue={10} options={options} />\n        </div>\n      );\n\n      const button = screen.getByRole('button');\n      button.focus();\n\n      const eventOptions = [\n        { altKey: true },\n        { ctrlKey: true },\n        { metaKey: true },\n        { shiftKey: true }\n      ];\n      eventOptions.forEach(eventOption => {\n        fireEvent.keyDown(button, { ...eventOption, code: 'KeyT' });\n        expect(button).toHaveTextContent('ten');\n        expect(onKeyDown).toHaveBeenCalledWith(\n          expect.objectContaining({ defaultPrevented: false })\n        );\n      });\n    });\n\n    it('moves options using ArrowUp, ArrowDown, Home and End', async () => {\n      renderWithTheme(<Select defaultValue={10} options={options} />);\n      const button = screen.getByRole('button');\n      fireEvent.mouseDown(button);\n\n      const listbox = screen.getByRole('listbox');\n\n      expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();\n\n      fireEvent.keyDown(listbox, { code: 'ArrowDown' });\n      expect(screen.getByRole('option', { name: 'twenty' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'ArrowUp' });\n      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'End' });\n      expect(screen.getByRole('option', { name: 'thirty' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'Home' });\n      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();\n    });\n\n    it('cycles through options when pressing the same key (open menu)', async () => {\n      renderWithTheme(<Select defaultValue={10} options={options} />);\n      const button = screen.getByRole('button');\n      fireEvent.mouseDown(button);\n\n      const listbox = screen.getByRole('listbox');\n\n      expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      expect(screen.getByRole('option', { name: 'twenty' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      expect(screen.getByRole('option', { name: 'thirty' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();\n    });\n\n    it('cycles through options when pressing the same key (closed menu)', async () => {\n      renderWithTheme(<Select defaultValue={10} options={options} />);\n      const button = screen.getByRole('button');\n      fireEvent.focus(button);\n\n      fireEvent.keyDown(button, { code: 'KeyT' });\n      fireEvent.keyDown(button, { code: 'KeyT' });\n      expect(button).toHaveTextContent('twenty');\n\n      fireEvent.keyDown(button, { code: 'KeyT' });\n      expect(button).toHaveTextContent('thirty');\n\n      fireEvent.keyDown(button, { code: 'KeyT' });\n      expect(button).toHaveTextContent('ten');\n    });\n\n    it('switches to search after cycling', async () => {\n      renderWithTheme(<Select defaultValue={10} options={options} />);\n      const button = screen.getByRole('button');\n      fireEvent.mouseDown(button);\n\n      const listbox = screen.getByRole('listbox');\n\n      expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      expect(screen.getByRole('option', { name: 'twenty' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'KeyE' });\n      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();\n    });\n\n    it('switches to cycling after search', async () => {\n      renderWithTheme(<Select defaultValue={10} options={options} />);\n      const button = screen.getByRole('button');\n      fireEvent.mouseDown(button);\n\n      const listbox = screen.getByRole('listbox');\n\n      expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'KeyH' });\n      expect(screen.getByRole('option', { name: 'thirty' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      expect(screen.getByRole('option', { name: 'twenty' })).toHaveFocus();\n    });\n\n    it('moves to specific option when typing', async () => {\n      renderWithTheme(<Select defaultValue={10} options={options} />);\n      const button = screen.getByRole('button');\n      fireEvent.mouseDown(button);\n\n      const listbox = screen.getByRole('listbox');\n\n      expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();\n\n      fireEvent.keyDown(listbox, { code: 'KeyH' });\n      fireEvent.keyDown(listbox, { code: 'KeyI' });\n      fireEvent.keyDown(listbox, { code: 'KeyR' });\n      expect(screen.getByRole('option', { name: 'thirty' })).toHaveFocus();\n    });\n\n    it('resets typing after timeout', async () => {\n      jest.useFakeTimers();\n      renderWithTheme(<Select defaultValue={10} options={options} />);\n      const button = screen.getByRole('button');\n      fireEvent.mouseDown(button);\n\n      const listbox = screen.getByRole('listbox');\n\n      expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      fireEvent.keyDown(listbox, { code: 'KeyH' });\n      fireEvent.keyDown(listbox, { code: 'KeyI' });\n      fireEvent.keyDown(listbox, { code: 'KeyR' });\n      expect(screen.getByRole('option', { name: 'thirty' })).toHaveFocus();\n      jest.runAllTimers();\n\n      fireEvent.keyDown(listbox, { code: 'KeyT' });\n      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();\n\n      jest.useRealTimers();\n    });\n  });\n\n  describe('accessibility', () => {\n    it('sets aria-expanded=\"true\" when the listbox is displayed', () => {\n      // since we make the rest of the UI inaccessible when open this doesn't\n      // technically matter. This is only here in case we keep the rest accessible\n      renderWithTheme(<Select open onOpen={noOp} defaultValue='' />);\n      expect(screen.getByRole('button', { hidden: true })).toHaveAttribute(\n        'aria-expanded',\n        'true'\n      );\n    });\n\n    it(\"aria-expanded is false if the listbox isn't displayed\", () => {\n      renderWithTheme(<Select defaultValue='' />);\n      expect(screen.getByRole('button')).toHaveAttribute(\n        'aria-expanded',\n        'false'\n      );\n    });\n\n    it('indicates that activating the button displays a listbox', () => {\n      renderWithTheme(<Select defaultValue='' />);\n      expect(screen.getByRole('button')).toHaveAttribute(\n        'aria-haspopup',\n        'listbox'\n      );\n    });\n\n    it('renders an element with listbox behavior', () => {\n      renderWithTheme(<Select open onOpen={noOp} defaultValue='' />);\n      expect(screen.getByRole('listbox')).toBeVisible();\n    });\n\n    it('the listbox is focusable', () => {\n      renderWithTheme(<Select open onOpen={noOp} options={options} />);\n      const listbox = screen.getByRole('listbox');\n      listbox.focus();\n      expect(listbox).toHaveFocus();\n    });\n\n    it('identifies each selectable element containing an option', () => {\n      renderWithTheme(<Select open onOpen={noOp} options={options} />);\n      const o = screen.getAllByRole('option');\n      expect(o[0]).toHaveTextContent('ten');\n      expect(o[1]).toHaveTextContent('twenty');\n    });\n\n    it('indicates the selected option', () => {\n      renderWithTheme(\n        <Select open onOpen={noOp} defaultValue={20} options={options} />\n      );\n      expect(screen.getAllByRole('option')[1]).toHaveAttribute(\n        'aria-selected',\n        'true'\n      );\n    });\n\n    it('it will fallback to its content for the accessible name when it has no name', () => {\n      renderWithTheme(<Select defaultValue='' />);\n      expect(screen.getByRole('button')).not.toHaveAttribute('aria-labelledby');\n    });\n\n    it('is labelled by itself when it has an id which is preferred over name', () => {\n      renderWithTheme(\n        <>\n          <span id='select-1-label'>Chose first option:</span>\n          <Select\n            aria-labelledby='select-1-label'\n            id='select-1'\n            name='select'\n            defaultValue=''\n          />\n          <span id='select-2-label'>Chose second option:</span>\n          <Select\n            aria-labelledby='select-2-label'\n            id='select-2'\n            name='select'\n            defaultValue=''\n          />\n        </>\n      );\n      const triggers = screen.getAllByRole('button');\n      expect(triggers[0]).toHaveAttribute('aria-labelledby', 'select-1-label');\n      expect(triggers[1]).toHaveAttribute('aria-labelledby', 'select-2-label');\n    });\n  });\n});\n"
  },
  {
    "path": "src/Select/Select.stories.data.ts",
    "content": "export const PokemonOptions = [\n  'Bulbasaur',\n  'Ivysaur',\n  'Venusaur',\n  'Charmander',\n  'Charmeleon',\n  'Charizard',\n  'Squirtle',\n  'Wartortle',\n  'Blastoise',\n  'Caterpie',\n  'Metapod',\n  'Butterfree',\n  'Weedle',\n  'Kakuna',\n  'Beedrill',\n  'Pidgey',\n  'Pidgeotto',\n  'Pidgeot',\n  'Rattata',\n  'Raticate',\n  'Spearow',\n  'Fearow',\n  'Ekans',\n  'Arbok',\n  'Pikachu',\n  'Raichu',\n  'Sandshrew',\n  'Sandslash',\n  'Nidoran♀',\n  'Nidorina',\n  'Nidoqueen',\n  'Nidoran♂',\n  'Nidorino',\n  'Nidoking',\n  'Clefairy',\n  'Clefable',\n  'Vulpix',\n  'Ninetales',\n  'Jigglypuff',\n  'Wigglytuff',\n  'Zubat',\n  'Golbat',\n  'Oddish',\n  'Gloom',\n  'Vileplume',\n  'Paras',\n  'Parasect',\n  'Venonat',\n  'Venomoth',\n  'Diglett',\n  'Dugtrio',\n  'Meowth',\n  'Persian',\n  'Psyduck',\n  'Golduck',\n  'Mankey',\n  'Primeape',\n  'Growlithe',\n  'Arcanine',\n  'Poliwag',\n  'Poliwhirl',\n  'Poliwrath',\n  'Abra',\n  'Kadabra',\n  'Alakazam',\n  'Machop',\n  'Machoke',\n  'Machamp',\n  'Bellsprout',\n  'Weepinbell',\n  'Victreebel',\n  'Tentacool',\n  'Tentacruel',\n  'Geodude',\n  'Graveler',\n  'Golem',\n  'Ponyta',\n  'Rapidash',\n  'Slowpoke',\n  'Slowbro',\n  'Magnemite',\n  'Magneton',\n  'Farfetch’d',\n  'Doduo',\n  'Dodrio',\n  'Seel',\n  'Dewgong',\n  'Grimer',\n  'Muk',\n  'Shellder',\n  'Cloyster',\n  'Gastly',\n  'Haunter',\n  'Gengar',\n  'Onix',\n  'Drowzee',\n  'Hypno',\n  'Krabby',\n  'Kingler',\n  'Voltorb',\n  'Electrode',\n  'Exeggcute',\n  'Exeggutor',\n  'Cubone',\n  'Marowak',\n  'Hitmonlee',\n  'Hitmonchan',\n  'Lickitung',\n  'Koffing',\n  'Weezing',\n  'Rhyhorn',\n  'Rhydon',\n  'Chansey',\n  'Tangela',\n  'Kangaskhan',\n  'Horsea',\n  'Seadra',\n  'Goldeen',\n  'Seaking',\n  'Staryu',\n  'Starmie',\n  'Mr. Mime',\n  'Scyther',\n  'Jynx',\n  'Electabuzz',\n  'Magmar',\n  'Pinsir',\n  'Tauros',\n  'Magikarp',\n  'Gyarados',\n  'Lapras',\n  'Ditto',\n  'Eevee',\n  'Vaporeon',\n  'Jolteon',\n  'Flareon',\n  'Porygon',\n  'Omanyte',\n  'Omastar',\n  'Kabuto',\n  'Kabutops',\n  'Aerodactyl',\n  'Snorlax',\n  'Articuno',\n  'Zapdos',\n  'Moltres',\n  'Dratini',\n  'Dragonair',\n  'Dragonite',\n  'Mewtwo',\n  'Mew'\n].map((label, index) => ({ value: index + 1, label }));\n"
  },
  {
    "path": "src/Select/Select.stories.tsx",
    "content": "/* eslint-disable no-console */\n\nimport { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport {\n  GroupBox,\n  ScrollView,\n  Select,\n  SelectNative,\n  Window,\n  WindowContent\n} from 'react95';\nimport styled from 'styled-components';\nimport { PokemonOptions } from './Select.stories.data';\nimport { SelectOption } from './Select.types';\n\nconst options = PokemonOptions;\n\nconst nativeOptions = options.map(option => ({\n  ...option,\n  value: String(option.value)\n}));\n\nconst Wrapper = styled.div`\n  background: ${({ theme }) => theme.material};\n  padding: 5rem;\n  fieldset,\n  fieldset {\n    margin-bottom: 2rem;\n  }\n  legend + * {\n    margin-bottom: 1rem;\n  }\n  #default-selects {\n    width: 200px;\n  }\n  #cutout > div {\n    width: 250px;\n    padding: 1rem;\n    background: ${({ theme }) => theme.canvas};\n    & > p {\n      margin-bottom: 2rem;\n    }\n  }\n`;\n\nconst onChange = <T,>(\n  selectedOption: SelectOption<T>,\n  changeOptions: { fromEvent: React.SyntheticEvent | Event }\n) => console.log(selectedOption, changeOptions.fromEvent);\n\nexport default {\n  title: 'Controls/Select',\n  component: Select,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Select>;\n\nexport function Default() {\n  return (\n    <div id='default-selects'>\n      <GroupBox label='default'>\n        <Select\n          defaultValue={2}\n          options={options}\n          menuMaxHeight={160}\n          width={160}\n          onChange={onChange}\n          onOpen={e => console.log('open', e)}\n          onClose={e => console.log('close', e)}\n          onBlur={e => console.log('blur', e)}\n          onFocus={e => console.log('focus', e)}\n        />\n        <Select\n          disabled\n          onChange={onChange}\n          defaultValue={2}\n          options={options}\n          width={160}\n          menuMaxHeight={160}\n        />\n      </GroupBox>\n      <GroupBox label='default native'>\n        <SelectNative\n          onChange={onChange}\n          defaultValue='2'\n          options={nativeOptions}\n          width={160}\n          onBlur={() => console.log('native blur')}\n          onFocus={() => console.log('native focus')}\n        />\n        <SelectNative\n          disabled\n          onChange={onChange}\n          width={160}\n          defaultValue='2'\n          options={nativeOptions}\n        />\n      </GroupBox>\n    </div>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n\nexport function Flat() {\n  return (\n    <Window>\n      <WindowContent>\n        <ScrollView id='cutout'>\n          <p>\n            When you want to use Select on a light background (like scrollable\n            content), just use the flat variant:\n          </p>\n          <GroupBox label='flat' variant='flat'>\n            <Select\n              variant='flat'\n              onChange={onChange}\n              options={options}\n              width='100%'\n              menuMaxHeight={160}\n            />\n            <Select\n              variant='flat'\n              disabled\n              onChange={onChange}\n              options={options}\n              width='100%'\n            />\n          </GroupBox>\n          <GroupBox label='flat native' variant='flat'>\n            <SelectNative\n              variant='flat'\n              onChange={onChange}\n              options={nativeOptions}\n              width='100%'\n            />\n            <SelectNative\n              variant='flat'\n              disabled\n              onChange={onChange}\n              width='100%'\n              options={nativeOptions}\n            />\n          </GroupBox>\n        </ScrollView>\n      </WindowContent>\n    </Window>\n  );\n}\n\nFlat.story = {\n  name: 'flat'\n};\n\nexport function CustomDisplayFormatting() {\n  return (\n    <Select\n      formatDisplay={opt => `${opt.label?.toUpperCase()} 👍 👍`}\n      onChange={onChange}\n      options={options}\n      width={220}\n    />\n  );\n}\n\nCustomDisplayFormatting.story = {\n  name: 'custom display formatting'\n};\n"
  },
  {
    "path": "src/Select/Select.styles.tsx",
    "content": "import styled, { css } from 'styled-components';\n\nimport { StyledButton as Button } from '../Button/Button';\nimport {\n  createDisabledTextStyles,\n  createFlatBoxStyles,\n  createScrollbars,\n  shadow as commonShadow\n} from '../common';\nimport { blockSizes } from '../common/system';\nimport { StyledScrollView } from '../ScrollView/ScrollView';\nimport { CommonThemeProps } from '../types';\n\nimport { SelectVariants } from './Select.types';\n\ntype CommonSelectStyleProps = {\n  $disabled?: boolean;\n  native?: boolean;\n  variant?: SelectVariants;\n} & CommonThemeProps;\n\nconst sharedInputContentStyles = css`\n  box-sizing: border-box;\n  padding-left: 4px;\n  overflow: hidden;\n  white-space: nowrap;\n  user-select: none;\n  line-height: 100%;\n`;\n\nconst sharedHoverStyles = css`\n  background: ${({ theme }) => theme.hoverBackground};\n  color: ${({ theme }) => theme.canvasTextInvert};\n`;\nexport const StyledInner = styled.div`\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  height: 100%;\n  width: 100%;\n  &:focus {\n    outline: none;\n  }\n`;\n\nexport const StyledSelectContent = styled.div`\n  ${sharedInputContentStyles}\n  padding-right: 8px;\n  align-items: center;\n  display: flex;\n  height: calc(100% - 4px);\n  width: calc(100% - 4px);\n  margin: 0 2px;\n  border: 2px solid transparent;\n  ${StyledInner}:focus & {\n    ${sharedHoverStyles}\n    border: 2px dotted ${({ theme }) => theme.focusSecondary};\n  }\n`;\nconst sharedWrapperStyles = css<CommonSelectStyleProps>`\n  height: ${blockSizes.md};\n  display: inline-block;\n  color: ${({ $disabled = false, theme }) =>\n    $disabled ? createDisabledTextStyles() : theme.canvasText};\n  font-size: 1rem;\n  cursor: ${({ $disabled }) => ($disabled ? 'default' : 'pointer')};\n`;\n\nexport const StyledSelectWrapper = styled(\n  StyledScrollView\n)<CommonSelectStyleProps>`\n  ${sharedWrapperStyles}\n  background: ${({ $disabled = false, theme }) =>\n    $disabled ? theme.material : theme.canvas};\n  &:focus {\n    outline: 0;\n  }\n`;\n\nexport const StyledFlatSelectWrapper = styled.div<CommonSelectStyleProps>`\n  ${createFlatBoxStyles()}\n  ${sharedWrapperStyles}\n  background: ${({ $disabled = false, theme }) =>\n    $disabled ? theme.flatLight : theme.canvas};\n`;\n\nexport const StyledNativeSelect = styled.select`\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  display: block;\n  width: 100%;\n  height: 100%;\n  color: inherit;\n  font-size: 1rem;\n  border: 0;\n  margin: 0;\n  background: none;\n  -webkit-tap-highlight-color: transparent;\n  border-radius: 0;\n  padding-right: 30px;\n  ${sharedInputContentStyles}\n  cursor: pointer;\n  &:disabled {\n    ${createDisabledTextStyles()};\n    background: ${({ theme }) => theme.material};\n    cursor: default;\n  }\n`;\n\nexport const StyledDropdownButton = styled(Button).attrs(() => ({\n  'aria-hidden': 'true'\n}))<Omit<CommonSelectStyleProps, 'variant'>>`\n  width: 30px;\n  padding: 0;\n  flex-shrink: 0;\n  ${({ variant = 'default' }) =>\n    variant === 'flat'\n      ? css`\n          height: 100%;\n          margin-right: 0;\n        `\n      : css`\n          height: 100%;\n        `}\n  ${({ native = false, variant = 'default' }) =>\n    native &&\n    (variant === 'flat'\n      ? `\n      position: absolute;\n      right: 0;\n      height: 100%;\n      `\n      : `\n    position: absolute;\n    top: 2px;\n    right: 2px;\n    height: calc(100% - 4px);\n    `)}\n    pointer-events: ${({ $disabled = false, native = false }) =>\n    $disabled || native ? 'none' : 'auto'}\n`;\n\nexport const StyledDropdownIcon = styled.span<CommonSelectStyleProps>`\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%);\n  width: 0;\n  height: 0;\n  border-left: 6px solid transparent;\n  border-right: 6px solid transparent;\n  display: inline-block;\n  border-top: 6px solid\n    ${({ $disabled = false, theme }) =>\n      $disabled ? theme.materialTextDisabled : theme.materialText};\n  ${({ $disabled = false, theme }) =>\n    $disabled &&\n    `\n    filter: drop-shadow(1px 1px 0px ${theme.materialTextDisabledShadow});\n    border-top-color: ${theme.materialTextDisabled};\n    `}\n  ${StyledDropdownButton}:active & {\n    margin-top: 2px;\n  }\n`;\n\nexport const StyledDropdownMenu = styled.ul<CommonSelectStyleProps>`\n  box-sizing: border-box;\n\n  font-size: 1rem;\n  position: absolute;\n  transform: translateY(100%);\n  left: 0;\n  background: ${({ theme }) => theme.canvas};\n  padding: 2px;\n  border-top: none;\n  cursor: default;\n  z-index: 1;\n  cursor: pointer;\n  box-shadow: ${commonShadow};\n  ${({ variant = 'default' }) =>\n    variant === 'flat'\n      ? css`\n          bottom: 2px;\n          width: 100%;\n          border: 2px solid ${({ theme }) => theme.flatDark};\n        `\n      : css`\n          bottom: -2px;\n          width: calc(100% - 2px);\n          border: 2px solid ${({ theme }) => theme.borderDarkest};\n        `}\n  ${({ variant = 'default' }) => createScrollbars(variant)}\n`;\n\nexport const StyledDropdownMenuItem = styled.li<{ active: boolean }>`\n  box-sizing: border-box;\n\n  width: 100%;\n  padding-left: 8px;\n\n  height: calc(${blockSizes.md} - 4px);\n  line-height: calc(${blockSizes.md} - 4px);\n  font-size: 1rem;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  color: ${({ theme }) => theme.canvasText};\n  &:focus {\n    outline: 0;\n  }\n  ${({ active }) => (active ? sharedHoverStyles : '')}\n  user-select: none;\n`;\n"
  },
  {
    "path": "src/Select/Select.tsx",
    "content": "import React, {\n  forwardRef,\n  useCallback,\n  useImperativeHandle,\n  useMemo,\n  useRef\n} from 'react';\n\nimport { useId } from '../common/hooks/useId';\nimport { CommonStyledProps } from '../types';\n\nimport {\n  StyledDropdownMenu,\n  StyledDropdownMenuItem,\n  StyledInner,\n  StyledSelectContent\n} from './Select.styles';\nimport { SelectOption, SelectInnerProps, SelectRef } from './Select.types';\nimport { useSelectCommon } from './useSelectCommon';\nimport { useSelectState } from './useSelectState';\n\ntype SelectProps<T> = SelectInnerProps<T> &\n  Omit<\n    React.HTMLAttributes<HTMLDivElement>,\n    'defaultValue' | 'name' | 'onChange' | 'onFocus' | 'style' | 'value'\n  > &\n  CommonStyledProps;\n\nfunction SelectInnerOption<T>({\n  activateOptionIndex,\n  active,\n  index,\n  onClick,\n  option,\n  selected,\n  setRef\n}: {\n  activateOptionIndex: (optionIndex: number) => void;\n  active: boolean;\n  index: number;\n  onClick: React.MouseEventHandler<HTMLLIElement>;\n  option: SelectOption<T>;\n  selected: boolean;\n  setRef: (ref: HTMLLIElement | null, optionIndex: number) => void;\n}) {\n  const handleOnMouseEnter = useCallback(() => {\n    activateOptionIndex(index);\n  }, [activateOptionIndex, index]);\n\n  const handleSetRef = useCallback(\n    (ref: HTMLLIElement | null) => {\n      setRef(ref, index);\n    },\n    [index, setRef]\n  );\n\n  const id = useId();\n\n  return (\n    <StyledDropdownMenuItem\n      active={active}\n      aria-selected={selected ? 'true' : undefined}\n      data-value={option.value}\n      id={id}\n      onClick={onClick}\n      onMouseEnter={handleOnMouseEnter}\n      ref={handleSetRef}\n      role='option'\n      tabIndex={0}\n    >\n      {option.label}\n    </StyledDropdownMenuItem>\n  );\n}\n\nfunction SelectInner<T>(\n  {\n    'aria-label': ariaLabel,\n    'aria-labelledby': ariaLabelledBy,\n    className,\n    defaultValue,\n    disabled = false,\n    formatDisplay,\n    inputProps,\n    labelId,\n    menuMaxHeight,\n    name,\n    onBlur,\n    onChange,\n    onClose,\n    onFocus,\n    onKeyDown,\n    onMouseDown,\n    onOpen,\n    open: openProp,\n    options: optionsProp,\n    readOnly,\n    shadow = true,\n    style,\n    variant = 'default',\n    value: valueProp,\n    width = 'auto',\n    ...otherProps\n  }: SelectProps<T>,\n  ref: React.ForwardedRef<SelectRef>\n) {\n  const {\n    isEnabled,\n    options,\n    setValue,\n    value,\n    wrapperProps,\n    DropdownButton,\n    Wrapper\n  } = useSelectCommon<T>({\n    className,\n    defaultValue,\n    disabled,\n    native: false,\n    onChange,\n    options: optionsProp,\n    style,\n    readOnly,\n    value: valueProp,\n    variant,\n    width\n  });\n\n  const inputRef = useRef<HTMLInputElement | null>(null);\n  const selectRef = useRef<HTMLDivElement | null>(null);\n  const wrapperRef = useRef<HTMLDivElement | null>(null);\n\n  const {\n    activeOption,\n    handleActivateOptionIndex,\n    handleBlur,\n    handleButtonKeyDown,\n    handleDropdownKeyDown,\n    handleFocus,\n    handleMouseDown,\n    handleOptionClick,\n    handleSetDropdownRef,\n    handleSetOptionRef,\n    open,\n    selectedOption\n  } = useSelectState<T>({\n    onBlur,\n    onChange,\n    onClose,\n    onFocus,\n    onKeyDown,\n    onMouseDown,\n    onOpen,\n    open: openProp,\n    options,\n    value,\n    selectRef,\n    setValue,\n    wrapperRef\n  });\n\n  // to hijack native focus. when somebody passes ref\n  // and triggers focus, we focus displayNode instead of input\n  useImperativeHandle(\n    ref,\n    () => ({\n      focus: focusOptions => {\n        selectRef.current?.focus(focusOptions);\n      },\n      node: inputRef.current,\n      value: String(value)\n    }),\n    [value]\n  );\n\n  const displayLabel = useMemo(\n    () =>\n      !selectedOption\n        ? ''\n        : typeof formatDisplay === 'function'\n        ? formatDisplay(selectedOption)\n        : selectedOption.label,\n    [formatDisplay, selectedOption]\n  );\n  const tabIndex = isEnabled ? 1 : undefined;\n\n  const dropdownMenuStyle = useMemo(\n    () =>\n      menuMaxHeight\n        ? { overflow: 'auto', maxHeight: menuMaxHeight }\n        : undefined,\n    [menuMaxHeight]\n  );\n\n  const dropdownMenuId = useId();\n\n  const optionsContent = useMemo(\n    () =>\n      options.map((option, index) => {\n        const key = `${value}-${index}`;\n        const active = option === activeOption;\n        const selected = option === selectedOption;\n        return (\n          <SelectInnerOption\n            activateOptionIndex={handleActivateOptionIndex}\n            active={active}\n            index={index}\n            key={key}\n            onClick={handleOptionClick}\n            option={option}\n            selected={selected}\n            setRef={handleSetOptionRef}\n          />\n        );\n      }),\n    [\n      activeOption,\n      handleActivateOptionIndex,\n      handleOptionClick,\n      handleSetOptionRef,\n      options,\n      selectedOption,\n      value\n    ]\n  );\n\n  return (\n    <Wrapper\n      {...wrapperProps}\n      $disabled={disabled}\n      ref={wrapperRef}\n      shadow={shadow}\n      style={{ ...style, width }}\n    >\n      <input\n        name={name}\n        ref={inputRef}\n        type='hidden'\n        value={String(value)}\n        {...inputProps}\n      />\n      <StyledInner\n        aria-disabled={disabled}\n        aria-expanded={open}\n        aria-haspopup='listbox'\n        aria-label={ariaLabel}\n        aria-labelledby={ariaLabelledBy ?? labelId}\n        aria-owns={isEnabled && open ? dropdownMenuId : undefined}\n        onBlur={handleBlur}\n        onFocus={handleFocus}\n        onKeyDown={handleButtonKeyDown}\n        onMouseDown={isEnabled ? handleMouseDown : onMouseDown}\n        ref={selectRef}\n        role='button'\n        tabIndex={tabIndex}\n        {...otherProps}\n      >\n        <StyledSelectContent>{displayLabel}</StyledSelectContent>\n\n        {DropdownButton}\n      </StyledInner>\n      {isEnabled && open && (\n        <StyledDropdownMenu\n          id={dropdownMenuId}\n          onKeyDown={handleDropdownKeyDown}\n          ref={handleSetDropdownRef}\n          role='listbox'\n          style={dropdownMenuStyle}\n          tabIndex={0}\n          variant={variant}\n        >\n          {optionsContent}\n        </StyledDropdownMenu>\n      )}\n    </Wrapper>\n  );\n}\n\n/* eslint-disable no-use-before-define */\nconst Select = forwardRef(SelectInner) as <T>(\n  props: SelectProps<T> & { ref?: React.ForwardedRef<SelectRef> }\n) => ReturnType<typeof SelectInner<T>>;\n/* eslint-enable no-use-before-define */\n\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore\nSelect.displayName = 'Select';\n\nexport * from './SelectNative';\n\nexport { Select, SelectProps };\n"
  },
  {
    "path": "src/Select/Select.types.ts",
    "content": "import React from 'react';\nimport { HTMLDataAttributes } from '../types';\n\ntype SelectChangeEventTargetValue<T> = { value: T; name: string | undefined };\n\nexport type SelectChangeEvent<T> =\n  | (Omit<React.ChangeEvent<HTMLSelectElement>, 'target'> & {\n      target: Omit<\n        React.ChangeEvent<HTMLSelectElement>['target'],\n        'name' | 'value'\n      > &\n        SelectChangeEventTargetValue<T>;\n    })\n  | (Omit<React.MouseEvent, 'target'> & {\n      target: Omit<React.MouseEvent['target'], 'name' | 'value'> &\n        SelectChangeEventTargetValue<T>;\n    });\n\nexport type SelectOption<T> = {\n  label?: string;\n  value: T;\n};\n\nexport type SelectRef = Pick<HTMLInputElement, 'value' | 'focus'> & {\n  node: HTMLInputElement | null;\n};\n\nexport type SelectVariants = 'default' | 'flat';\n\nexport type SelectFormatDisplayCallback<T> = (\n  option: SelectOption<T>\n) => string;\n\nexport type SelectCommonProps<T> = {\n  'aria-label'?: string;\n  'aria-labelledby'?: string;\n  className?: string;\n  defaultValue?: T;\n  disabled?: boolean;\n  name?: string;\n  onChange?: (\n    selectedOption: SelectOption<T>,\n    options: {\n      fromEvent: Event | React.SyntheticEvent;\n    }\n  ) => void;\n  options?: (SelectOption<T> | null | undefined)[];\n  readOnly?: boolean;\n  shadow?: boolean;\n  style?: React.CSSProperties;\n  value?: T;\n  variant?: SelectVariants;\n  width?: React.CSSProperties['width'];\n};\n\nexport type SelectInnerProps<T> = {\n  formatDisplay?: SelectFormatDisplayCallback<T>;\n  inputProps?: React.HTMLAttributes<HTMLInputElement> & HTMLDataAttributes;\n  /** @deprecated Use `aria-labelledby` instead */\n  labelId?: string;\n  menuMaxHeight?: string | number;\n  onClose?: (options: { fromEvent: Event | React.SyntheticEvent }) => void;\n  onOpen?: (options: { fromEvent: Event | React.SyntheticEvent }) => void;\n  open?: boolean;\n} & Pick<\n  React.HTMLAttributes<HTMLDivElement>,\n  'onBlur' | 'onFocus' | 'onKeyDown' | 'onMouseDown'\n> &\n  SelectCommonProps<T>;\n"
  },
  {
    "path": "src/Select/SelectNative.spec.tsx",
    "content": "// Bsased on https://github.com/mui-org/material-ui\n\nimport { fireEvent, screen } from '@testing-library/react';\nimport React from 'react';\nimport { renderWithTheme } from '../../test/utils';\nimport { SelectOption } from './Select.types';\nimport { SelectNative } from './SelectNative';\n\nconst options: SelectOption<string>[] = [\n  { label: 'ten', value: '10' },\n  { label: 'twenty', value: '20' },\n  { label: 'thirty', value: '30' }\n];\n\ndescribe('<SelectNative />', () => {\n  describe('prop: native', () => {\n    it('renders a <select />', () => {\n      const { container } = renderWithTheme(<SelectNative options={options} />);\n      expect(container.querySelector('select')).toBeInTheDocument();\n    });\n\n    it('renders uses values for labels', () => {\n      const optionsWithoutLabels = options.map(({ value }) => ({ value }));\n      renderWithTheme(\n        <SelectNative options={optionsWithoutLabels} data-testid='select' />\n      );\n      expect(screen.getByTestId('select')).toHaveTextContent('10');\n    });\n\n    it('calls onChange if not disabled or readOnly', () => {\n      const handleChange = jest.fn();\n      renderWithTheme(\n        <>\n          <SelectNative\n            options={options}\n            data-testid='selectEnabled'\n            onChange={handleChange}\n          />\n          <SelectNative\n            options={options}\n            data-testid='selectDisabled'\n            disabled\n            onChange={handleChange}\n          />\n          <SelectNative\n            options={options}\n            data-testid='selectReadOnly'\n            onChange={handleChange}\n            readOnly\n          />\n        </>\n      );\n      fireEvent.change(screen.getByTestId('selectEnabled'));\n      fireEvent.change(screen.getByTestId('selectDisabled'));\n      fireEvent.change(screen.getByTestId('selectReadOnly'));\n      expect(handleChange).toHaveBeenCalledTimes(1);\n      expect(handleChange).toHaveBeenCalledWith(options[0], {\n        fromEvent: expect.objectContaining({ type: 'change' })\n      });\n    });\n\n    it('can be labelled with a <label />', () => {\n      renderWithTheme(\n        <>\n          <label htmlFor='select'>A select</label>\n          <SelectNative id='select' options={options} />\n        </>\n      );\n      expect(screen.getByLabelText('A select')).toHaveProperty(\n        'tagName',\n        'SELECT'\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "src/Select/SelectNative.tsx",
    "content": "import React, { forwardRef, useCallback } from 'react';\n\nimport { noOp } from '../common/utils';\n\nimport { StyledInner, StyledNativeSelect } from './Select.styles';\nimport { SelectCommonProps } from './Select.types';\nimport { useSelectCommon } from './useSelectCommon';\n\ntype SelectNativeProps = SelectCommonProps<string> &\n  Omit<\n    React.SelectHTMLAttributes<HTMLSelectElement>,\n    'defaultValue' | 'name' | 'onChange' | 'style' | 'value'\n  >;\n\nconst SelectNative = forwardRef<HTMLSelectElement, SelectNativeProps>(\n  (\n    {\n      className,\n      defaultValue,\n      disabled,\n      onChange,\n      options: optionsProp,\n      readOnly,\n      style,\n      value: valueProp,\n      variant,\n      width,\n      ...otherProps\n    },\n    ref\n  ) => {\n    const { isEnabled, options, setValue, value, DropdownButton, Wrapper } =\n      useSelectCommon<string>({\n        defaultValue,\n        disabled,\n        native: true,\n        onChange,\n        options: optionsProp,\n        readOnly,\n        value: valueProp,\n        variant\n      });\n\n    const handleChange = useCallback(\n      (event: React.ChangeEvent<HTMLSelectElement>) => {\n        const selectedOption = options.find(\n          option => option.value === event.target.value\n        );\n\n        if (!selectedOption) {\n          return;\n        }\n\n        setValue(selectedOption.value);\n        onChange?.(selectedOption, { fromEvent: event });\n      },\n      [onChange, options, setValue]\n    );\n\n    return (\n      <Wrapper className={className} style={{ ...style, width }}>\n        <StyledInner>\n          <StyledNativeSelect\n            {...otherProps}\n            disabled={disabled}\n            onChange={isEnabled ? handleChange : noOp}\n            ref={ref}\n            value={value}\n          >\n            {options.map((option, index) => (\n              <option key={`${option.value}-${index}`} value={option.value}>\n                {option.label ?? option.value}\n              </option>\n            ))}\n          </StyledNativeSelect>\n          {DropdownButton}\n        </StyledInner>\n      </Wrapper>\n    );\n  }\n);\n\nSelectNative.displayName = 'SelectNative';\n\nexport { SelectNative, SelectNativeProps };\n"
  },
  {
    "path": "src/Select/useSelectCommon.tsx",
    "content": "import React, { useMemo } from 'react';\nimport useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';\nimport {\n  StyledDropdownButton,\n  StyledDropdownIcon,\n  StyledFlatSelectWrapper,\n  StyledSelectWrapper\n} from './Select.styles';\n\nimport { SelectCommonProps, SelectOption } from './Select.types';\n\nconst emptyArray: [] = [];\n\nexport const useSelectCommon = <T,>({\n  className,\n  defaultValue,\n  disabled,\n  native,\n  onChange,\n  options: optionsProp = emptyArray,\n  readOnly,\n  style,\n  value: valueProp,\n  variant,\n  width\n}: { native: boolean } & SelectCommonProps<T>) => {\n  const options = useMemo(\n    () => optionsProp.filter(Boolean) as SelectOption<T>[],\n    [optionsProp]\n  );\n\n  const [value, setValue] = useControlledOrUncontrolled({\n    defaultValue: defaultValue ?? options?.[0]?.value,\n    onChange,\n    readOnly,\n    value: valueProp\n  });\n\n  const isEnabled = !(disabled || readOnly);\n\n  const wrapperProps: React.HTMLAttributes<HTMLDivElement> = useMemo(\n    () => ({\n      className,\n      style: { ...style, width }\n    }),\n    [className, style, width]\n  );\n\n  const DropdownButton = useMemo(\n    () => (\n      <StyledDropdownButton\n        as='div'\n        data-testid='select-button'\n        $disabled={disabled}\n        native={native}\n        tabIndex={-1}\n        variant={variant === 'flat' ? 'flat' : 'raised'}\n      >\n        <StyledDropdownIcon data-testid='select-icon' $disabled={disabled} />\n      </StyledDropdownButton>\n    ),\n    [disabled, native, variant]\n  );\n\n  const Wrapper = useMemo(\n    () => (variant === 'flat' ? StyledFlatSelectWrapper : StyledSelectWrapper),\n    [variant]\n  );\n\n  return useMemo(\n    () => ({\n      isEnabled,\n      options,\n      value,\n      setValue,\n      wrapperProps,\n      DropdownButton,\n      Wrapper\n    }),\n    [DropdownButton, Wrapper, isEnabled, options, setValue, value, wrapperProps]\n  );\n};\n"
  },
  {
    "path": "src/Select/useSelectState.ts",
    "content": "import React, {\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\n  useState\n} from 'react';\n\nimport { KEYBOARD_KEY_CODES } from '../common/constants';\nimport useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';\nimport { clamp } from '../common/utils';\n\nimport { SelectOption, SelectInnerProps } from './Select.types';\n\nconst TYPING_RESET_DELAY = 1000;\n\nexport const useSelectState = <T>({\n  onBlur,\n  onChange,\n  onClose,\n  onFocus,\n  onKeyDown,\n  onMouseDown,\n  onOpen,\n  open: openProp,\n  options,\n  readOnly,\n  value,\n  selectRef,\n  setValue,\n  wrapperRef\n}: Omit<SelectInnerProps<T>, 'options' | 'value'> & {\n  options: SelectOption<T>[];\n  selectRef: React.MutableRefObject<HTMLDivElement | null>;\n  setValue: (newValue: React.SetStateAction<T>) => void;\n  value: T;\n  wrapperRef: React.MutableRefObject<HTMLDivElement | null>;\n}) => {\n  // Element references for scrolling to the active option\n  const dropdownRef = useRef<HTMLUListElement | null>(null);\n  const optionRefs = useRef<(HTMLLIElement | null)[]>([]);\n\n  // State references so callbacks are not reset on every change\n  const selectedIndex = useRef<number>(0);\n  const activeIndex = useRef(0);\n\n  // Buffer to focus option after it is rendered and the reference becomes known\n  const focusIndexWhenSet = useRef<number>();\n\n  // Typing state references so callbacks are not reset on every change\n  const typingMode = useRef<'search' | 'cycleFirstLetter'>('search');\n  const typedString = useRef<string>('');\n  const typingTimer = useRef<ReturnType<typeof setTimeout>>();\n\n  // Open state\n  const [open, setOpen] = useControlledOrUncontrolled({\n    defaultValue: false,\n    onChange: onOpen,\n    onChangePropName: 'onOpen',\n    readOnly,\n    value: openProp,\n    valuePropName: 'open'\n  });\n\n  // Exposed selected option\n  const selectedOption = useMemo(() => {\n    const index = options.findIndex(option => option.value === value);\n    selectedIndex.current = clamp(index, 0, null);\n    return options[index];\n  }, [options, value]);\n\n  // Exposed active option\n  const [activeOption, setActiveOption] = useState(options[0]);\n\n  // Focuses and scrolls to the option, pinning it to the top or bottom of the\n  // scroll area. The default focus behavior scrolls inconsistently.\n  const focusOption = useCallback(\n    (index: number) => {\n      const dropdownEl = dropdownRef.current;\n      const optionEl = optionRefs.current[index];\n      if (!optionEl || !dropdownEl) {\n        focusIndexWhenSet.current = index;\n        return;\n      }\n      focusIndexWhenSet.current = undefined;\n\n      const dropdownHeight = dropdownEl.clientHeight;\n      const dropdownScrollTop = dropdownEl.scrollTop;\n      const dropdownScrollEnd = dropdownEl.scrollTop + dropdownHeight;\n      const optionTop = optionEl.offsetTop;\n      const optionHeight = optionEl.offsetHeight;\n      const optionBottom = optionEl.offsetTop + optionEl.offsetHeight;\n\n      if (optionTop < dropdownScrollTop) {\n        dropdownEl.scrollTo(0, optionTop);\n      }\n      if (optionBottom > dropdownScrollEnd) {\n        dropdownEl.scrollTo(0, optionTop - dropdownHeight + optionHeight);\n      }\n      optionEl.focus({ preventScroll: true });\n    },\n    [dropdownRef]\n  );\n\n  // Activates an option relatively or absolutely\n  const activateOption = useCallback(\n    (\n      indexOrOption:\n        | number\n        | 'first'\n        | 'last'\n        | 'next'\n        | 'previous'\n        | 'selected',\n      { scroll }: { scroll?: boolean } = {}\n    ) => {\n      const lastIndex = options.length - 1;\n      let index;\n      switch (indexOrOption) {\n        case 'first': {\n          index = 0;\n          break;\n        }\n        case 'last': {\n          index = lastIndex;\n          break;\n        }\n        case 'next': {\n          index = clamp(activeIndex.current + 1, 0, lastIndex);\n          break;\n        }\n        case 'previous': {\n          index = clamp(activeIndex.current - 1, 0, lastIndex);\n          break;\n        }\n        case 'selected': {\n          index = clamp(selectedIndex.current ?? 0, 0, lastIndex);\n          break;\n        }\n        default:\n          index = indexOrOption;\n      }\n\n      activeIndex.current = index;\n      setActiveOption(options[index]);\n\n      if (scroll) {\n        focusOption(index);\n      }\n    },\n    [activeIndex, options, focusOption]\n  );\n\n  // Opens the dropdown and activates the selected option\n  const openDropdown = useCallback(\n    ({ fromEvent }: { fromEvent: React.SyntheticEvent }) => {\n      setOpen(true);\n      activateOption('selected', { scroll: true });\n      onOpen?.({ fromEvent });\n    },\n    [activateOption, onOpen, setOpen]\n  );\n\n  // Resets the typing states and clears timers\n  const clearSearchFromTyping = useCallback(() => {\n    typingMode.current = 'search';\n    typedString.current = '';\n    clearTimeout(typingTimer.current);\n  }, []);\n\n  // Closes the dropdown and resets its state\n  const closeDropdown = useCallback(\n    ({\n      focusSelect,\n      fromEvent\n    }: {\n      focusSelect: boolean;\n      fromEvent: Event | React.SyntheticEvent;\n    }) => {\n      onClose?.({ fromEvent });\n      setOpen(false);\n      setActiveOption(options[0]);\n      clearSearchFromTyping();\n      focusIndexWhenSet.current = undefined;\n      if (focusSelect) {\n        selectRef.current?.focus();\n      }\n    },\n    [clearSearchFromTyping, onClose, options, selectRef, setOpen]\n  );\n\n  // Toggles the dropdown open state\n  const toggleDropdown = useCallback(\n    ({ fromEvent }: { fromEvent: React.SyntheticEvent }) => {\n      if (open) {\n        closeDropdown({ focusSelect: false, fromEvent });\n      } else {\n        openDropdown({ fromEvent });\n      }\n    },\n    [closeDropdown, openDropdown, open]\n  );\n\n  // Selects an option and updates the exposed state\n  const selectOptionIndex = useCallback(\n    (\n      optionIndex: number,\n      { fromEvent }: { fromEvent: Event | React.SyntheticEvent }\n    ) => {\n      if (selectedIndex.current === optionIndex) {\n        return;\n      }\n\n      selectedIndex.current = optionIndex;\n      setValue(options[optionIndex].value);\n      onChange?.(options[optionIndex], { fromEvent });\n    },\n    [onChange, options, setValue]\n  );\n\n  // Selects the active option and close the dropdown\n  const selectActiveOptionAndClose = useCallback(\n    ({\n      focusSelect,\n      fromEvent\n    }: {\n      focusSelect: boolean;\n      fromEvent: Event | React.SyntheticEvent;\n    }) => {\n      selectOptionIndex(activeIndex.current, { fromEvent });\n      closeDropdown({ focusSelect, fromEvent });\n    },\n    [closeDropdown, selectOptionIndex]\n  );\n\n  // Searches options for the typed letter and activates it (if open) or selects\n  // it (if closed)\n  const searchFromTyping = useCallback(\n    (\n      letter: string,\n      {\n        fromEvent,\n        select\n      }: { fromEvent: React.SyntheticEvent; select: boolean }\n    ) => {\n      if (\n        typingMode.current === 'cycleFirstLetter' &&\n        letter !== typedString.current\n      ) {\n        typingMode.current = 'search';\n      }\n\n      if (letter === typedString.current) {\n        typingMode.current = 'cycleFirstLetter';\n      } else {\n        typedString.current += letter;\n      }\n\n      switch (typingMode.current) {\n        case 'search': {\n          let foundOptionIndex = options.findIndex(\n            option =>\n              option.label?.toLocaleUpperCase().indexOf(typedString.current) ===\n              0\n          );\n          if (foundOptionIndex < 0) {\n            foundOptionIndex = options.findIndex(\n              option => option.label?.toLocaleUpperCase().indexOf(letter) === 0\n            );\n            typedString.current = letter;\n          }\n          if (foundOptionIndex >= 0) {\n            if (select) {\n              selectOptionIndex(foundOptionIndex, { fromEvent });\n            } else {\n              activateOption(foundOptionIndex, { scroll: true });\n            }\n          }\n          break;\n        }\n        case 'cycleFirstLetter': {\n          const currentOptionIndex = select\n            ? selectedIndex.current ?? -1\n            : activeIndex.current;\n          let foundOptionIndex = options.findIndex(\n            (option, index) =>\n              index > currentOptionIndex &&\n              option.label?.toLocaleUpperCase().indexOf(letter) === 0\n          );\n          if (foundOptionIndex < 0) {\n            foundOptionIndex = options.findIndex(\n              option => option.label?.toLocaleUpperCase().indexOf(letter) === 0\n            );\n          }\n          if (foundOptionIndex >= 0) {\n            if (select) {\n              selectOptionIndex(foundOptionIndex, { fromEvent });\n            } else {\n              activateOption(foundOptionIndex, { scroll: true });\n            }\n          }\n\n          break;\n        }\n        default:\n      }\n\n      clearTimeout(typingTimer.current);\n      typingTimer.current = setTimeout(() => {\n        if (typingMode.current === 'search') {\n          typedString.current = '';\n        }\n      }, TYPING_RESET_DELAY);\n    },\n    [activateOption, options, selectOptionIndex]\n  );\n\n  // MouseDown handler for the select button\n  const handleMouseDown = useCallback(\n    (event: React.MouseEvent<HTMLDivElement>) => {\n      // ignore everything but left-click\n      if (event.button !== 0) {\n        return;\n      }\n\n      // hijack the default focus behavior.\n      event.preventDefault();\n      selectRef.current?.focus();\n\n      toggleDropdown({ fromEvent: event });\n\n      onMouseDown?.(event);\n    },\n    [onMouseDown, selectRef, toggleDropdown]\n  );\n\n  // Click handler for every option\n  const handleOptionClick = useCallback(\n    (event: React.MouseEvent<HTMLLIElement>) => {\n      selectActiveOptionAndClose({ focusSelect: true, fromEvent: event });\n    },\n    [selectActiveOptionAndClose]\n  );\n\n  // KeyDown handler for select button and dropdown menu, implementing\n  // recommended keyboard interactions from [ARIA's document][1] as well as some\n  // common practices for listboxes on Windows and macOS.\n  // [1]: https://www.w3.org/WAI/ARIA/apg/patterns/listbox/#keyboard-interaction-11\n  const handleKeyDown = useCallback(\n    (event: React.KeyboardEvent<HTMLDivElement | HTMLUListElement>) => {\n      const { altKey, code, ctrlKey, metaKey, shiftKey } = event;\n      const { ARROW_DOWN, ARROW_UP, END, ENTER, ESC, HOME, SPACE, TAB } =\n        KEYBOARD_KEY_CODES;\n\n      const modifierKey = altKey || ctrlKey || metaKey || shiftKey;\n      const modifierKeyButShift = altKey || ctrlKey || metaKey;\n\n      // Skips handling if any modifier key is set, but allows shift + tab to select the\n      if (\n        (code === TAB && modifierKeyButShift) ||\n        (code !== TAB && modifierKey)\n      ) {\n        return;\n      }\n\n      switch (code) {\n        case ARROW_DOWN: {\n          event.preventDefault();\n\n          if (!open) {\n            openDropdown({ fromEvent: event });\n            return;\n          }\n\n          activateOption('next', { scroll: true });\n          break;\n        }\n        case ARROW_UP: {\n          event.preventDefault();\n\n          if (!open) {\n            openDropdown({ fromEvent: event });\n            return;\n          }\n\n          activateOption('previous', { scroll: true });\n          break;\n        }\n        case END: {\n          event.preventDefault();\n\n          if (!open) {\n            openDropdown({ fromEvent: event });\n            return;\n          }\n\n          activateOption('last', { scroll: true });\n          break;\n        }\n        case ENTER: {\n          if (!open) {\n            return;\n          }\n\n          event.preventDefault();\n\n          selectActiveOptionAndClose({ focusSelect: true, fromEvent: event });\n          break;\n        }\n        case ESC: {\n          if (!open) {\n            return;\n          }\n\n          event.preventDefault();\n\n          closeDropdown({ focusSelect: true, fromEvent: event });\n          break;\n        }\n        case HOME: {\n          event.preventDefault();\n\n          if (!open) {\n            openDropdown({ fromEvent: event });\n            return;\n          }\n\n          activateOption('first', { scroll: true });\n          break;\n        }\n        case SPACE: {\n          event.preventDefault();\n          if (open) {\n            selectActiveOptionAndClose({ focusSelect: true, fromEvent: event });\n          } else {\n            openDropdown({ fromEvent: event });\n          }\n\n          break;\n        }\n        case TAB: {\n          if (!open) {\n            return;\n          }\n\n          if (!shiftKey) {\n            event.preventDefault();\n          }\n\n          selectActiveOptionAndClose({\n            focusSelect: !shiftKey,\n            fromEvent: event\n          });\n          break;\n        }\n        default:\n          if (!modifierKey && code.match(/^Key/)) {\n            event.preventDefault();\n            event.stopPropagation();\n\n            searchFromTyping(code.replace(/^Key/, ''), {\n              select: !open,\n              fromEvent: event\n            });\n          }\n      }\n    },\n    [\n      activateOption,\n      closeDropdown,\n      open,\n      openDropdown,\n      searchFromTyping,\n      selectActiveOptionAndClose\n    ]\n  );\n\n  // KeyDown handler for the select button\n  const handleButtonKeyDown = useCallback(\n    (event: React.KeyboardEvent<HTMLDivElement>) => {\n      handleKeyDown(event);\n      onKeyDown?.(event);\n    },\n    [handleKeyDown, onKeyDown]\n  );\n\n  // Activate handler when MouseEntering an option\n  const handleActivateOptionIndex = useCallback(\n    (index: number) => {\n      activateOption(index);\n    },\n    [activateOption]\n  );\n\n  // Blur handler for the select button\n  const handleBlur = useCallback(\n    (event: React.FocusEvent<HTMLDivElement>) => {\n      // Trigger onBlur only when dropdown is closed, otherwise it would be\n      // triggered when switching focus to the menu.\n      if (open) {\n        return;\n      }\n      clearSearchFromTyping();\n      onBlur?.(event);\n    },\n    [clearSearchFromTyping, onBlur, open]\n  );\n\n  // Focus handler for the select button\n  const handleFocus = useCallback(\n    (event: React.FocusEvent<HTMLDivElement>) => {\n      clearSearchFromTyping();\n      onFocus?.(event);\n    },\n    [clearSearchFromTyping, onFocus]\n  );\n\n  // Handles setting the dropdown ref and focusing the active option\n  const handleSetDropdownRef = useCallback(\n    (ref: HTMLUListElement | null) => {\n      dropdownRef.current = ref;\n      if (focusIndexWhenSet.current !== undefined) {\n        focusOption(focusIndexWhenSet.current);\n      }\n    },\n    [focusOption]\n  );\n\n  // Handles setting each option ref and focusing the active option\n  const handleSetOptionRef = useCallback(\n    (optionRef: HTMLLIElement | null, index: number) => {\n      optionRefs.current[index] = optionRef;\n      if (focusIndexWhenSet.current === index) {\n        focusOption(focusIndexWhenSet.current);\n      }\n    },\n    [focusOption]\n  );\n\n  // Listen to mousedown outside of the element to close the dropdown\n  useEffect(() => {\n    if (!open) {\n      return () => {};\n    }\n\n    const outsideMouseDown = (event: MouseEvent) => {\n      const target = event.target as Node;\n\n      if (!wrapperRef.current?.contains(target)) {\n        event.preventDefault();\n        closeDropdown({ focusSelect: false, fromEvent: event });\n      }\n    };\n\n    document.addEventListener('mousedown', outsideMouseDown);\n    return () => {\n      document.removeEventListener('mousedown', outsideMouseDown);\n    };\n  }, [closeDropdown, open, wrapperRef]);\n\n  return useMemo(\n    () => ({\n      activeOption,\n      handleActivateOptionIndex,\n      handleBlur,\n      handleButtonKeyDown,\n      handleDropdownKeyDown: handleKeyDown,\n      handleFocus,\n      handleMouseDown,\n      handleOptionClick,\n      handleSetDropdownRef,\n      handleSetOptionRef,\n      open,\n      selectedOption\n    }),\n    [\n      activeOption,\n      handleActivateOptionIndex,\n      handleBlur,\n      handleButtonKeyDown,\n      handleFocus,\n      handleKeyDown,\n      handleMouseDown,\n      handleOptionClick,\n      handleSetDropdownRef,\n      handleSetOptionRef,\n      open,\n      selectedOption\n    ]\n  );\n};\n"
  },
  {
    "path": "src/Separator/Separator.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Separator } from './Separator';\n\ndescribe('<Separator />', () => {\n  it('should render Separator', () => {\n    const { container } = renderWithTheme(<Separator />);\n    const separator = container.firstElementChild;\n\n    expect(separator).toBeInTheDocument();\n  });\n\n  describe('prop: size', () => {\n    it('defaults to 100%', () => {\n      const { container } = renderWithTheme(<Separator />);\n      const separator = container.firstElementChild;\n      expect(separator).toHaveStyleRule('width', '100%');\n    });\n    it('sets size passed correctly', () => {\n      const size = '53px';\n      const { container } = renderWithTheme(<Separator size={size} />);\n      const separator = container.firstElementChild;\n\n      expect(separator).toHaveStyleRule('width', size);\n    });\n  });\n\n  describe('prop: orientation', () => {\n    it('renders horizontal line by default', () => {\n      const size = '53px';\n      const { container } = renderWithTheme(<Separator size={size} />);\n      const separator = container.firstElementChild;\n\n      expect(separator).toHaveStyleRule('width', size);\n    });\n\n    it('renders vertical line when orientation=\"vertical\"', () => {\n      const size = '53px';\n      const { container } = renderWithTheme(\n        <Separator orientation='vertical' size={size} />\n      );\n      const separator = container.firstElementChild;\n\n      expect(separator).toHaveStyleRule('height', size);\n    });\n  });\n\n  describe('prop: size', () => {\n    it('should set proper size', () => {\n      const { container } = renderWithTheme(<Separator size='85%' />);\n      const separator = container.firstElementChild;\n\n      expect(separator).toHaveStyleRule('width', '85%');\n    });\n\n    it('when passed a number, sets size in px', () => {\n      const { container } = renderWithTheme(<Separator size={25} />);\n      const separator = container.firstElementChild;\n\n      expect(separator).toHaveStyleRule('width', '25px');\n    });\n\n    it('should set height when vertical', () => {\n      const { container } = renderWithTheme(\n        <Separator size={25} orientation='vertical' />\n      );\n      const separator = container.firstElementChild;\n\n      expect(separator).toHaveStyleRule('height', '25px');\n    });\n  });\n});\n"
  },
  {
    "path": "src/Separator/Separator.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport styled from 'styled-components';\n\nimport { MenuList, MenuListItem, Separator } from 'react95';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n`;\n\nexport default {\n  title: 'Layout/Separator',\n  component: Separator,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Separator>;\n\nexport function Default() {\n  return (\n    <>\n      <MenuList>\n        <MenuListItem>Item 1</MenuListItem>\n        <Separator />\n        <MenuListItem>Item 2</MenuListItem>\n        <Separator />\n        <MenuListItem>Item 3</MenuListItem>\n      </MenuList>\n      <MenuList inline style={{ margin: 30 }}>\n        <MenuListItem>Item 1</MenuListItem>\n        <Separator orientation='vertical' size='43px' />\n        <MenuListItem>Item 2</MenuListItem>\n        <Separator orientation='vertical' size='43px' />\n        <MenuListItem>Item 3</MenuListItem>\n      </MenuList>\n    </>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/Separator/Separator.tsx",
    "content": "import styled from 'styled-components';\nimport { getSize } from '../common/utils';\nimport { Orientation } from '../types';\n\ntype SeparatorProps = {\n  size?: string | number;\n  orientation?: Orientation;\n};\n\nconst Separator = styled.div<SeparatorProps>`\n  ${({ orientation, theme, size = '100%' }) =>\n    orientation === 'vertical'\n      ? `\n    height: ${getSize(size)};\n    border-left: 2px solid ${theme.borderDark};\n    border-right: 2px solid ${theme.borderLightest};\n    margin: 0;\n    `\n      : `\n    width: ${getSize(size)};\n    border-bottom: 2px solid ${theme.borderLightest};\n    border-top: 2px solid ${theme.borderDark};\n    margin: 0;\n    `}\n`;\n\nSeparator.displayName = 'Separator';\n\nexport { Separator, SeparatorProps };\n"
  },
  {
    "path": "src/Slider/Slider.spec.tsx",
    "content": "// Pretty much straight out copied from https://github.com/mui-org/material-ui 😂\nimport { fireEvent } from '@testing-library/react';\nimport React from 'react';\n\nimport { renderWithTheme, Touch } from '../../test/utils';\nimport { Slider } from './Slider';\n\nfunction createTouches(\n  touches: { identifier: number; clientX?: number; clientY?: number }[]\n) {\n  return {\n    changedTouches: touches.map(touch => new Touch(touch))\n  };\n}\n\ndescribe('<Slider />', () => {\n  beforeAll(() => {\n    jest\n      .spyOn(HTMLElement.prototype, 'getBoundingClientRect')\n      .mockImplementation(\n        () =>\n          ({\n            width: 100,\n            height: 20,\n            bottom: 20,\n            left: 0\n          } as DOMRect)\n      );\n  });\n\n  it('should call handlers', () => {\n    const handleChange = jest.fn();\n    const handleChangeCommitted = jest.fn();\n\n    const { container, getByRole } = renderWithTheme(\n      <Slider\n        onChange={handleChange}\n        onChangeCommitted={handleChangeCommitted}\n        value={0}\n      />\n    );\n\n    const slider = container.firstElementChild as HTMLElement;\n    fireEvent.mouseDown(slider);\n    fireEvent.mouseUp(document.body);\n\n    expect(handleChange).toHaveBeenCalledTimes(1);\n    expect(handleChangeCommitted).toHaveBeenCalledTimes(1);\n\n    getByRole('slider').focus();\n    const focusedSlider = document.activeElement as HTMLElement;\n    fireEvent.keyDown(focusedSlider, {\n      key: 'Home'\n    });\n    expect(handleChange).toHaveBeenCalledTimes(2);\n    expect(handleChangeCommitted).toHaveBeenCalledTimes(2);\n  });\n\n  it('should only listen to changes from the same touchpoint', () => {\n    const handleChange = jest.fn();\n    const handleChangeCommitted = jest.fn();\n    const { container } = renderWithTheme(\n      <Slider\n        onChange={handleChange}\n        onChangeCommitted={handleChangeCommitted}\n        value={0}\n      />\n    );\n\n    const slider = container.firstElementChild as HTMLElement;\n    fireEvent.touchStart(slider, createTouches([{ identifier: 1 }]));\n    expect(handleChange).toHaveBeenCalledTimes(1);\n    expect(handleChangeCommitted).not.toHaveBeenCalled();\n\n    fireEvent.touchEnd(document.body, createTouches([{ identifier: 2 }]));\n    expect(handleChange).toHaveBeenCalledTimes(1);\n    expect(handleChangeCommitted).not.toHaveBeenCalled();\n\n    fireEvent.touchMove(document.body, createTouches([{ identifier: 1 }]));\n    expect(handleChange).toHaveBeenCalledTimes(2);\n    expect(handleChangeCommitted).toHaveBeenCalledTimes(0);\n\n    fireEvent.touchMove(document.body, createTouches([{ identifier: 2 }]));\n    expect(handleChange).toHaveBeenCalledTimes(2);\n    expect(handleChangeCommitted).toHaveBeenCalledTimes(0);\n\n    fireEvent.touchEnd(document.body, createTouches([{ identifier: 1 }]));\n    expect(handleChange).toHaveBeenCalledTimes(2);\n    expect(handleChangeCommitted).toHaveBeenCalledTimes(1);\n  });\n\n  it('defaults to horizontal orientation', () => {\n    const { getByRole } = renderWithTheme(<Slider defaultValue={0} />);\n\n    expect(getByRole('slider')).toHaveAttribute(\n      'aria-orientation',\n      'horizontal'\n    );\n  });\n  it('should forward mouseDown', () => {\n    const handleMouseDown = jest.fn();\n    const { container } = renderWithTheme(\n      <Slider onMouseDown={handleMouseDown} defaultValue={0} />\n    );\n    const slider = container.firstElementChild as HTMLElement;\n    fireEvent.mouseDown(slider);\n    expect(handleMouseDown).toHaveBeenCalledTimes(1);\n  });\n  describe('prop: step', () => {\n    it('should handle a null step', () => {\n      const { getByRole, container } = renderWithTheme(\n        <Slider\n          step={null}\n          marks={[{ value: 0 }, { value: 20 }, { value: 30 }]}\n          defaultValue={0}\n        />\n      );\n      const slider = container.firstElementChild as HTMLElement;\n      // mocking containers size\n      const thumb = getByRole('slider');\n\n      fireEvent.touchStart(\n        slider,\n        createTouches([{ identifier: 1, clientX: 22, clientY: 0 }])\n      );\n      expect(thumb).toHaveAttribute('aria-valuenow', '20');\n\n      thumb.focus();\n      fireEvent.keyDown(document.activeElement as HTMLElement, {\n        key: 'ArrowUp'\n      });\n      expect(thumb).toHaveAttribute('aria-valuenow', '30');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, {\n        key: 'ArrowDown'\n      });\n      expect(thumb).toHaveAttribute('aria-valuenow', '20');\n    });\n  });\n\n  describe('prop: disabled', () => {\n    it('should render disabled slider', () => {\n      const { getByRole, container } = renderWithTheme(\n        <Slider\n          step={null}\n          marks={[{ value: 0 }, { value: 20 }, { value: 30 }]}\n          defaultValue={0}\n          disabled\n        />\n      );\n      const slider = container.firstElementChild as HTMLElement;\n      const thumb = getByRole('slider');\n      expect(\n        window.getComputedStyle(slider, null).getPropertyValue('pointer-events')\n      ).toBe('none');\n      expect(thumb).toHaveAttribute('aria-disabled', 'true');\n    });\n  });\n\n  describe('keyboard', () => {\n    it('should handle all the keys', () => {\n      const { getByRole } = renderWithTheme(<Slider defaultValue={50} />);\n      const thumb = getByRole('slider');\n      thumb.focus();\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, {\n        key: 'Home'\n      });\n      expect(thumb).toHaveAttribute('aria-valuenow', '0');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, {\n        key: 'End'\n      });\n      expect(thumb).toHaveAttribute('aria-valuenow', '100');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, {\n        key: 'PageDown'\n      });\n      expect(thumb).toHaveAttribute('aria-valuenow', '90');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, {\n        key: 'Escape'\n      });\n      expect(thumb).toHaveAttribute('aria-valuenow', '90');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, {\n        key: 'PageUp'\n      });\n      expect(thumb).toHaveAttribute('aria-valuenow', '100');\n    });\n\n    const moveLeftEvent = {\n      key: 'ArrowLeft'\n    };\n    const moveRightEvent = {\n      key: 'ArrowRight'\n    };\n\n    it('should use min as the step origin', () => {\n      const { getByRole } = renderWithTheme(\n        <Slider defaultValue={4} step={2} max={12} min={2} />\n      );\n      const thumb = getByRole('slider');\n      thumb.focus();\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveRightEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '6');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveLeftEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '4');\n\n      expect(thumb.style.left).toBe('20%');\n    });\n\n    it('should reach right edge value', () => {\n      const { getByRole } = renderWithTheme(\n        <Slider defaultValue={90} min={6} max={108} step={10} />\n      );\n      const thumb = getByRole('slider');\n      thumb.focus();\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveRightEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '96');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveRightEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '106');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveRightEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '108');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveLeftEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '96');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveLeftEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '86');\n    });\n\n    it('should reach left edge value', () => {\n      const { getByRole } = renderWithTheme(\n        <Slider defaultValue={20} min={6} max={108} step={10} />\n      );\n      const thumb = getByRole('slider');\n      thumb.focus();\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveLeftEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '6');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveRightEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '16');\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveRightEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '26');\n    });\n\n    it('should round value to step precision', () => {\n      const { getByRole } = renderWithTheme(\n        <Slider defaultValue={0.2} min={0} max={1} step={0.1} />\n      );\n      const thumb = getByRole('slider');\n      thumb.focus();\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveRightEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '0.3');\n    });\n\n    it('should not fail to round value to step precision when step is very small', () => {\n      const { getByRole } = renderWithTheme(\n        <Slider\n          defaultValue={0.00000002}\n          min={0}\n          max={0.00000005}\n          step={0.00000001}\n        />\n      );\n      const thumb = getByRole('slider');\n      thumb.focus();\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveRightEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '3e-8');\n    });\n\n    it('should not fail to round value to step precision when step is very small and negative', () => {\n      const { getByRole } = renderWithTheme(\n        <Slider\n          defaultValue={-0.00000002}\n          min={-0.00000005}\n          max={0}\n          step={0.00000001}\n        />\n      );\n      const thumb = getByRole('slider');\n      thumb.focus();\n\n      fireEvent.keyDown(document.activeElement as HTMLElement, moveLeftEvent);\n      expect(thumb).toHaveAttribute('aria-valuenow', '-3e-8');\n    });\n  });\n\n  describe('prop: orientation', () => {\n    it('when vertical, should render with aria-orientation attribute set to \"vertical\" ', () => {\n      const { getByRole } = renderWithTheme(\n        <Slider orientation='vertical' defaultValue={0} />\n      );\n\n      expect(getByRole('slider')).toHaveAttribute(\n        'aria-orientation',\n        'vertical'\n      );\n    });\n\n    it('should report the right position', () => {\n      const handleChange = jest.fn();\n      const { container } = renderWithTheme(\n        <Slider\n          orientation='vertical'\n          defaultValue={20}\n          onChange={handleChange}\n        />\n      );\n\n      const slider = container.firstElementChild as HTMLElement;\n\n      // mocking containers size\n      jest\n        .spyOn(HTMLElement.prototype, 'getBoundingClientRect')\n        .mockImplementation(\n          () =>\n            ({\n              width: 20,\n              height: 100,\n              bottom: 100,\n              left: 0\n            } as DOMRect)\n        );\n\n      fireEvent.touchStart(\n        slider,\n        createTouches([{ identifier: 1, clientX: 0, clientY: 20 }])\n      );\n      fireEvent.touchMove(\n        document.body,\n        createTouches([{ identifier: 1, clientX: 0, clientY: 22 }])\n      );\n\n      expect(handleChange).toHaveBeenCalledTimes(2);\n      expect(handleChange.mock.calls[0][0]).toBe(80);\n      expect(handleChange.mock.calls[1][0]).toBe(78);\n    });\n  });\n\n  describe('prop: marks', () => {\n    it('displays only ticks when marks is set to \"true\"', () => {\n      const { queryAllByTestId } = renderWithTheme(\n        <Slider marks defaultValue={0} min={0} max={6} />\n      );\n      const ticks = queryAllByTestId('tick');\n      const marks = queryAllByTestId('mark');\n\n      expect(ticks.length).toBe(7);\n      expect(marks.length).toBe(0);\n    });\n\n    it('displays marks passed as prop', () => {\n      const { queryAllByTestId } = renderWithTheme(\n        <Slider\n          defaultValue={0}\n          marks={[\n            { value: 0, label: 'zero' },\n            { value: 20, label: 'twenty' },\n            { value: 30, label: 'thirty' }\n          ]}\n        />\n      );\n      const marks = queryAllByTestId('mark');\n\n      expect(marks[0].textContent).toBe('zero');\n      expect(marks[1].textContent).toBe('twenty');\n      expect(marks[2].textContent).toBe('thirty');\n    });\n  });\n});\n"
  },
  {
    "path": "src/Slider/Slider.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { ScrollView, Slider, SliderOnChangeHandler } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  background: ${({ theme }) => theme.material};\n  padding: 5rem;\n\n  .col {\n    display: flex;\n    flex-direction: column;\n    justify-content: space-around;\n  }\n  .row {\n    display: flex;\n    & > *:first-child {\n      margin-right: 5rem;\n    }\n  }\n  #cutout {\n    width: 400px;\n  }\n  #cutout > div {\n    background: ${({ theme }) => theme.canvas};\n    padding: 1rem;\n\n    & > * {\n      margin-bottom: 2rem;\n    }\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n  }\n`;\n\nexport default {\n  title: 'Controls/Slider',\n  component: Slider,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Slider>;\n\nexport function Default() {\n  const [state, setState] = React.useState(0);\n\n  const onChange: SliderOnChangeHandler = newValue => setState(newValue);\n\n  return (\n    <div className='row'>\n      <div className='col'>\n        <Slider size='300px' defaultValue={30} />\n        <br />\n        <Slider\n          disabled\n          size='300px'\n          min={0}\n          max={6}\n          step={1.5}\n          defaultValue={0}\n          marks={[\n            { value: 0, label: '0°C' },\n            { value: 2, label: '2°C' },\n            { value: 4, label: '4°C' },\n            { value: 6, label: '6°C' }\n          ]}\n        />\n        <br />\n        <Slider\n          size='300px'\n          min={0}\n          max={6}\n          step={1}\n          value={state}\n          onChange={onChange}\n          marks={[\n            { value: 0, label: '0°C' },\n            { value: 2, label: '2°C' },\n            { value: 4, label: '4°C' },\n            { value: 6, label: '6°C' }\n          ]}\n        />\n        <br />\n        <Slider\n          size='300px'\n          min={1.35}\n          max={6}\n          step={null}\n          marks={[\n            { value: 1.35, label: '1.35°C' },\n            { value: 2.75, label: '2.75°C' },\n            { value: 6, label: '6°C' }\n          ]}\n        />\n        <br />\n      </div>\n      <div className='col'>\n        <div>\n          <Slider\n            size='300px'\n            min={0}\n            max={6}\n            step={1}\n            defaultValue={0}\n            marks={[\n              { value: 0, label: '0°C' },\n              { value: 2, label: '2°C' },\n              { value: 4, label: '4°C' },\n              { value: 6, label: '6°C' }\n            ]}\n            orientation='vertical'\n          />\n        </div>\n      </div>\n    </div>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n\nexport function Flat() {\n  return (\n    <ScrollView id='cutout'>\n      <p>\n        When you want to add input field on a light background (like scrollable\n        content), just use the flat variant:\n      </p>\n      <Slider\n        variant='flat'\n        size='300px'\n        min={0}\n        max={6}\n        step={1}\n        defaultValue={0}\n        marks={[\n          { value: 0, label: '0°C' },\n          { value: 6, label: '6°C' }\n        ]}\n      />\n      <Slider\n        disabled\n        variant='flat'\n        size='300px'\n        min={0}\n        max={6}\n        step={1}\n        defaultValue={0}\n        marks={[\n          { value: 0, label: '0°C' },\n          { value: 6, label: '6°C' }\n        ]}\n      />\n    </ScrollView>\n  );\n}\n\nFlat.story = {\n  name: 'flat'\n};\n"
  },
  {
    "path": "src/Slider/Slider.tsx",
    "content": "// helper functions and event handling basically copied from Material UI (https://github.com/mui-org/material-ui) Slider component\nimport React, {\n  forwardRef,\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\n  useState\n} from 'react';\nimport styled, { css } from 'styled-components';\n\nimport {\n  createBorderStyles,\n  createBoxStyles,\n  createDisabledTextStyles,\n  createFlatBoxStyles,\n  createHatchedBackground\n} from '../common';\nimport useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';\nimport useEventCallback from '../common/hooks/useEventCallback';\nimport useForkRef from '../common/hooks/useForkRef';\nimport { useIsFocusVisible } from '../common/hooks/useIsFocusVisible';\nimport { clamp, getSize, roundValueToStep } from '../common/utils';\nimport { StyledScrollView } from '../ScrollView/ScrollView';\nimport { CommonStyledProps } from '../types';\n\nexport type SliderOnChangeHandler = (value: number) => void;\n\ntype SliderProps = {\n  defaultValue?: number;\n  disabled?: boolean;\n  marks?: boolean | { label?: string; value: number }[];\n  max?: number;\n  min?: number;\n  name?: string;\n  onChange?: SliderOnChangeHandler;\n  onChangeCommitted?: SliderOnChangeHandler;\n  onMouseDown?: (event: React.MouseEvent<HTMLDivElement>) => void;\n  orientation?: 'horizontal' | 'vertical';\n  size?: string | number;\n  step?: number | null;\n  value?: number;\n  variant?: 'default' | 'flat';\n} & Omit<\n  React.HTMLAttributes<HTMLDivElement>,\n  'defaultValue' | 'onChange' | 'onMouseDown'\n> &\n  CommonStyledProps;\n\nfunction percentToValue(percent: number, min: number, max: number) {\n  return (max - min) * percent + min;\n}\n\nfunction trackFinger(\n  event: MouseEvent | React.MouseEvent<HTMLDivElement> | TouchEvent,\n  touchId: number | undefined\n) {\n  if (touchId !== undefined && 'changedTouches' in event) {\n    for (let i = 0; i < event.changedTouches.length; i += 1) {\n      const touch = event.changedTouches[i];\n      if (touch.identifier === touchId) {\n        return {\n          x: touch.clientX,\n          y: touch.clientY\n        };\n      }\n    }\n\n    return false;\n  }\n\n  if ('clientX' in event) {\n    return {\n      x: event.clientX,\n      y: event.clientY\n    };\n  }\n\n  return false;\n}\n\nfunction ownerDocument(node?: Element) {\n  return (node && node.ownerDocument) || document;\n}\n\nfunction findClosest(values: number[], currentValue: number) {\n  const { index: closestIndex } =\n    values.reduce<{\n      distance: number;\n      index: number;\n    } | null>((acc, value, index) => {\n      const distance = Math.abs(currentValue - value);\n\n      if (\n        acc === null ||\n        distance < acc.distance ||\n        distance === acc.distance\n      ) {\n        return {\n          distance,\n          index\n        };\n      }\n\n      return acc;\n    }, null) ?? {};\n\n  return closestIndex ?? -1;\n}\n\ntype StyledSliderProps = Pick<\n  SliderProps,\n  'orientation' | 'size' | 'variant'\n> & {\n  $disabled?: boolean;\n  hasMarks?: boolean;\n  isFocused?: boolean;\n};\n\nconst Wrapper = styled.div<StyledSliderProps>`\n  display: inline-block;\n  position: relative;\n  touch-action: none;\n  &:before {\n    content: '';\n    display: inline-block;\n    position: absolute;\n    top: -2px;\n    left: -15px;\n    width: calc(100% + 30px);\n    height: ${({ hasMarks }) => (hasMarks ? '41px' : '39px')};\n    ${({ isFocused, theme }) =>\n      isFocused &&\n      `\n        outline: 2px dotted ${theme.materialText};\n        `}\n  }\n\n  ${({ orientation, size }) =>\n    orientation === 'vertical'\n      ? css<StyledSliderProps>`\n          height: ${size};\n          margin-right: 1.5rem;\n          &:before {\n            left: -6px;\n            top: -15px;\n            height: calc(100% + 30px);\n            width: ${({ hasMarks }) => (hasMarks ? '41px' : '39px')};\n          }\n        `\n      : css<StyledSliderProps>`\n          width: ${size};\n          margin-bottom: 1.5rem;\n          &:before {\n            top: -2px;\n            left: -15px;\n            width: calc(100% + 30px);\n            height: ${({ hasMarks }) => (hasMarks ? '41px' : '39px')};\n          }\n        `}\n\n  pointer-events: ${({ $disabled }) => ($disabled ? 'none' : 'auto')};\n`;\nconst sharedGrooveStyles = () => css<StyledSliderProps>`\n  position: absolute;\n  ${({ orientation }) =>\n    orientation === 'vertical'\n      ? css`\n          bottom: 0;\n          left: 50%;\n          transform: translateX(-50%);\n          height: 100%;\n          width: 8px;\n        `\n      : css`\n          left: 0;\n          top: 50%;\n          transform: translateY(-50%);\n          height: 8px;\n          width: 100%;\n        `}\n`;\nconst StyledGroove = styled(StyledScrollView)<StyledSliderProps>`\n  ${sharedGrooveStyles()}\n`;\nconst StyledFlatGroove = styled(StyledScrollView)<StyledSliderProps>`\n  ${sharedGrooveStyles()}\n\n  border-left-color: ${({ theme }) => theme.flatLight};\n  border-top-color: ${({ theme }) => theme.flatLight};\n  border-right-color: ${({ theme }) => theme.canvas};\n  border-bottom-color: ${({ theme }) => theme.canvas};\n  &:before {\n    border-left-color: ${({ theme }) => theme.flatDark};\n    border-top-color: ${({ theme }) => theme.flatDark};\n    border-right-color: ${({ theme }) => theme.flatLight};\n    border-bottom-color: ${({ theme }) => theme.flatLight};\n  }\n`;\nconst Thumb = styled.span<StyledSliderProps>`\n  position: relative;\n  ${({ orientation }) =>\n    orientation === 'vertical'\n      ? css`\n          width: 32px;\n          height: 18px;\n          right: 2px;\n          transform: translateY(-50%);\n        `\n      : css`\n          height: 32px;\n          width: 18px;\n          top: 2px;\n          transform: translateX(-50%);\n        `}\n  ${({ variant }) =>\n    variant === 'flat'\n      ? css`\n          ${createFlatBoxStyles()}\n          outline: 2px solid ${({ theme }) => theme.flatDark};\n          background: ${({ theme }) => theme.flatLight};\n        `\n      : css`\n          ${createBoxStyles()}\n          ${createBorderStyles()}\n          &:focus {\n            outline: none;\n          }\n        `}\n    ${({ $disabled, theme }) =>\n    $disabled &&\n    createHatchedBackground({\n      mainColor: theme.material,\n      secondaryColor: theme.borderLightest\n    })}\n`;\n\nconst tickHeight = 6;\nconst Tick = styled.span<StyledSliderProps>`\n  display: inline-block;\n  position: absolute;\n\n  ${({ orientation }) =>\n    orientation === 'vertical'\n      ? css`\n          right: ${-tickHeight - 2}px;\n          bottom: 0px;\n          transform: translateY(1px);\n          width: ${tickHeight}px;\n          border-bottom: 2px solid ${({ theme }) => theme.materialText};\n        `\n      : css`\n          bottom: ${-tickHeight}px;\n          height: ${tickHeight}px;\n          transform: translateX(-1px);\n          border-left: 1px solid ${({ theme }) => theme.materialText};\n          border-right: 1px solid ${({ theme }) => theme.materialText};\n        `}\n\n  color:  ${({ theme }) => theme.materialText};\n  ${({ $disabled, theme }) =>\n    $disabled &&\n    css`\n      ${createDisabledTextStyles()}\n      box-shadow: 1px 1px 0px ${theme.materialTextDisabledShadow};\n      border-color: ${theme.materialTextDisabled};\n    `}\n`;\nconst Mark = styled.div<StyledSliderProps>`\n  position: absolute;\n  bottom: 0;\n  left: 0;\n  line-height: 1;\n  font-size: 0.875rem;\n\n  ${({ orientation }) =>\n    orientation === 'vertical'\n      ? css`\n          transform: translate(${tickHeight + 2}px, ${tickHeight + 1}px);\n        `\n      : css`\n          transform: translate(-0.5ch, calc(100% + 2px));\n        `}\n`;\n\nconst Slider = forwardRef<HTMLDivElement, SliderProps>(\n  (\n    {\n      defaultValue,\n      disabled = false,\n      marks: marksProp = false,\n      max = 100,\n      min = 0,\n      name,\n      onChange,\n      onChangeCommitted,\n      onMouseDown,\n      orientation = 'horizontal',\n      size = '100%',\n      step = 1,\n      value,\n      variant = 'default',\n      ...otherProps\n    },\n    ref\n  ) => {\n    const Groove = variant === 'flat' ? StyledFlatGroove : StyledGroove;\n    const vertical = orientation === 'vertical';\n    const [valueDerived = min, setValueState] = useControlledOrUncontrolled({\n      defaultValue,\n      onChange: onChange ?? onChangeCommitted,\n      value\n    });\n\n    const {\n      isFocusVisible,\n      onBlurVisible,\n      ref: focusVisibleRef\n    } = useIsFocusVisible<HTMLDivElement>();\n    const [focusVisible, setFocusVisible] = useState(false);\n    const sliderRef = useRef<HTMLDivElement>();\n    const thumbRef = useRef<HTMLSpanElement | null>(null);\n    const handleFocusRef = useForkRef(focusVisibleRef, sliderRef);\n    const handleRef = useForkRef(ref, handleFocusRef);\n\n    const handleFocus = useEventCallback(\n      (event: React.FocusEvent<HTMLSpanElement>) => {\n        if (isFocusVisible(event)) {\n          setFocusVisible(true);\n        }\n      }\n    );\n\n    const handleBlur = useEventCallback(() => {\n      if (focusVisible !== false) {\n        setFocusVisible(false);\n        onBlurVisible();\n      }\n    });\n\n    const touchId = useRef<number>();\n\n    const marks = useMemo(\n      () =>\n        marksProp === true && Number.isFinite(step)\n          ? [...Array(Math.round((max - min) / (step as number)) + 1)].map(\n              (_, index) => ({\n                label: undefined,\n                value: min + (step as number) * index\n              })\n            )\n          : Array.isArray(marksProp)\n          ? marksProp\n          : [],\n      [marksProp, max, min, step]\n    );\n\n    const handleKeyDown = useEventCallback(\n      (event: React.KeyboardEvent<HTMLSpanElement>) => {\n        const tenPercents = (max - min) / 10;\n        const marksValues = marks.map(mark => mark.value);\n        const marksIndex = marksValues.indexOf(valueDerived);\n        let newValue = 0;\n\n        switch (event.key) {\n          case 'Home':\n            newValue = min;\n            break;\n          case 'End':\n            newValue = max;\n            break;\n          case 'PageUp':\n            if (step) {\n              newValue = valueDerived + tenPercents;\n            }\n            break;\n          case 'PageDown':\n            if (step) {\n              newValue = valueDerived - tenPercents;\n            }\n            break;\n          case 'ArrowRight':\n          case 'ArrowUp':\n            if (step) {\n              newValue = valueDerived + step;\n            } else {\n              newValue =\n                marksValues[marksIndex + 1] ||\n                marksValues[marksValues.length - 1];\n            }\n            break;\n          case 'ArrowLeft':\n          case 'ArrowDown':\n            if (step) {\n              newValue = valueDerived - step;\n            } else {\n              newValue = marksValues[marksIndex - 1] || marksValues[0];\n            }\n            break;\n          default:\n            return;\n        }\n\n        // Prevent scroll of the page\n        event.preventDefault();\n        if (step) {\n          newValue = roundValueToStep(newValue, step, min);\n        }\n\n        newValue = clamp(newValue, min, max);\n\n        setValueState(newValue);\n        setFocusVisible(true);\n\n        onChange?.(newValue);\n        onChangeCommitted?.(newValue);\n      }\n    );\n\n    const getNewValue = useCallback(\n      (finger: { x: number; y: number }) => {\n        if (!sliderRef.current) {\n          return 0;\n        }\n        const rect = sliderRef.current.getBoundingClientRect();\n\n        let percent;\n        if (vertical) {\n          percent = (rect.bottom - finger.y) / rect.height;\n        } else {\n          percent = (finger.x - rect.left) / rect.width;\n        }\n        let newValue;\n\n        newValue = percentToValue(percent, min, max);\n        if (step) {\n          newValue = roundValueToStep(newValue, step, min);\n        } else {\n          const marksValues = marks.map(mark => mark.value);\n          const closestIndex = findClosest(marksValues, newValue);\n          newValue = marksValues[closestIndex];\n        }\n        newValue = clamp(newValue, min, max);\n        return newValue;\n      },\n      [marks, max, min, step, vertical]\n    );\n\n    const handleTouchMove = useEventCallback(\n      (event: MouseEvent | TouchEvent) => {\n        const finger = trackFinger(event, touchId.current);\n\n        if (!finger) {\n          return;\n        }\n        const newValue = getNewValue(finger);\n\n        thumbRef.current?.focus();\n        setValueState(newValue);\n        setFocusVisible(true);\n\n        onChange?.(newValue);\n      }\n    );\n\n    const handleTouchEnd = useEventCallback(\n      (event: MouseEvent | TouchEvent) => {\n        const finger = trackFinger(event, touchId.current);\n\n        if (!finger) {\n          return;\n        }\n\n        const newValue = getNewValue(finger);\n\n        onChangeCommitted?.(newValue);\n\n        touchId.current = undefined;\n\n        const doc = ownerDocument(sliderRef.current);\n        doc.removeEventListener('mousemove', handleTouchMove);\n        doc.removeEventListener('mouseup', handleTouchEnd);\n        doc.removeEventListener('touchmove', handleTouchMove);\n        doc.removeEventListener('touchend', handleTouchEnd);\n      }\n    );\n\n    const handleMouseDown = useEventCallback(\n      (event: React.MouseEvent<HTMLDivElement>) => {\n        // TODO should we also pass event together with new value to callbacks? (same thing with other input components)\n        onMouseDown?.(event);\n\n        event.preventDefault();\n        thumbRef.current?.focus();\n        setFocusVisible(true);\n\n        const finger = trackFinger(event, touchId.current);\n        if (finger) {\n          const newValue = getNewValue(finger);\n          setValueState(newValue);\n          onChange?.(newValue);\n        }\n\n        const doc = ownerDocument(sliderRef.current);\n        doc.addEventListener('mousemove', handleTouchMove);\n        doc.addEventListener('mouseup', handleTouchEnd);\n      }\n    );\n\n    const handleTouchStart = useEventCallback((event: TouchEvent) => {\n      // Workaround as Safari has partial support for touchAction: 'none'.\n      event.preventDefault();\n      const touch = event.changedTouches[0];\n      if (touch != null) {\n        // A number that uniquely identifies the current finger in the touch session.\n        touchId.current = touch.identifier;\n      }\n\n      thumbRef.current?.focus();\n      setFocusVisible(true);\n\n      const finger = trackFinger(event, touchId.current);\n      if (finger) {\n        const newValue = getNewValue(finger);\n        setValueState(newValue);\n        onChange?.(newValue);\n      }\n\n      const doc = ownerDocument(sliderRef.current);\n      doc.addEventListener('touchmove', handleTouchMove);\n      doc.addEventListener('touchend', handleTouchEnd);\n    });\n\n    useEffect(() => {\n      const { current: slider } = sliderRef;\n      slider?.addEventListener('touchstart', handleTouchStart);\n      const doc = ownerDocument(slider);\n\n      return () => {\n        slider?.removeEventListener('touchstart', handleTouchStart);\n        doc.removeEventListener('mousemove', handleTouchMove);\n        doc.removeEventListener('mouseup', handleTouchEnd);\n        doc.removeEventListener('touchmove', handleTouchMove);\n        doc.removeEventListener('touchend', handleTouchEnd);\n      };\n    }, [handleTouchEnd, handleTouchMove, handleTouchStart]);\n\n    return (\n      <Wrapper\n        $disabled={disabled}\n        hasMarks={Boolean(marks.length)}\n        isFocused={focusVisible}\n        onMouseDown={handleMouseDown}\n        orientation={orientation}\n        ref={handleRef}\n        size={getSize(size)}\n        {...otherProps}\n      >\n        {/* should we keep the hidden input ? */}\n        <input\n          disabled={disabled}\n          name={name}\n          type='hidden'\n          value={valueDerived ?? 0}\n        />\n        {marks &&\n          marks.map(m => (\n            <Tick\n              $disabled={disabled}\n              data-testid='tick'\n              key={(m.value / (max - min)) * 100}\n              orientation={orientation}\n              style={{\n                [vertical ? 'bottom' : 'left']: `${\n                  ((m.value - min) / (max - min)) * 100\n                }%`\n              }}\n            >\n              {m.label && (\n                <Mark aria-hidden data-testid='mark' orientation={orientation}>\n                  {m.label}\n                </Mark>\n              )}\n            </Tick>\n          ))}\n        <Groove orientation={orientation} variant={variant} />\n        <Thumb\n          $disabled={disabled}\n          aria-disabled={disabled ? true : undefined}\n          aria-orientation={orientation}\n          aria-valuemax={max}\n          aria-valuemin={min}\n          aria-valuenow={valueDerived}\n          onBlur={handleBlur}\n          onFocus={handleFocus}\n          onKeyDown={handleKeyDown}\n          orientation={orientation}\n          ref={thumbRef}\n          role='slider'\n          style={{\n            [vertical ? 'bottom' : 'left']: `${\n              (vertical ? -100 : 0) + (100 * (valueDerived - min)) / (max - min)\n            }%`\n          }}\n          tabIndex={disabled ? undefined : 0}\n          variant={variant}\n        />\n      </Wrapper>\n    );\n  }\n);\n\nSlider.displayName = 'Slider';\n\nexport { Slider, SliderProps };\n"
  },
  {
    "path": "src/Table/Table.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Table } from './Table';\n\ndescribe('<Table />', () => {\n  it('renders Table', () => {\n    const { container } = renderWithTheme(<Table />);\n    const table = container.firstElementChild;\n\n    expect(table).toBeInTheDocument();\n  });\n  it('renders table element', () => {\n    const { getByRole } = renderWithTheme(<Table />);\n\n    expect(getByRole('table')).toBeInTheDocument();\n  });\n  it('renders children', () => {\n    const { getByTestId } = renderWithTheme(\n      <Table>\n        <tbody data-testid='children' />\n      </Table>\n    );\n    expect(getByTestId('children')).toBeInTheDocument();\n  });\n});\n"
  },
  {
    "path": "src/Table/Table.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport {\n  Table,\n  TableBody,\n  TableDataCell,\n  TableHead,\n  TableHeadCell,\n  TableRow,\n  Window,\n  WindowContent,\n  WindowHeader\n} from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n`;\n\nexport default {\n  title: 'Controls/Table',\n  component: Table,\n  subcomponents: {\n    Table,\n    TableBody,\n    TableHead,\n    TableRow,\n    TableHeadCell,\n    TableDataCell\n  },\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Table>;\n\nexport function Default() {\n  return (\n    <Window style={{ width: 320 }}>\n      <WindowHeader>Pokedex.exe</WindowHeader>\n      <WindowContent>\n        <Table>\n          <TableHead>\n            <TableRow>\n              <TableHeadCell>Type</TableHeadCell>\n              <TableHeadCell>Name</TableHeadCell>\n              <TableHeadCell disabled>Level</TableHeadCell>\n            </TableRow>\n          </TableHead>\n          <TableBody>\n            <TableRow>\n              <TableDataCell style={{ textAlign: 'center' }}>\n                <span role='img' aria-label='LEAF'>\n                  🌿\n                </span>\n              </TableDataCell>\n              <TableDataCell>Bulbasaur</TableDataCell>\n              <TableDataCell>64</TableDataCell>\n            </TableRow>\n            <TableRow>\n              <TableDataCell style={{ textAlign: 'center' }}>\n                <span role='img' aria-label='fire'>\n                  🔥\n                </span>\n              </TableDataCell>\n              <TableDataCell>Charizard</TableDataCell>\n              <TableDataCell>209</TableDataCell>\n            </TableRow>\n            <TableRow>\n              <TableDataCell style={{ textAlign: 'center' }}>\n                <span role='img' aria-label='lightning'>\n                  ⚡\n                </span>\n              </TableDataCell>\n              <TableDataCell>Pikachu</TableDataCell>\n              <TableDataCell>82</TableDataCell>\n            </TableRow>\n          </TableBody>\n        </Table>\n      </WindowContent>\n    </Window>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/Table/Table.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { StyledScrollView } from '../ScrollView/ScrollView';\nimport { CommonStyledProps } from '../types';\n\ntype TableProps = {\n  children?: React.ReactNode;\n} & React.TableHTMLAttributes<HTMLTableElement> &\n  CommonStyledProps;\n\nconst StyledTable = styled.table`\n  display: table;\n  width: 100%;\n  border-collapse: collapse;\n  border-spacing: 0;\n  font-size: 1rem;\n`;\n\nconst Wrapper = styled(StyledScrollView)`\n  &:before {\n    box-shadow: none;\n  }\n`;\n\nconst Table = forwardRef<HTMLTableElement, TableProps>(\n  ({ children, ...otherProps }, ref) => {\n    return (\n      <Wrapper>\n        <StyledTable ref={ref} {...otherProps}>\n          {children}\n        </StyledTable>\n      </Wrapper>\n    );\n  }\n);\n\nTable.displayName = 'Table';\n\nexport * from './TableBody';\nexport * from './TableDataCell';\nexport * from './TableHead';\nexport * from './TableHeadCell';\nexport * from './TableRow';\n\nexport { Table, TableProps };\n"
  },
  {
    "path": "src/Table/TableBody.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { TableBody } from './TableBody';\n\ndescribe('<TableBody />', () => {\n  function mountInTable(node: React.ReactNode) {\n    const { container, getByTestId } = renderWithTheme(<table>{node}</table>);\n    return {\n      tbody: container.querySelector('table')?.firstElementChild,\n      getByTestId\n    };\n  }\n\n  it('renders TableBody', () => {\n    const { tbody } = mountInTable(<TableBody />);\n\n    expect(tbody).toBeInTheDocument();\n    expect(tbody?.tagName).toBe('TBODY');\n  });\n\n  it('renders children', () => {\n    const children = <tr data-testid='tr' />;\n    const { getByTestId } = mountInTable(<TableBody>{children}</TableBody>);\n    expect(getByTestId('tr')).toBeInTheDocument();\n  });\n});\n"
  },
  {
    "path": "src/Table/TableBody.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { insetShadow } from '../common';\nimport { CommonStyledProps } from '../types';\n\ntype TableBodyProps = {\n  children?: React.ReactNode;\n} & React.HTMLAttributes<HTMLTableSectionElement> &\n  CommonStyledProps;\n\nconst StyledTableBody = styled.tbody`\n  background: ${({ theme }) => theme.canvas};\n  display: table-row-group;\n  box-shadow: ${insetShadow};\n  overflow-y: auto;\n`;\n\nconst TableBody = forwardRef<HTMLTableSectionElement, TableBodyProps>(\n  function TableBody({ children, ...otherProps }, ref) {\n    return (\n      <StyledTableBody ref={ref} {...otherProps}>\n        {children}\n      </StyledTableBody>\n    );\n  }\n);\n\nTableBody.displayName = 'TableBody';\n\nexport { TableBody, TableBodyProps };\n"
  },
  {
    "path": "src/Table/TableDataCell.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { TableDataCell } from './TableDataCell';\n\ndescribe('<TableDataCell />', () => {\n  function mountInTable(node: React.ReactNode) {\n    const { container, getByText } = renderWithTheme(\n      <table>\n        <tbody>\n          <tr>{node}</tr>\n        </tbody>\n      </table>\n    );\n    return {\n      td: container.querySelector('tr')?.firstElementChild,\n      getByText\n    };\n  }\n\n  it('renders TableDataCell', () => {\n    const { td } = mountInTable(<TableDataCell />);\n    expect(td?.tagName).toBe('TD');\n  });\n\n  it('renders children', () => {\n    const { getByText } = mountInTable(<TableDataCell>children</TableDataCell>);\n    expect(getByText('children')).toBeInTheDocument();\n  });\n});\n"
  },
  {
    "path": "src/Table/TableDataCell.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { CommonStyledProps } from '../types';\n\ntype TableDataCellProps = {\n  children?: React.ReactNode;\n} & React.HTMLAttributes<HTMLTableCellElement> &\n  CommonStyledProps;\n\nconst StyledTd = styled.td`\n  padding: 0 8px;\n`;\n\nconst TableDataCell = forwardRef<HTMLTableCellElement, TableDataCellProps>(\n  function TableDataCell({ children, ...otherProps }, ref) {\n    return (\n      <StyledTd ref={ref} {...otherProps}>\n        {children}\n      </StyledTd>\n    );\n  }\n);\n\nTableDataCell.displayName = 'TableDataCell';\n\nexport { TableDataCell, TableDataCellProps };\n"
  },
  {
    "path": "src/Table/TableHead.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { TableHead } from './TableHead';\n\ndescribe('<TableHead />', () => {\n  function mountInTable(node: React.ReactNode) {\n    const { container, getByTestId } = renderWithTheme(<table>{node}</table>);\n    return {\n      tbody: container.querySelector('table')?.firstElementChild,\n      getByTestId\n    };\n  }\n\n  it('renders TableHead', () => {\n    const { tbody } = mountInTable(<TableHead />);\n\n    expect(tbody).toBeInTheDocument();\n    expect(tbody?.tagName).toBe('THEAD');\n  });\n\n  it('renders children', () => {\n    const children = <tr data-testid='tr' />;\n    const { getByTestId } = mountInTable(<TableHead>{children}</TableHead>);\n    expect(getByTestId('tr')).toBeInTheDocument();\n  });\n});\n"
  },
  {
    "path": "src/Table/TableHead.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { CommonStyledProps } from '../types';\n\ntype TableHeadProps = {\n  children?: React.ReactNode;\n} & React.HTMLAttributes<HTMLTableSectionElement> &\n  CommonStyledProps;\n\nconst StyledTableHead = styled.thead`\n  display: table-header-group;\n`;\nconst TableHead = forwardRef<HTMLTableSectionElement, TableHeadProps>(\n  function TableHead({ children, ...otherProps }, ref) {\n    return (\n      <StyledTableHead ref={ref} {...otherProps}>\n        {children}\n      </StyledTableHead>\n    );\n  }\n);\n\nTableHead.displayName = 'TableHead';\n\nexport { TableHead, TableHeadProps };\n"
  },
  {
    "path": "src/Table/TableHeadCell.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { TableHeadCell } from './TableHeadCell';\n\ndescribe('<TableHeadCell />', () => {\n  function mountInTable(node: React.ReactNode) {\n    const { container, getByText } = renderWithTheme(\n      <table>\n        <thead>\n          <tr>{node}</tr>\n        </thead>\n      </table>\n    );\n    return {\n      th: container.querySelector('tr')?.firstElementChild as HTMLElement,\n      getByText\n    };\n  }\n\n  it('renders TableHeadCell', () => {\n    const { th } = mountInTable(<TableHeadCell />);\n    expect(th?.tagName).toBe('TH');\n  });\n\n  it('renders children', () => {\n    const { getByText } = mountInTable(<TableHeadCell>children</TableHeadCell>);\n    expect(getByText('children')).toBeInTheDocument();\n  });\n\n  describe('prop: sort', () => {\n    it('should render without aria-sort attribute by default', () => {\n      const { th } = mountInTable(<TableHeadCell />);\n      expect(th).not.toHaveAttribute('aria-sort');\n    });\n\n    it('should render aria-sort=\"ascending\" when prop sort=\"asc\" provided', () => {\n      const { th } = mountInTable(<TableHeadCell sort='asc' />);\n      expect(th).toHaveAttribute('aria-sort', 'ascending');\n    });\n\n    it('should render aria-sort=\"descending\" when prop sort=\"desc\" provided', () => {\n      const { th } = mountInTable(<TableHeadCell sort='desc' />);\n      expect(th).toHaveAttribute('aria-sort', 'descending');\n    });\n  });\n\n  describe('prop: disabled', () => {\n    it('should disable th element', () => {\n      const handleChange = jest.fn();\n\n      const { th } = mountInTable(\n        <TableHeadCell disabled onChange={handleChange} />\n      );\n      expect(th).toHaveAttribute('aria-disabled', 'true');\n\n      th?.click?.();\n      expect(handleChange).not.toHaveBeenCalled();\n    });\n  });\n});\n"
  },
  {
    "path": "src/Table/TableHeadCell.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { createBorderStyles, createDisabledTextStyles } from '../common';\nimport { noOp } from '../common/utils';\nimport { CommonStyledProps } from '../types';\n\ntype TableHeadCellProps = {\n  children?: React.ReactNode;\n  disabled?: boolean;\n  sort?: 'asc' | 'desc' | null;\n} & React.TdHTMLAttributes<HTMLTableCellElement> &\n  CommonStyledProps;\n\nconst StyledHeadCell = styled.th<{ $disabled: boolean }>`\n  position: relative;\n  padding: 0 8px;\n  display: table-cell;\n  vertical-align: inherit;\n  background: ${({ theme }) => theme.material};\n  cursor: default;\n  user-select: none;\n  &:before {\n    box-sizing: border-box;\n    content: '';\n    position: absolute;\n    left: 0;\n    top: 0;\n    width: 100%;\n    height: 100%;\n    ${createBorderStyles()}\n\n    border-left: none;\n    border-top: none;\n  }\n  ${({ $disabled }) =>\n    !$disabled &&\n    css`\n      &:active {\n        &:before {\n          ${createBorderStyles({ invert: true, style: 'window' })}\n          border-left: none;\n          border-top: none;\n          padding-top: 2px;\n        }\n\n        & > div {\n          position: relative;\n          top: 2px;\n        }\n      }\n    `}\n\n  color: ${({ theme }) => theme.materialText};\n  ${({ $disabled }) => $disabled && createDisabledTextStyles()}\n  &:hover {\n    color: ${({ theme }) => theme.materialText};\n    ${({ $disabled }) => $disabled && createDisabledTextStyles()}\n  }\n`;\n\nconst TableHeadCell = forwardRef<HTMLTableCellElement, TableHeadCellProps>(\n  function TableHeadCell(\n    {\n      disabled = false,\n      children,\n      onClick,\n      onTouchStart = noOp,\n      sort,\n      ...otherProps\n    },\n    ref\n  ) {\n    const ariaSort =\n      sort === 'asc' ? 'ascending' : sort === 'desc' ? 'descending' : undefined;\n    return (\n      <StyledHeadCell\n        $disabled={disabled}\n        aria-disabled={disabled}\n        aria-sort={ariaSort}\n        onClick={disabled ? undefined : onClick}\n        onTouchStart={disabled ? undefined : onTouchStart}\n        ref={ref}\n        {...otherProps}\n      >\n        <div>{children}</div>\n      </StyledHeadCell>\n    );\n  }\n);\n\nTableHeadCell.displayName = 'TableHeadCell';\n\nexport { TableHeadCell, TableHeadCellProps };\n"
  },
  {
    "path": "src/Table/TableRow.spec.tsx",
    "content": "import React from 'react';\nimport { renderWithTheme } from '../../test/utils';\n\nimport { TableRow } from './TableRow';\n\ndescribe('<TableRow />', () => {\n  function mountInTable(node: React.ReactNode) {\n    const { container, getByTestId } = renderWithTheme(\n      <table>\n        <tbody>{node}</tbody>\n      </table>\n    );\n    return {\n      tr: container.querySelector('tbody')?.firstElementChild,\n      getByTestId\n    };\n  }\n\n  it('renders TableRow', () => {\n    const { tr } = mountInTable(<TableRow />);\n    expect(tr?.tagName).toBe('TR');\n  });\n\n  it('renders children', () => {\n    const children = <td data-testid='td' />;\n    const { getByTestId } = mountInTable(<TableRow>{children}</TableRow>);\n    expect(getByTestId('td')).toBeInTheDocument();\n  });\n});\n"
  },
  {
    "path": "src/Table/TableRow.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { blockSizes } from '../common/system';\n\ntype TableRowProps = {\n  children?: React.ReactNode;\n} & React.HTMLAttributes<HTMLTableRowElement>;\n\nconst StyledTr = styled.tr`\n  color: inherit;\n  display: table-row;\n  height: calc(${blockSizes.md} - 2px);\n  line-height: calc(${blockSizes.md} - 2px);\n  vertical-align: middle;\n  outline: none;\n\n  color: ${({ theme }) => theme.canvasText};\n  &:hover {\n    background: ${({ theme }) => theme.hoverBackground};\n    color: ${({ theme }) => theme.canvasTextInvert};\n  }\n`;\n\nconst TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(\n  function TableRow({ children, ...otherProps }, ref) {\n    return (\n      <StyledTr ref={ref} {...otherProps}>\n        {children}\n      </StyledTr>\n    );\n  }\n);\n\nTableRow.displayName = 'TableRow';\n\nexport { TableRow, TableRowProps };\n"
  },
  {
    "path": "src/Tabs/Tab.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { Tab } from './Tab';\n\ndescribe('<Tab />', () => {\n  describe('prop: children', () => {\n    it('should render passed child', () => {\n      const child = 'Hey there!';\n      const { getByText } = renderWithTheme(<Tab>{child}</Tab>);\n\n      expect(getByText(child)).toBeInTheDocument();\n    });\n  });\n\n  describe('prop: selected', () => {\n    it('should render with correct aria attribute', () => {\n      const { getByRole } = renderWithTheme(<Tab selected />);\n\n      expect(getByRole('tab')).toHaveAttribute('aria-selected', 'true');\n    });\n  });\n\n  describe('prop: onClick', () => {\n    it('should be called when a click is triggered', () => {\n      const handleClick = jest.fn();\n      const { getByRole } = renderWithTheme(<Tab onClick={handleClick} />);\n\n      getByRole('tab').click();\n\n      expect(handleClick).toHaveBeenCalledTimes(1);\n    });\n  });\n});\n"
  },
  {
    "path": "src/Tabs/Tab.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\n\nimport { createBorderStyles, createBoxStyles, focusOutline } from '../common';\nimport { blockSizes } from '../common/system';\nimport { CommonStyledProps } from '../types';\n\ntype TabProps = {\n  children?: React.ReactNode;\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  onClick?: (value: any, event: React.MouseEvent<HTMLButtonElement>) => void;\n  selected?: boolean;\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  value?: any;\n} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'value'> &\n  CommonStyledProps;\n\nconst StyledTab = styled.button<TabProps>`\n  ${createBoxStyles()}\n  ${createBorderStyles()}\n  position: relative;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 1rem;\n  height: ${blockSizes.md};\n  line-height: ${blockSizes.md};\n  padding: 0 8px;\n  border-bottom: none;\n  border-top-left-radius: 5px;\n  border-top-right-radius: 5px;\n  margin: 0 0 -2px 0;\n  cursor: default;\n  color: ${({ theme }) => theme.materialText};\n  user-select: none;\n  font-family: inherit;\n  &:focus:after,\n  &:active:after {\n    content: '';\n    position: absolute;\n    left: 0;\n    top: 0;\n    width: 100%;\n    height: 100%;\n    ${focusOutline}\n    outline-offset: -6px;\n  }\n  ${props =>\n    props.selected &&\n    `\n    z-index: 1;\n    height: calc(${blockSizes.md} + 4px);\n    top: -4px;\n    margin-bottom: -6px;\n    padding: 0 16px;\n    margin-left: -8px;\n    &:not(:last-child) {\n      margin-right: -8px;\n    }\n  `}\n  &:before {\n    content: '';\n    position: absolute;\n    width: calc(100% - 4px);\n    height: 6px;\n    background: ${({ theme }) => theme.material};\n    bottom: -4px;\n    left: 2px;\n  }\n`;\n\nconst Tab = forwardRef<HTMLButtonElement, TabProps>(\n  ({ value, onClick, selected = false, children, ...otherProps }, ref) => {\n    return (\n      <StyledTab\n        aria-selected={selected}\n        selected={selected}\n        onClick={(e: React.MouseEvent<HTMLButtonElement>) =>\n          onClick?.(value, e)\n        }\n        ref={ref}\n        role='tab'\n        {...otherProps}\n      >\n        {children}\n      </StyledTab>\n    );\n  }\n);\n\nTab.displayName = 'Tab';\n\nexport { Tab, TabProps };\n"
  },
  {
    "path": "src/Tabs/TabBody.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { TabBody } from './TabBody';\n\ndescribe('<TabBody />', () => {\n  describe('prop: children', () => {\n    it('should render passed child', () => {\n      const child = 'Hey there!';\n      const { getByText } = renderWithTheme(<TabBody>{child}</TabBody>);\n\n      expect(getByText(child)).toBeInTheDocument();\n    });\n  });\n});\n"
  },
  {
    "path": "src/Tabs/TabBody.tsx",
    "content": "import React, { forwardRef } from 'react';\n\nimport styled from 'styled-components';\nimport { createBorderStyles, createBoxStyles } from '../common';\nimport { CommonStyledProps } from '../types';\n\ntype TabBodyProps = {\n  children: React.ReactNode;\n} & React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\nconst StyledTabBody = styled.div`\n  ${createBoxStyles()}\n  ${createBorderStyles()}\n  position: relative;\n  display: block;\n  height: 100%;\n  padding: 16px;\n  font-size: 1rem;\n`;\nconst TabBody = forwardRef<HTMLDivElement, TabBodyProps>(\n  ({ children, ...otherProps }, ref) => {\n    return (\n      <StyledTabBody ref={ref} {...otherProps}>\n        {children}\n      </StyledTabBody>\n    );\n  }\n);\n\nTabBody.displayName = 'TabBody';\n\nexport { TabBody, TabBodyProps };\n"
  },
  {
    "path": "src/Tabs/Tabs.spec.tsx",
    "content": "import { fireEvent } from '@testing-library/react';\nimport React from 'react';\n\nimport { Tab } from '..';\nimport { renderWithTheme } from '../../test/utils';\nimport { Tabs } from './Tabs';\n\ndescribe('<Tabs />', () => {\n  describe('prop: children', () => {\n    it('should accept a null child', () => {\n      const { getAllByRole } = renderWithTheme(\n        <Tabs value={0}>\n          {null}\n          <Tab />\n        </Tabs>\n      );\n      expect(getAllByRole('tab').length).toBe(1);\n    });\n  });\n\n  describe('prop: value', () => {\n    const tabs = (\n      <Tabs value={1}>\n        <Tab value={0} />\n        <Tab value={1} />\n      </Tabs>\n    );\n\n    it('should pass selected prop to children', () => {\n      const { getAllByRole } = renderWithTheme(tabs);\n      const tabElements = getAllByRole('tab');\n      expect(tabElements[0]).toHaveAttribute('aria-selected', 'false');\n      expect(tabElements[1]).toHaveAttribute('aria-selected', 'true');\n    });\n\n    it('should accept any value as selected tab value', () => {\n      const tab0 = {};\n      const tab1 = {};\n      expect(tab0).not.toBe(tab1);\n\n      const { getAllByRole } = renderWithTheme(\n        <Tabs value={tab0}>\n          <Tab value={tab0} />\n          <Tab value={tab1} />\n        </Tabs>\n      );\n      const tabElements = getAllByRole('tab');\n      expect(tabElements[0]).toHaveAttribute('aria-selected', 'true');\n      expect(tabElements[1]).toHaveAttribute('aria-selected', 'false');\n    });\n  });\n  describe('prop: onChange', () => {\n    it('should call onChange when clicking', () => {\n      const handleChange = jest.fn();\n      const { getAllByRole } = renderWithTheme(\n        <Tabs value={0} onChange={handleChange}>\n          <Tab value={0} />\n          <Tab value={1} />\n        </Tabs>\n      );\n\n      fireEvent.click(getAllByRole('tab')[1]);\n      expect(handleChange).toBeCalledTimes(1);\n      expect(handleChange.mock.calls[0][0]).toBe(1);\n    });\n  });\n\n  describe('prop: rows', () => {\n    it('should render specified number of rows', () => {\n      const tabs = (\n        <Tabs value={1} rows={4}>\n          {/* row 1 */}\n          <Tab value={0} />\n          <Tab value={1} />\n          <Tab value={3} />\n\n          {/* row 2 */}\n          <Tab value={4} />\n          <Tab value={5} />\n\n          {/* row 3  */}\n          <Tab value={6} />\n          <Tab value={7} />\n\n          {/* row 4 */}\n          <Tab value={8} />\n          <Tab value={9} />\n        </Tabs>\n      );\n      const { getAllByTestId } = renderWithTheme(tabs);\n      const rowElements = getAllByTestId('tab-row');\n      expect(rowElements.length).toBe(4);\n    });\n\n    it('row containing currently selected tab should be at the bottom (last row)', () => {\n      const tabs = (\n        <Tabs value={4} rows={3}>\n          <Tab value={0} />\n          <Tab value={1} />\n          <Tab value={3} />\n\n          <Tab value={4} />\n          <Tab value={5} />\n          <Tab value={6} />\n\n          <Tab value={7} />\n          <Tab value={8} />\n          <Tab value={9} />\n        </Tabs>\n      );\n      const { container, getAllByTestId } = renderWithTheme(tabs);\n      const rowElements = getAllByTestId('tab-row');\n      const selectedTab = container.querySelector('[aria-selected=true]');\n\n      expect(rowElements?.pop()?.contains(selectedTab)).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "src/Tabs/Tabs.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport {\n  Anchor,\n  Checkbox,\n  GroupBox,\n  NumberInput,\n  Tab,\n  TabBody,\n  Tabs,\n  Window,\n  WindowContent,\n  WindowHeader\n} from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n`;\n\nexport default {\n  title: 'Controls/Tabs',\n  component: Tabs,\n  subcomponents: { Tab, TabBody },\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Tabs>;\n\nexport function Default() {\n  const [state, setState] = useState({\n    activeTab: 0\n  });\n\n  const handleChange = (\n    value: number,\n    event: React.MouseEvent<HTMLButtonElement>\n  ) => {\n    console.log({ value, event });\n    setState({ activeTab: value });\n  };\n\n  const { activeTab } = state;\n  return (\n    <Window style={{ width: 350 }}>\n      <WindowHeader>store.exe</WindowHeader>\n      <WindowContent>\n        <Tabs value={activeTab} onChange={handleChange}>\n          <Tab value={0}>Shoes</Tab>\n          <Tab value={1}>Accesories</Tab>\n          <Tab value={2}>Clothing</Tab>\n        </Tabs>\n        <TabBody style={{ height: 300 }}>\n          {activeTab === 0 && (\n            <div>\n              <GroupBox label='Order:'>\n                <div style={{ padding: '0.5em 0 0.5em 0' }}>Amount:</div>\n                <NumberInput width='100%' min={0} defaultValue={0} />\n                <br />\n                <Checkbox\n                  name='shipping'\n                  value='fast'\n                  label='Fast shipping'\n                  onChange={() => null}\n                  defaultChecked\n                />\n              </GroupBox>\n            </div>\n          )}\n          {activeTab === 1 && (\n            <div>\n              <div>Accesories stuff here</div>\n            </div>\n          )}\n          {activeTab === 2 && (\n            <div>\n              <div>Clothing stuff here</div>\n            </div>\n          )}\n        </TabBody>\n      </WindowContent>\n    </Window>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n\nexport function MultiRow() {\n  const [state, setState] = useState({\n    activeTab: 'Shoes'\n  });\n\n  const handleChange = (value: string) => setState({ activeTab: value });\n\n  const { activeTab } = state;\n  return (\n    <Window style={{ width: 450 }}>\n      <WindowHeader>store.exe</WindowHeader>\n      <WindowContent>\n        <Tabs rows={2} value={activeTab} onChange={handleChange}>\n          <Tab value='Shoes'>Shoes</Tab>\n          <Tab value='Accesories'>Accesories</Tab>\n          <Tab value='Clothing'>Clothing</Tab>\n          <Tab value='Cars'>Cars</Tab>\n          <Tab value='Electronics'>Electronics</Tab>\n          <Tab value='Art'>Art</Tab>\n          <Tab value='Perfumes'>Perfumes</Tab>\n          <Tab value='Games'>Games</Tab>\n          <Tab value='Food'>Food</Tab>\n        </Tabs>\n        <TabBody style={{ height: 300 }}>\n          <p>\n            Currently active tab: <mark>{activeTab}</mark>\n          </p>\n          <br />\n          <p>\n            Keep in mind that multi row tabs are{' '}\n            <Anchor href='http://hallofshame.gp.co.at/tabs.htm' target='_blank'>\n              REALLY bad UX\n            </Anchor>\n            . We&apos;ve added them just because it was a thing back in the day,\n            but there are better ways to handle navigation with many options.\n          </p>\n        </TabBody>\n      </WindowContent>\n    </Window>\n  );\n}\n\nMultiRow.story = {\n  name: 'multi row'\n};\n"
  },
  {
    "path": "src/Tabs/Tabs.tsx",
    "content": "import React, { forwardRef, useMemo } from 'react';\n\nimport styled from 'styled-components';\nimport { noOp } from '../common/utils';\nimport { CommonStyledProps } from '../types';\nimport { TabProps } from './Tab';\n\ntype TabsProps = {\n  value?: TabProps['value'];\n  onChange?: TabProps['onClick'];\n  children?: React.ReactNode;\n  rows?: number;\n} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onChange' | 'value'> &\n  CommonStyledProps;\n\nconst StyledTabs = styled.div<{ isMultiRow: boolean }>`\n  position: relative;\n  ${({ isMultiRow, theme }) =>\n    isMultiRow &&\n    `\n  button {\n    flex-grow: 1;\n  }\n  button:last-child:before {\n    border-right: 2px solid ${theme.borderDark};\n  }\n  `}\n`;\n\nconst Row = styled.div.attrs(() => ({\n  'data-testid': 'tab-row'\n}))`\n  position: relative;\n  display: flex;\n  flex-wrap: no-wrap;\n  text-align: left;\n  left: 8px;\n  width: calc(100% - 8px);\n\n  &:not(:first-child):before {\n    content: '';\n    position: absolute;\n    right: 0;\n    left: 0;\n    height: 100%;\n    border-right: 2px solid ${({ theme }) => theme.borderDarkest};\n    border-left: 2px solid ${({ theme }) => theme.borderLightest};\n  }\n`;\n\nfunction splitToChunks<T>(array: T[], parts: number) {\n  const result = [];\n  for (let i = parts; i > 0; i -= 1) {\n    result.push(array.splice(0, Math.ceil(array.length / i)));\n  }\n  return result;\n}\n\nconst Tabs = forwardRef<HTMLDivElement, TabsProps>(\n  ({ value, onChange = noOp, children, rows = 1, ...otherProps }, ref) => {\n    // split tabs into equal rows and assign key to each row\n    const tabRowsToRender = useMemo(() => {\n      const childrenWithProps =\n        React.Children.map(children, child => {\n          if (!React.isValidElement(child)) {\n            return null;\n          }\n          const tabProps = {\n            selected: child.props.value === value,\n            onClick: onChange\n          };\n          return React.cloneElement(child, tabProps);\n        }) ?? [];\n\n      const tabRows = splitToChunks(childrenWithProps, rows).map((tabs, i) => ({\n        key: i,\n        tabs\n      }));\n\n      // move row containing currently selected tab to the bottom\n      const currentlySelectedRowIndex = tabRows.findIndex(tabRow =>\n        tabRow.tabs.some(tab => tab.props.selected)\n      );\n      tabRows.push(tabRows.splice(currentlySelectedRowIndex, 1)[0]);\n\n      return tabRows;\n    }, [children, onChange, rows, value]);\n\n    return (\n      <StyledTabs\n        {...otherProps}\n        isMultiRow={rows > 1}\n        role='tablist'\n        ref={ref}\n      >\n        {tabRowsToRender.map(row => (\n          <Row key={row.key}>{row.tabs}</Row>\n        ))}\n      </StyledTabs>\n    );\n  }\n);\n\nTabs.displayName = 'Tabs';\n\nexport * from './Tab';\n\nexport * from './TabBody';\n\nexport { Tabs, TabsProps };\n"
  },
  {
    "path": "src/TextInput/TextInput.spec.tsx",
    "content": "// Pretty much straight out copied from https://github.com/mui-org/material-ui 😂\n\nimport React from 'react';\nimport { fireEvent } from '@testing-library/react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { TextInput } from './TextInput';\n\ndescribe('<TextInput />', () => {\n  it('should render an <input /> inside the div', () => {\n    const { container } = renderWithTheme(<TextInput />);\n    const input = container.querySelector('input');\n    expect(input).toHaveAttribute('type', 'text');\n    expect(input).not.toHaveAttribute('required');\n  });\n\n  it('should fire event callbacks', () => {\n    const handleChange = jest.fn();\n    const handleFocus = jest.fn();\n    const handleBlur = jest.fn();\n    const handleKeyUp = jest.fn();\n    const handleKeyDown = jest.fn();\n    const { getByRole } = renderWithTheme(\n      <TextInput\n        onChange={handleChange}\n        onFocus={handleFocus}\n        onBlur={handleBlur}\n        onKeyUp={handleKeyUp}\n        onKeyDown={handleKeyDown}\n      />\n    );\n    const input = getByRole('textbox');\n\n    // simulating user input: gain focus, key input (keydown, (input), change, keyup), blur\n\n    input.focus();\n    expect(handleFocus).toHaveBeenCalledTimes(1);\n\n    fireEvent.keyDown(document.activeElement as HTMLInputElement, { key: 'a' });\n    expect(handleKeyDown).toHaveBeenCalledTimes(1);\n\n    fireEvent.change(input, { target: { value: 'a' } });\n    expect(handleChange).toHaveBeenCalledTimes(1);\n\n    fireEvent.keyUp(document.activeElement as HTMLInputElement, { key: 'a' });\n    expect(handleKeyUp).toHaveBeenCalledTimes(1);\n\n    input.blur();\n    expect(handleBlur).toHaveBeenCalledTimes(1);\n  });\n\n  it('should considered [] as controlled', () => {\n    const { getByRole } = renderWithTheme(<TextInput value={[]} />);\n    const input = getByRole('textbox');\n\n    expect(input).toHaveProperty('value', '');\n    fireEvent.change(input, { target: { value: 'do not work' } });\n    expect(input).toHaveProperty('value', '');\n  });\n\n  it('should forwardRef to native input', () => {\n    const inputRef = React.createRef<HTMLInputElement>();\n    const { getByRole } = renderWithTheme(<TextInput ref={inputRef} />);\n    const input = getByRole('textbox');\n    expect(inputRef.current).toBe(input);\n  });\n\n  describe('multiline', () => {\n    it('should render textarea when passed the multiline prop', () => {\n      const { container } = renderWithTheme(<TextInput multiline />);\n      const textarea = container.querySelector('textarea');\n      expect(textarea).not.toBe(null);\n    });\n\n    it('should forward rows prop', () => {\n      const { container } = renderWithTheme(<TextInput multiline rows={3} />);\n      const textarea = container.querySelector('textarea');\n      expect(textarea).toHaveAttribute('rows', '3');\n    });\n  });\n\n  describe('prop: disabled', () => {\n    it('should render a disabled <input />', () => {\n      const { container } = renderWithTheme(<TextInput disabled />);\n      const input = container.querySelector('input');\n      expect(input).toHaveAttribute('disabled');\n    });\n    it('should be overridden by props', () => {\n      const { getByRole, rerender } = renderWithTheme(<TextInput disabled />);\n      rerender(<TextInput disabled={false} />);\n      const input = getByRole('textbox');\n      expect(input).not.toHaveAttribute('disabled');\n    });\n  });\n\n  describe('prop: variant', () => {\n    it('should be \"default\" by default', () => {\n      const { getByTestId } = renderWithTheme(<TextInput />);\n      expect(getByTestId('variant-default')).toBeInTheDocument();\n    });\n    it('should handle \"flat\" variant', () => {\n      const { getByTestId } = renderWithTheme(<TextInput variant='flat' />);\n      expect(getByTestId('variant-flat')).toBeInTheDocument();\n    });\n  });\n\n  describe('prop: fullWidth', () => {\n    it('should make component take 100% width', () => {\n      const { container } = renderWithTheme(<TextInput fullWidth />);\n      expect(\n        window.getComputedStyle(container.firstChild as HTMLInputElement).width\n      ).toBe('100%');\n    });\n  });\n});\n"
  },
  {
    "path": "src/TextInput/TextInput.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport { Button, ScrollView, TextInput } from 'react95';\nimport styled from 'styled-components';\n\nconst loremIpsum = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sollicitudin, ante vel porttitor posuere, tellus nisi interdum ipsum, non bibendum ante risus ut purus. Curabitur vel posuere odio. Vivamus rutrum, nunc et ullamcorper sagittis, tellus ligula maximus quam, id dapibus sapien metus lobortis diam. Proin luctus, dolor in finibus feugiat, lacus enim gravida sem, quis aliquet tellus leo nec enim. Morbi varius bibendum augue quis venenatis. Curabitur ut elit augue. Pellentesque posuere enim a mattis interdum. Donec sodales convallis turpis, a vulputate elit. Suspendisse potenti.`;\nconst onChange = (\n  e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>\n) => console.log(e.target.value);\n\nconst Wrapper = styled.div`\n  background: ${({ theme }) => theme.material};\n  padding: 5rem;\n  #cutout {\n    padding: 1rem;\n    width: 400px;\n    background: ${({ theme }) => theme.canvas};\n  }\n`;\n\nexport default {\n  title: 'Controls/TextInput',\n  component: TextInput,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof TextInput>;\n\nexport function Default() {\n  const [state, setState] = useState({\n    value: ''\n  });\n\n  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) =>\n    setState({ value: e.target.value });\n  const reset = () => setState({ value: '' });\n\n  return (\n    <div style={{ width: 400 }}>\n      <div style={{ display: 'flex' }}>\n        <TextInput\n          value={state.value}\n          placeholder='Type here...'\n          onChange={handleChange}\n          fullWidth\n        />\n        <Button onClick={reset} style={{ marginLeft: 4 }}>\n          Reset\n        </Button>\n      </div>\n      <br />\n      <TextInput defaultValue='Disabled' disabled fullWidth />\n      <br />\n      <TextInput multiline rows={4} defaultValue={loremIpsum} fullWidth />\n      <br />\n      <TextInput\n        multiline\n        disabled\n        rows={4}\n        defaultValue={loremIpsum}\n        fullWidth\n      />\n    </div>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n\nexport function Flat() {\n  return (\n    <ScrollView id='cutout'>\n      <p>\n        When you want to add input field on a light background (like scrollable\n        content), just use the flat variant:\n      </p>\n      <br />\n      <TextInput\n        variant='flat'\n        placeholder='type here...'\n        width={150}\n        onChange={onChange}\n        fullWidth\n      />\n      <br />\n      <TextInput\n        id='disabled'\n        variant='flat'\n        placeholder='Disabled'\n        width={150}\n        onChange={onChange}\n        disabled\n        fullWidth\n      />\n      <br />\n      <TextInput\n        multiline\n        variant='flat'\n        rows={4}\n        defaultValue={loremIpsum}\n        onChange={onChange}\n        fullWidth\n      />\n      <br />\n      <TextInput\n        multiline\n        variant='flat'\n        disabled\n        rows={4}\n        defaultValue={loremIpsum}\n        onChange={onChange}\n        fullWidth\n      />\n    </ScrollView>\n  );\n}\n\nFlat.story = {\n  name: 'flat'\n};\n"
  },
  {
    "path": "src/TextInput/TextInput.tsx",
    "content": "import React, { forwardRef, useMemo } from 'react';\nimport styled, { css } from 'styled-components';\nimport {\n  createDisabledTextStyles,\n  createFlatBoxStyles,\n  createScrollbars\n} from '../common';\nimport { blockSizes } from '../common/system';\nimport { noOp } from '../common/utils';\nimport { StyledScrollView } from '../ScrollView/ScrollView';\nimport { CommonStyledProps, CommonThemeProps } from '../types';\n\ntype TextInputInputProps = {\n  multiline?: false | undefined;\n  onChange?: React.ChangeEventHandler<HTMLInputElement>;\n  /** @default text */\n  type?: React.HTMLInputTypeAttribute;\n} & Omit<\n  React.InputHTMLAttributes<HTMLInputElement>,\n  'className' | 'disabled' | 'style' | 'type'\n>;\n\ntype TextInputTextAreaProps = {\n  multiline: true;\n  onChange?: React.ChangeEventHandler<HTMLTextAreaElement>;\n} & Omit<\n  React.TextareaHTMLAttributes<HTMLTextAreaElement>,\n  'className' | 'disabled' | 'style' | 'type'\n>;\n\ntype TextInputProps = {\n  className?: string;\n  disabled?: boolean;\n  fullWidth?: boolean;\n  multiline?: boolean;\n  shadow?: boolean;\n  style?: React.CSSProperties;\n  variant?: 'default' | 'flat';\n} & (TextInputInputProps | TextInputTextAreaProps) &\n  CommonStyledProps;\n\ntype WrapperProps = Pick<TextInputProps, 'fullWidth' | 'variant'> &\n  CommonThemeProps;\n\nconst sharedWrapperStyles = css<WrapperProps>`\n  display: flex;\n  align-items: center;\n  width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};\n  min-height: ${blockSizes.md};\n`;\n\nconst Wrapper = styled(StyledScrollView).attrs({\n  'data-testid': 'variant-default'\n})<WrapperProps>`\n  ${sharedWrapperStyles}\n  background: ${({ $disabled, theme }) =>\n    $disabled ? theme.material : theme.canvas};\n`;\n\nconst FlatWrapper = styled.div.attrs({\n  'data-testid': 'variant-flat'\n})<WrapperProps>`\n  ${createFlatBoxStyles()}\n  ${sharedWrapperStyles}\n  position: relative;\n`;\n\ntype InputProps = Pick<TextInputProps, 'disabled' | 'fullWidth' | 'variant'>;\n\nconst sharedInputStyles = css<InputProps>`\n  display: block;\n  box-sizing: border-box;\n  width: 100%;\n  height: 100%;\n  outline: none;\n  border: none;\n  background: none;\n  font-size: 1rem;\n  min-height: 27px;\n  font-family: inherit;\n  color: ${({ theme }) => theme.canvasText};\n  ${({ disabled, variant }) =>\n    variant !== 'flat' && disabled && createDisabledTextStyles()}\n`;\n\nconst StyledTextInput = styled.input<InputProps>`\n  ${sharedInputStyles}\n  padding: 0 8px;\n`;\n\nconst StyledTextArea = styled.textarea<InputProps>`\n  ${sharedInputStyles}\n  padding: 8px;\n  resize: none;\n  ${({ variant }) => createScrollbars(variant)}\n`;\n\nconst TextInput = forwardRef<\n  HTMLInputElement | HTMLTextAreaElement,\n  TextInputProps\n>(\n  (\n    {\n      className,\n      disabled = false,\n      fullWidth,\n      onChange = noOp,\n      shadow = true,\n      style,\n      variant = 'default',\n      ...otherProps\n    },\n    ref\n  ) => {\n    const WrapperComponent = variant === 'flat' ? FlatWrapper : Wrapper;\n\n    const field = useMemo(\n      () =>\n        otherProps.multiline ? (\n          <StyledTextArea\n            disabled={disabled}\n            onChange={disabled ? undefined : onChange}\n            readOnly={disabled}\n            ref={ref}\n            variant={variant}\n            {...otherProps}\n          />\n        ) : (\n          <StyledTextInput\n            disabled={disabled}\n            onChange={disabled ? undefined : onChange}\n            readOnly={disabled}\n            ref={ref}\n            type={otherProps.type ?? 'text'}\n            variant={variant}\n            {...otherProps}\n          />\n        ),\n      [disabled, onChange, otherProps, ref, variant]\n    );\n\n    return (\n      <WrapperComponent\n        className={className}\n        fullWidth={fullWidth}\n        $disabled={disabled}\n        shadow={shadow}\n        style={style}\n      >\n        {field}\n      </WrapperComponent>\n    );\n  }\n);\n\nTextInput.displayName = 'TextInput';\n\nexport { TextInput, TextInputProps };\n"
  },
  {
    "path": "src/Toolbar/Toolbar.spec.tsx",
    "content": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { Toolbar } from './Toolbar';\n\ndescribe('<Toolbar />', () => {\n  it('should render', () => {\n    const { container } = render(<Toolbar />);\n    const toolbar = container.firstChild;\n\n    expect(toolbar).toBeInTheDocument();\n  });\n\n  it('should render children', () => {\n    const { container } = render(<Toolbar>A nice app bar</Toolbar>);\n    const toolbar = container.firstChild;\n\n    expect(toolbar).toHaveTextContent('A nice app bar');\n  });\n\n  describe('prop: noPadding', () => {\n    it('should apply 0 padding', () => {\n      const { container } = render(<Toolbar noPadding />);\n      const toolbar = container.firstChild;\n\n      expect(toolbar).toHaveStyleRule('padding', '0');\n    });\n  });\n});\n"
  },
  {
    "path": "src/Toolbar/Toolbar.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\n\ntype ToolbarProps = {\n  children?: React.ReactNode;\n  noPadding?: boolean;\n} & React.HTMLAttributes<HTMLDivElement>;\n\nconst StyledToolbar = styled.div<ToolbarProps>`\n  position: relative;\n  display: flex;\n  align-items: center;\n  padding: ${props => (props.noPadding ? '0' : '4px')};\n`;\n\nconst Toolbar = forwardRef<HTMLDivElement, ToolbarProps>(function Toolbar(\n  { children, noPadding = false, ...otherProps },\n  ref\n) {\n  return (\n    <StyledToolbar noPadding={noPadding} ref={ref} {...otherProps}>\n      {children}\n    </StyledToolbar>\n  );\n});\n\nToolbar.displayName = 'Toolbar';\n\nexport { Toolbar };\n"
  },
  {
    "path": "src/Tooltip/Tooltip.spec.tsx",
    "content": "import { fireEvent, render, waitFor } from '@testing-library/react';\nimport React from 'react';\n\nimport { Tooltip, TooltipProps } from './Tooltip';\n\nconst getProps = (\n  props: Partial<TooltipProps> = {}\n): Omit<TooltipProps, 'children'> => ({\n  className: props.className,\n  disableFocusListener: props.disableFocusListener,\n  disableMouseListener: props.disableMouseListener,\n  enterDelay: props.enterDelay !== undefined ? props.enterDelay : 0,\n  leaveDelay: props.leaveDelay !== undefined ? props.leaveDelay : 0,\n  onBlur: jest.fn(),\n  onClose: jest.fn(),\n  onFocus: jest.fn(),\n  onMouseEnter: jest.fn(),\n  onMouseLeave: jest.fn(),\n  onOpen: jest.fn(),\n  style: props.style,\n  text: 'I am the tooltip'\n});\n\nconst renderTooltip = (props: Omit<TooltipProps, 'children'>) => (\n  <Tooltip {...props}>\n    <div>Kid</div>\n  </Tooltip>\n);\n\ndescribe('<Tooltip />', () => {\n  describe('render', () => {\n    it('should render wrapper element', () => {\n      const { getByTestId } = render(renderTooltip(getProps()));\n\n      const wrapper = getByTestId('tooltip-wrapper');\n\n      expect(wrapper).toBeInTheDocument();\n      expect(wrapper.tagName).toBe('DIV');\n    });\n\n    it('should render inner tooltip', () => {\n      const { getByTestId } = render(renderTooltip(getProps()));\n\n      const tip = getByTestId('tooltip');\n\n      expect(tip).toBeInTheDocument();\n      expect(tip.tagName).toBe('SPAN');\n    });\n\n    it('should render children', () => {\n      const { getByText } = render(renderTooltip(getProps()));\n\n      const children = getByText('Kid');\n\n      expect(children).toBeInTheDocument();\n      expect(children.tagName).toBe('DIV');\n    });\n\n    it('should render tooltip with provided className', () => {\n      const { getByTestId } = render(\n        renderTooltip(\n          getProps({\n            className: 'my-tip'\n          })\n        )\n      );\n\n      const tip = getByTestId('tooltip');\n\n      expect(tip.className.includes('my-tip')).toBeTruthy();\n    });\n  });\n\n  describe('transition delays', () => {\n    beforeEach(() => {\n      jest.useFakeTimers({\n        legacyFakeTimers: true\n      });\n    });\n\n    afterEach(() => {\n      jest.useRealTimers();\n    });\n\n    it('should respect enterDelay', async () => {\n      const { getByTestId } = render(\n        renderTooltip(\n          getProps({\n            enterDelay: 5\n          })\n        )\n      );\n\n      const wrapper = getByTestId('tooltip-wrapper');\n\n      fireEvent.focus(wrapper);\n\n      expect(window.setTimeout).toHaveBeenCalledWith(expect.any(Function), 5);\n    });\n\n    it('should respect leaveDelay', async () => {\n      const { getByTestId } = render(\n        renderTooltip(\n          getProps({\n            leaveDelay: 6\n          })\n        )\n      );\n\n      const wrapper = getByTestId('tooltip-wrapper');\n\n      fireEvent.blur(wrapper);\n\n      expect(window.setTimeout).toHaveBeenCalledWith(expect.any(Function), 6);\n    });\n  });\n\n  describe('event callbacks', () => {\n    it('should handle onFocus events, and call onOpen', async () => {\n      const props = getProps();\n\n      const { getByTestId } = render(renderTooltip(props));\n\n      const wrapper = getByTestId('tooltip-wrapper');\n\n      fireEvent.focus(wrapper);\n      await waitFor(() => {\n        expect(props.onFocus).toHaveBeenCalled();\n      });\n      await waitFor(() => {\n        expect(props.onOpen).toHaveBeenCalled();\n      });\n    });\n\n    it('should handle onBlur events, and call onClose', async () => {\n      const props = getProps();\n\n      const { getByTestId } = render(renderTooltip(props));\n\n      const wrapper = getByTestId('tooltip-wrapper');\n\n      fireEvent.focus(wrapper);\n      await waitFor(() => {\n        expect(props.onFocus).toHaveBeenCalled();\n      });\n\n      fireEvent.blur(wrapper);\n      await waitFor(() => {\n        expect(props.onBlur).toHaveBeenCalled();\n      });\n      await waitFor(() => {\n        expect(props.onClose).toHaveBeenCalled();\n      });\n    });\n\n    it('should handle onMouseEnter events, and call onOpen', async () => {\n      const props = getProps();\n\n      const { getByTestId } = render(renderTooltip(props));\n\n      const wrapper = getByTestId('tooltip-wrapper');\n\n      fireEvent.mouseEnter(wrapper);\n\n      await waitFor(() => {\n        expect(props.onMouseEnter).toHaveBeenCalled();\n      });\n      await waitFor(() => {\n        expect(props.onOpen).toHaveBeenCalled();\n      });\n    });\n\n    it('should handle onMouseLeave events, and call onClose', async () => {\n      const props = getProps();\n\n      const { getByTestId } = render(renderTooltip(props));\n\n      const wrapper = getByTestId('tooltip-wrapper');\n\n      fireEvent.mouseEnter(wrapper);\n      await waitFor(() => {\n        expect(props.onMouseEnter).toHaveBeenCalled();\n      });\n\n      fireEvent.mouseLeave(wrapper);\n      await waitFor(() => {\n        expect(props.onMouseLeave).toHaveBeenCalled();\n      });\n      await waitFor(() => {\n        expect(props.onClose).toHaveBeenCalled();\n      });\n    });\n\n    it('should not handle onFocus events when disableFocusListener is true', () => {\n      const props = getProps({ disableFocusListener: true });\n\n      const { getByTestId } = render(renderTooltip(props));\n\n      const wrapper = getByTestId('tooltip-wrapper');\n\n      fireEvent.focus(wrapper);\n\n      expect(props.onFocus).not.toHaveBeenCalled();\n    });\n\n    it('should not handle onBlur events when disableFocusListener is true', () => {\n      const props = getProps({ disableFocusListener: true });\n\n      const { getByTestId } = render(renderTooltip(props));\n\n      const wrapper = getByTestId('tooltip-wrapper');\n\n      fireEvent.blur(wrapper);\n\n      expect(props.onBlur).not.toHaveBeenCalled();\n    });\n\n    it('should not handle onMouseEnter events when disableMouseListener is true', () => {\n      const props = getProps({ disableMouseListener: true });\n\n      const { getByTestId } = render(renderTooltip(props));\n\n      const wrapper = getByTestId('tooltip-wrapper');\n\n      fireEvent.mouseEnter(wrapper);\n\n      expect(props.onMouseEnter).not.toHaveBeenCalled();\n    });\n\n    it('should not handle onMouseLeave events when disableMouseListener is true', () => {\n      const props = getProps({ disableMouseListener: true });\n\n      const { getByTestId } = render(renderTooltip(props));\n\n      const wrapper = getByTestId('tooltip-wrapper');\n\n      fireEvent.mouseLeave(wrapper);\n\n      expect(props.onMouseLeave).not.toHaveBeenCalled();\n    });\n  });\n});\n"
  },
  {
    "path": "src/Tooltip/Tooltip.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Button, Tooltip } from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n`;\n\nexport default {\n  title: 'Controls/Tooltip',\n  component: Tooltip,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Tooltip>;\n\nexport function Default() {\n  return (\n    <Tooltip text='I see you! 🤷‍' enterDelay={100} leaveDelay={500}>\n      <Button>Hover me</Button>\n    </Tooltip>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/Tooltip/Tooltip.tsx",
    "content": "import React, { forwardRef, useState } from 'react';\nimport styled from 'styled-components';\n\nimport { shadow } from '../common';\nimport { isReactFocusEvent, isReactMouseEvent } from '../common/utils/events';\nimport { CommonStyledProps } from '../types';\n\ntype TooltipPosition = 'top' | 'bottom' | 'left' | 'right';\n\ntype TooltipProps = {\n  children: React.ReactNode;\n  className?: string;\n  disableFocusListener?: boolean;\n  disableMouseListener?: boolean;\n  enterDelay?: number;\n  leaveDelay?: number;\n  onBlur?: React.FocusEventHandler<HTMLDivElement>;\n  onClose?: (\n    event: React.FocusEvent<HTMLDivElement> | React.MouseEvent<HTMLDivElement>\n  ) => void;\n  onFocus?: React.FocusEventHandler<HTMLDivElement>;\n  onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;\n  onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;\n  onOpen?: (\n    event: React.FocusEvent<HTMLDivElement> | React.MouseEvent<HTMLDivElement>\n  ) => void;\n  style?: React.CSSProperties;\n  text: string;\n  position?: TooltipPosition;\n} & Omit<\n  React.HTMLAttributes<HTMLSpanElement>,\n  'onBlur' | 'onClose' | 'onFocus' | 'onMouseEnter' | 'onMouseLeave' | 'onOpen'\n> &\n  CommonStyledProps;\n\nconst positioningStyles: Record<TooltipPosition, string> = {\n  top: `top: -4px;\n        left: 50%;\n        transform: translate(-50%, -100%);`,\n  bottom: `bottom: -4px;\n           left: 50%;\n           transform: translate(-50%, 100%);`,\n  left: `left: -4px;\n         top: 50%;\n         transform: translate(-100%, -50%);`,\n  right: `right: -4px;\n          top: 50%;\n          transform: translate(100%, -50%);`\n};\n\nconst Tip = styled.span<{ position: TooltipPosition; show: boolean }>`\n  position: absolute;\n\n  z-index: 1;\n  display: ${props => (props.show ? 'block' : 'none')};\n  padding: 4px;\n  border: 2px solid ${({ theme }) => theme.borderDarkest};\n  background: ${({ theme }) => theme.tooltip};\n  box-shadow: ${shadow};\n  text-align: center;\n  font-size: 1rem;\n  ${props => positioningStyles[props.position]}\n`;\n\nconst Wrapper = styled.div`\n  position: relative;\n  display: inline-block;\n  white-space: nowrap;\n`;\n\nconst Tooltip = forwardRef<HTMLSpanElement, TooltipProps>(\n  (\n    {\n      className,\n      children,\n      disableFocusListener = false,\n      disableMouseListener = false,\n      enterDelay = 1000,\n      leaveDelay = 0,\n      onBlur,\n      onClose,\n      onFocus,\n      onMouseEnter,\n      onMouseLeave,\n      onOpen,\n      style,\n      text,\n      position = 'top',\n      ...otherProps\n    },\n    ref\n  ) => {\n    const [show, setShow] = useState(false);\n    const [openTimer, setOpenTimer] = useState<number>();\n    const [closeTimer, setCloseTimer] = useState<number>();\n\n    const isUsingFocus = !disableFocusListener;\n    const isUsingMouse = !disableMouseListener;\n\n    const handleOpen = (\n      event: React.FocusEvent<HTMLDivElement> | React.MouseEvent<HTMLDivElement>\n    ) => {\n      window.clearTimeout(openTimer);\n      window.clearTimeout(closeTimer);\n\n      const timer = window.setTimeout(() => {\n        setShow(true);\n\n        onOpen?.(event);\n      }, enterDelay);\n\n      setOpenTimer(timer);\n    };\n\n    const handleEnter = (\n      event: React.FocusEvent<HTMLDivElement> | React.MouseEvent<HTMLDivElement>\n    ) => {\n      event.persist();\n\n      if (isReactFocusEvent(event)) {\n        onFocus?.(event);\n      } else if (isReactMouseEvent(event)) {\n        onMouseEnter?.(event);\n      }\n\n      handleOpen(event);\n    };\n\n    const handleClose = (\n      event: React.FocusEvent<HTMLDivElement> | React.MouseEvent<HTMLDivElement>\n    ) => {\n      window.clearTimeout(openTimer);\n      window.clearTimeout(closeTimer);\n\n      const timer = window.setTimeout(() => {\n        setShow(false);\n\n        onClose?.(event);\n      }, leaveDelay);\n\n      setCloseTimer(timer);\n    };\n\n    const handleLeave = (\n      event: React.FocusEvent<HTMLDivElement> | React.MouseEvent<HTMLDivElement>\n    ) => {\n      event.persist();\n\n      if (isReactFocusEvent(event)) {\n        onBlur?.(event);\n      } else if (isReactMouseEvent(event)) {\n        onMouseLeave?.(event);\n      }\n\n      handleClose(event);\n    };\n\n    // set callbacks for onBlur and onFocus, unless disableFocusListener is true\n    const blurCb = isUsingFocus ? handleLeave : undefined;\n    const focusCb = isUsingFocus ? handleEnter : undefined;\n\n    // set callbacks for onMouseEnter and onMouseLeave, unless disableMouseListener is true\n    const mouseEnterCb = isUsingMouse ? handleEnter : undefined;\n    const mouseLeaveCb = isUsingMouse ? handleLeave : undefined;\n\n    // set the wrapper's tabIndex for focus events, unless disableFocusListener is true\n    const tabIndex = isUsingFocus ? 0 : undefined;\n\n    return (\n      <Wrapper\n        data-testid='tooltip-wrapper'\n        onBlur={blurCb}\n        onFocus={focusCb}\n        onMouseEnter={mouseEnterCb}\n        onMouseLeave={mouseLeaveCb}\n        tabIndex={tabIndex}\n      >\n        <Tip\n          className={className}\n          data-testid='tooltip'\n          position={position}\n          ref={ref}\n          show={show}\n          style={style}\n          {...otherProps}\n        >\n          {text}\n        </Tip>\n        {children}\n      </Wrapper>\n    );\n  }\n);\n\nTooltip.displayName = 'Tooltip';\n\nexport { Tooltip, TooltipProps };\n"
  },
  {
    "path": "src/TreeView/TreeView.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { TreeView } from './TreeView';\n\nconst categories = [\n  {\n    id: 'beverages',\n    label: 'Beverages',\n    icon: <>🥤</>,\n    items: [\n      {\n        id: 'juices',\n        label: 'Juices',\n        icon: <>🧃</>,\n        items: [\n          { id: 'apple-juice', label: 'Apple juice', icon: <>🍎</> },\n          { id: 'orange-juice', label: 'Orange juice', icon: <>🍊</> },\n          { id: 'strawberry-juice', label: 'Strawberry juice', icon: <>🍓</> }\n        ]\n      },\n      {\n        id: 'coffee',\n        label: 'Coffee',\n        icon: <>☕</>,\n        items: [\n          { id: 'latte', label: 'Latte', icon: <>☕</> },\n          { id: 'espresso', label: 'Espresso', icon: <>☕</> }\n        ]\n      }\n    ]\n  },\n  {\n    id: 'dairy',\n    label: 'Dairy',\n    icon: <>🧈</>,\n    items: [\n      {\n        id: 'cheeses',\n        label: 'Cheeses',\n        icon: <>🧀</>,\n        items: [\n          { id: 'goat-cheese', label: 'Goat cheese', icon: <>🧀</> },\n          { id: 'camembert-cheese', label: 'Camembert', icon: <>🧀</> },\n          { id: 'cheddar-cheese', label: 'Cheddar', icon: <>🧀</> }\n        ]\n      },\n      {\n        id: 'milk',\n        label: 'Milk',\n        icon: <>🥛</>,\n        items: [\n          { id: 'cow-milk', label: 'Cow Milk', icon: <>🐄</> },\n          { id: 'soya-milk', label: 'Soya milk', icon: <>🥛</> },\n          { id: 'oat-milk', label: 'Oat milk', icon: <>🥛</> }\n        ]\n      }\n    ]\n  }\n];\n\ndescribe('<TreeView />', () => {\n  describe('prop: onNodeSelect', () => {\n    it('should call onNodeSelect when uncontrolled', () => {\n      const onNodeSelect = jest.fn((_, id) => id);\n\n      const { getByText } = renderWithTheme(\n        <TreeView tree={categories} onNodeSelect={onNodeSelect} />\n      );\n\n      getByText('Beverages').click();\n\n      expect(onNodeSelect).toHaveBeenCalledTimes(1);\n      expect(onNodeSelect.mock.results[0].value).toBe('beverages');\n    });\n\n    it('should call onNodeSelect when controlled', () => {\n      const onNodeSelect = jest.fn((_, id) => id);\n\n      const { getByText } = renderWithTheme(\n        <TreeView\n          tree={categories}\n          selected='dairy'\n          onNodeSelect={onNodeSelect}\n        />\n      );\n\n      getByText('Beverages').click();\n\n      expect(onNodeSelect).toHaveBeenCalledTimes(1);\n      expect(onNodeSelect.mock.results[0].value).toBe('beverages');\n    });\n  });\n\n  describe('prop: onNodeToggle', () => {\n    it('should call onNodeToggle when uncontrolled', () => {\n      const onNodeToggle = jest.fn((_, ids) => ids);\n\n      const { getByText } = renderWithTheme(\n        <TreeView tree={categories} onNodeToggle={onNodeToggle} />\n      );\n\n      getByText('Beverages').click();\n\n      expect(onNodeToggle).toHaveBeenCalledTimes(1);\n      expect(onNodeToggle.mock.results[0].value).toStrictEqual(['beverages']);\n    });\n\n    it('should call onNodeToggle when controlled', () => {\n      const onNodeToggle = jest.fn((_, ids) => ids);\n\n      const { getByText } = renderWithTheme(\n        <TreeView\n          tree={categories}\n          expanded={['dairy']}\n          onNodeToggle={onNodeToggle}\n        />\n      );\n\n      getByText('Beverages').click();\n\n      expect(onNodeToggle).toHaveBeenCalledTimes(1);\n      expect(onNodeToggle.mock.results[0].value).toStrictEqual([\n        'dairy',\n        'beverages'\n      ]);\n    });\n  });\n\n  describe('prop: disabled', () => {\n    it('should disable the whole tree', () => {\n      const onNodeSelect = jest.fn((_, id) => id);\n      const onNodeToggle = jest.fn((_, ids) => ids);\n\n      const { getByText } = renderWithTheme(\n        <TreeView\n          disabled\n          tree={categories}\n          onNodeSelect={onNodeSelect}\n          onNodeToggle={onNodeToggle}\n        />\n      );\n\n      getByText('Beverages').click();\n\n      expect(onNodeSelect).not.toHaveBeenCalled();\n      expect(onNodeToggle).not.toHaveBeenCalled();\n    });\n\n    it('should disable a tree item', () => {\n      const onNodeSelect = jest.fn((_, id) => id);\n      const onNodeToggle = jest.fn((_, ids) => ids);\n      const modifiedTree = categories.map((item, index) =>\n        index !== 0 ? item : { ...item, disabled: true }\n      );\n\n      const { getByText } = renderWithTheme(\n        <TreeView\n          tree={modifiedTree}\n          onNodeSelect={onNodeSelect}\n          onNodeToggle={onNodeToggle}\n        />\n      );\n\n      getByText('Beverages').click();\n\n      expect(onNodeSelect).not.toHaveBeenCalled();\n      expect(onNodeToggle).not.toHaveBeenCalled();\n\n      getByText('Dairy').click();\n\n      expect(onNodeSelect).toHaveBeenCalledTimes(1);\n      expect(onNodeToggle).toHaveBeenCalledTimes(1);\n    });\n  });\n});\n"
  },
  {
    "path": "src/TreeView/TreeView.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React, { useCallback, useState } from 'react';\nimport { GroupBox, TreeLeaf, TreeView } from 'react95';\nimport styled from 'styled-components';\nimport { Button } from '../Button/Button';\n\nconst Wrapper = styled.div`\n  background: ${({ theme }) => theme.material};\n  padding: 5rem;\n\n  #cutout {\n    background: ${({ theme }) => theme.canvas};\n    padding: 1rem;\n    width: 250px;\n    display: flex;\n    align-items: center;\n    justify-content: space-around;\n  }\n`;\n\nconst Panel = styled.div`\n  padding: 2rem;\n`;\n\nexport default {\n  title: 'Controls/TreeView',\n  component: TreeView,\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof TreeView>;\n\nconst categories = [\n  {\n    id: 'beverages',\n    label: 'Beverages',\n    icon: <>🥤</>,\n    items: [\n      {\n        id: 'juices',\n        label: 'Juices',\n        icon: <>🧃</>,\n        items: [\n          { id: 'apple-juice', label: 'Apple juice', icon: <>🍎</> },\n          { id: 'orange-juice', label: 'Orange juice', icon: <>🍊</> },\n          { id: 'strawberry-juice', label: 'Strawberry juice', icon: <>🍓</> }\n        ]\n      },\n      {\n        id: 'coffee',\n        label: 'Coffee',\n        icon: <>☕</>,\n        items: [\n          { id: 'latte', label: 'Latte', icon: <>☕</> },\n          { id: 'espresso', label: 'Espresso', icon: <>☕</> }\n        ]\n      }\n    ]\n  },\n  {\n    id: 'dairy',\n    label: 'Dairy',\n    icon: <>🧈</>,\n    items: [\n      {\n        id: 'cheeses',\n        label: 'Cheeses',\n        icon: <>🧀</>,\n        items: [\n          { id: 'goat-cheese', label: 'Goat cheese', icon: <>🧀</> },\n          { id: 'camembert-cheese', label: 'Camembert', icon: <>🧀</> },\n          { id: 'cheddar-cheese', label: 'Cheddar', icon: <>🧀</> }\n        ]\n      },\n      {\n        id: 'milk',\n        label: 'Milk',\n        icon: <>🥛</>,\n        items: [\n          { id: 'cow-milk', label: 'Cow Milk', icon: <>🐄</> },\n          { id: 'soya-milk', label: 'Soya milk', icon: <>🥛</> },\n          { id: 'oat-milk', label: 'Oat milk', icon: <>🥛</> }\n        ]\n      }\n    ]\n  }\n];\n\nconst allIds: string[] = [];\n\nfunction getIds(item: TreeLeaf<string>) {\n  allIds.push(item.id);\n  // eslint-disable-next-line no-unused-expressions\n  item.items?.forEach(getIds);\n}\n\ncategories.forEach(getIds);\n\nexport function Basic() {\n  return (\n    <div style={{ maxWidth: '250px' }}>\n      <GroupBox label='Catalog'>\n        <TreeView tree={categories} />\n      </GroupBox>\n    </div>\n  );\n}\n\nBasic.story = {\n  name: 'basic'\n};\n\nexport function Controlled() {\n  const [selected, setSelected] = useState('oat-milk');\n  const [expanded, setExpanded] = useState(['dairy', 'milk']);\n\n  const handleExpandClick = useCallback(() => {\n    setExpanded(oldExpanded => (oldExpanded.length === 0 ? allIds : []));\n  }, []);\n\n  return (\n    <div style={{ maxWidth: '250px' }}>\n      <Panel>\n        <Button onClick={handleExpandClick}>\n          {expanded.length === 0 ? 'Expand all' : 'Collapse all'}\n        </Button>\n      </Panel>\n\n      <GroupBox label='Catalog'>\n        <TreeView\n          tree={categories}\n          onNodeSelect={(_, id) => setSelected(id)}\n          onNodeToggle={(_, ids) => setExpanded(ids)}\n          expanded={expanded}\n          selected={selected}\n        />\n      </GroupBox>\n    </div>\n  );\n}\n\nControlled.story = {\n  name: 'controlled'\n};\n\nexport function Disabled() {\n  return (\n    <div style={{ maxWidth: '250px' }}>\n      <GroupBox label='Catalog'>\n        <TreeView tree={categories} disabled />\n      </GroupBox>\n    </div>\n  );\n}\n\nDisabled.story = {\n  name: 'disabled'\n};\n\nexport function DisabledTreeItems() {\n  function disableSecondItem<T>(items: TreeLeaf<T>[]): TreeLeaf<T>[] {\n    return items.map((item, index) => ({\n      ...item,\n      items: item.items ? disableSecondItem(item.items) : undefined,\n      disabled: index === 1\n    }));\n  }\n  const modifiedTree = disableSecondItem(categories);\n\n  return (\n    <div style={{ maxWidth: '250px' }}>\n      <GroupBox label='Catalog'>\n        <TreeView tree={modifiedTree} />\n      </GroupBox>\n    </div>\n  );\n}\n\nDisabledTreeItems.story = {\n  name: 'disabled tree items'\n};\n"
  },
  {
    "path": "src/TreeView/TreeView.tsx",
    "content": "import React, { forwardRef, useCallback } from 'react';\nimport styled, { css } from 'styled-components';\n\nimport useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';\nimport { LabelText, StyledLabel } from '../common/SwitchBase';\nimport { CommonStyledProps } from '../types';\n\ntype TreeLeaf<T> = {\n  disabled?: boolean;\n  icon?: React.ReactNode;\n  id: T;\n  items?: TreeLeaf<T>[];\n  label?: string;\n};\n\ntype TreeViewProps<T> = {\n  className?: string;\n  defaultExpanded?: T[];\n  defaultSelected?: T;\n  disabled?: boolean;\n  expanded?: T[];\n  onNodeSelect?: (event: React.MouseEvent<HTMLElement>, id: T) => void;\n  onNodeToggle?: (\n    event: React.MouseEvent<HTMLElement>,\n    expandedIds: T[]\n  ) => void;\n  selected?: T;\n  style?: React.CSSProperties;\n  tree: TreeLeaf<T>[];\n} & CommonStyledProps;\n\ntype TreeBranchProps<T> = {\n  className: string | undefined;\n  disabled: boolean;\n  expanded: T[];\n  innerRef?: React.Ref<HTMLUListElement>;\n  level: number;\n  select: (event: React.MouseEvent<HTMLElement>, item: TreeLeaf<T>) => void;\n  selected: T | undefined;\n  style: React.CSSProperties | undefined;\n  tree: TreeLeaf<T>[];\n} & CommonStyledProps;\n\nconst Text = styled(LabelText)`\n  white-space: nowrap;\n`;\n\nconst focusedElementStyles = css<{ $disabled: boolean }>`\n  :focus {\n    outline: none;\n  }\n\n  ${({ $disabled }) =>\n    !$disabled\n      ? css`\n          cursor: pointer;\n\n          :focus {\n            ${Text} {\n              background: ${({ theme }) => theme.hoverBackground};\n              color: ${({ theme }) => theme.materialTextInvert};\n              outline: 2px dotted ${({ theme }) => theme.focusSecondary};\n            }\n          }\n        `\n      : `cursor: default;`}\n`;\n\nconst TreeWrapper = styled.ul<{ isRootLevel: boolean }>`\n  position: relative;\n  isolation: isolate;\n\n  ${({ isRootLevel }) =>\n    isRootLevel &&\n    css`\n      &:before {\n        content: '';\n        position: absolute;\n        top: 20px;\n        bottom: 0;\n        left: 5.5px;\n        width: 1px;\n        border-left: 2px dashed ${({ theme }) => theme.borderDark};\n      }\n    `}\n\n  ul {\n    padding-left: 19.5px;\n  }\n\n  li {\n    position: relative;\n\n    &:before {\n      content: '';\n      position: absolute;\n      top: 17.5px;\n      left: 5.5px;\n      width: 22px;\n      border-top: 2px dashed ${({ theme }) => theme.borderDark};\n      font-size: 12px;\n    }\n  }\n`;\n\nconst TreeItem = styled.li<{ hasItems: boolean; isRootLevel: boolean }>`\n  position: relative;\n  padding-left: ${({ hasItems }) => (!hasItems ? '13px' : '0')};\n\n  ${({ isRootLevel }) =>\n    !isRootLevel\n      ? css`\n          &:last-child {\n            &:after {\n              content: '';\n              position: absolute;\n              z-index: 1;\n              top: 19.5px;\n              bottom: 0;\n              left: 1.5px;\n              width: 10px;\n              background: ${({ theme }) => theme.material};\n            }\n          }\n        `\n      : css`\n          &:last-child {\n            &:after {\n              content: '';\n              position: absolute;\n              top: 19.5px;\n              left: 1px;\n              bottom: 0;\n              width: 10px;\n              background: ${({ theme }) => theme.material};\n            }\n          }\n        `}\n\n  & > details > ul {\n    &:after {\n      content: '';\n      position: absolute;\n      top: -18px;\n      bottom: 0;\n      left: 25px;\n      border-left: 2px dashed ${({ theme }) => theme.borderDark};\n    }\n  }\n`;\n\nconst Details = styled.details`\n  position: relative;\n  z-index: 2;\n\n  &[open] > summary:before {\n    content: '-';\n  }\n`;\n\nconst Summary = styled.summary`\n  position: relative;\n  z-index: 1;\n  display: inline-flex;\n  align-items: center;\n  color: ${({ theme }) => theme.materialText};\n  user-select: none;\n  padding-left: 18px;\n  ${focusedElementStyles};\n\n  &::-webkit-details-marker {\n    display: none;\n  }\n\n  &:before {\n    content: '+';\n    position: absolute;\n    left: 0;\n    display: block;\n    width: 8px;\n    height: 9px;\n    border: 2px solid #808080;\n    padding-left: 1px;\n    background-color: #fff;\n    line-height: 8px;\n    text-align: center;\n  }\n`;\n\nconst TitleWithIcon = styled(StyledLabel)`\n  position: relative;\n  z-index: 1;\n  background: none;\n  border: 0;\n  font-family: inherit;\n  padding-top: 8px;\n  padding-bottom: 8px;\n  margin: 0;\n  ${focusedElementStyles};\n`;\n\nconst Icon = styled.span`\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 16px;\n  height: 16px;\n  margin-right: 6px;\n`;\n\nfunction toggleItem<T>(state: T[], id: T) {\n  return state.includes(id)\n    ? state.filter(item => item !== id)\n    : [...state, id];\n}\n\nfunction preventDefault(event: React.SyntheticEvent) {\n  event.preventDefault();\n}\n\nfunction TreeBranch<T>({\n  className,\n  disabled,\n  expanded,\n  innerRef,\n  level,\n  select,\n  selected,\n  style,\n  tree = []\n}: TreeBranchProps<T>) {\n  const isRootLevel = level === 0;\n\n  const renderLeaf = useCallback(\n    (item: TreeLeaf<T>) => {\n      const hasItems = Boolean(item.items && item.items.length > 0);\n      const isMenuShown = expanded.includes(item.id);\n      const isNodeDisabled = (disabled || item.disabled) ?? false;\n      const onClickSummary = !isNodeDisabled\n        ? (event: React.MouseEvent<HTMLLabelElement>) => select(event, item)\n        : preventDefault;\n      const onClickLeaf = !isNodeDisabled\n        ? (event: React.MouseEvent<HTMLElement>) => select(event, item)\n        : preventDefault;\n      const isSelected = selected === item.id;\n      const icon = <Icon aria-hidden>{item.icon}</Icon>;\n\n      return (\n        <TreeItem\n          key={item.label}\n          isRootLevel={isRootLevel}\n          role='treeitem'\n          aria-expanded={isMenuShown}\n          aria-selected={isSelected}\n          hasItems={hasItems}\n        >\n          {!hasItems ? (\n            <TitleWithIcon\n              as='button'\n              $disabled={isNodeDisabled}\n              onClick={onClickLeaf}\n            >\n              {icon}\n              <Text>{item.label}</Text>\n            </TitleWithIcon>\n          ) : (\n            <Details open={isMenuShown}>\n              <Summary onClick={onClickSummary} $disabled={isNodeDisabled}>\n                <TitleWithIcon $disabled={isNodeDisabled}>\n                  {icon}\n                  <Text>{item.label}</Text>\n                </TitleWithIcon>\n              </Summary>\n\n              {isMenuShown && (\n                <TreeBranch\n                  className={className}\n                  disabled={isNodeDisabled}\n                  expanded={expanded}\n                  level={level + 1}\n                  select={select}\n                  selected={selected}\n                  style={style}\n                  tree={item.items ?? []}\n                />\n              )}\n            </Details>\n          )}\n        </TreeItem>\n      );\n    },\n    [className, disabled, expanded, isRootLevel, level, select, selected, style]\n  );\n\n  return (\n    <TreeWrapper\n      className={isRootLevel ? className : undefined}\n      style={isRootLevel ? style : undefined}\n      ref={isRootLevel ? innerRef : undefined}\n      role={isRootLevel ? 'tree' : 'group'}\n      isRootLevel={isRootLevel}\n    >\n      {tree.map(renderLeaf)}\n    </TreeWrapper>\n  );\n}\n\nfunction TreeInner<T>(\n  {\n    className,\n    defaultExpanded = [],\n    defaultSelected,\n    disabled = false,\n    expanded,\n    onNodeSelect,\n    onNodeToggle,\n    selected,\n    style,\n    tree = []\n  }: TreeViewProps<T>,\n  ref: React.ForwardedRef<HTMLUListElement>\n) {\n  const [expandedInternal, setExpandedInternal] = useControlledOrUncontrolled({\n    defaultValue: defaultExpanded,\n    onChange: onNodeToggle,\n    onChangePropName: 'onNodeToggle',\n    value: expanded,\n    valuePropName: 'expanded'\n  });\n\n  const [selectedInternal, setSelectedInternal] = useControlledOrUncontrolled({\n    defaultValue: defaultSelected,\n    onChange: onNodeSelect,\n    onChangePropName: 'onNodeSelect',\n    value: selected,\n    valuePropName: 'selected'\n  });\n\n  const toggleMenu = useCallback(\n    (event: React.MouseEvent<HTMLElement>, id: T) => {\n      if (onNodeToggle) {\n        const newState = toggleItem(expandedInternal, id);\n        onNodeToggle(event, newState);\n      }\n\n      setExpandedInternal(previouslyExpandedIds =>\n        toggleItem(previouslyExpandedIds, id)\n      );\n    },\n    [expandedInternal, onNodeToggle, setExpandedInternal]\n  );\n\n  const select = useCallback(\n    (event: React.MouseEvent<HTMLElement>, id: T) => {\n      setSelectedInternal(id);\n\n      if (onNodeSelect) {\n        onNodeSelect(event, id);\n      }\n    },\n    [onNodeSelect, setSelectedInternal]\n  );\n\n  const handleSelect = useCallback(\n    (event: React.MouseEvent<HTMLElement>, item: TreeLeaf<T>) => {\n      event.preventDefault();\n      select(event, item.id);\n      if (item.items && item.items.length) {\n        toggleMenu(event, item.id);\n      }\n    },\n    [select, toggleMenu]\n  );\n\n  return (\n    <TreeBranch\n      className={className}\n      disabled={disabled}\n      expanded={expandedInternal}\n      level={0}\n      innerRef={ref}\n      select={handleSelect}\n      selected={selectedInternal}\n      style={style}\n      tree={tree}\n    />\n  );\n}\n\n/* eslint-disable no-use-before-define */\nconst TreeView = forwardRef(TreeInner) as <T>(\n  // eslint-disable-next-line no-use-before-define\n  props: TreeViewProps<T> & { ref?: React.ForwardedRef<HTMLUListElement> }\n) => ReturnType<typeof TreeInner<T>>;\n/* eslint-enable no-use-before-define */\n\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore\nTreeView.displayName = 'TreeView';\n\nexport { TreeView, TreeViewProps, TreeLeaf };\n"
  },
  {
    "path": "src/Window/Window.spec.tsx",
    "content": "import React, { createRef } from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Window } from './Window';\n\ndescribe('<Window />', () => {\n  it('renders Window', () => {\n    const { container } = renderWithTheme(<Window />);\n    const window = container.firstChild;\n\n    expect(window).toBeInTheDocument();\n  });\n\n  it('renders children', () => {\n    const textContent = 'Hi there!';\n    const { getByText } = renderWithTheme(\n      <Window>\n        <span>{textContent}</span>\n      </Window>\n    );\n    expect(getByText(textContent)).toBeInTheDocument();\n  });\n\n  describe('prop: resizable', () => {\n    it('does not render resize handle by default', () => {\n      const { queryByTestId } = renderWithTheme(<Window />);\n\n      expect(queryByTestId('resizeHandle')).not.toBeInTheDocument();\n    });\n\n    it('renders resize handle when set to true', () => {\n      const { queryByTestId } = renderWithTheme(<Window resizable />);\n\n      expect(queryByTestId('resizeHandle')).toBeInTheDocument();\n    });\n\n    it('passes resizeRef to the resizable element', () => {\n      const ref = createRef<HTMLSpanElement>();\n      const { queryByTestId } = renderWithTheme(\n        <Window resizable resizeRef={ref} />\n      );\n\n      expect(queryByTestId('resizeHandle')).toBe(ref.current);\n    });\n  });\n});\n"
  },
  {
    "path": "src/Window/Window.stories.tsx",
    "content": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport {\n  Button,\n  Frame,\n  Toolbar,\n  Window,\n  WindowContent,\n  WindowHeader\n} from 'react95';\nimport styled from 'styled-components';\n\nconst Wrapper = styled.div`\n  padding: 5rem;\n  background: ${({ theme }) => theme.desktopBackground};\n  .window-title {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n  }\n  .close-icon {\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    margin-left: -1px;\n    margin-top: -1px;\n    transform: rotateZ(45deg);\n    position: relative;\n    &:before,\n    &:after {\n      content: '';\n      position: absolute;\n      background: ${({ theme }) => theme.materialText};\n    }\n    &:before {\n      height: 100%;\n      width: 3px;\n      left: 50%;\n      transform: translateX(-50%);\n    }\n    &:after {\n      height: 3px;\n      width: 100%;\n      left: 0px;\n      top: 50%;\n      transform: translateY(-50%);\n    }\n  }\n  .window {\n    width: 400px;\n    min-height: 200px;\n  }\n  .window:nth-child(2) {\n    margin: 2rem;\n  }\n  .footer {\n    display: block;\n    margin: 0.25rem;\n    height: 31px;\n    line-height: 31px;\n    padding-left: 0.25rem;\n  }\n`;\n\nexport default {\n  title: 'Environment/Window',\n  component: Window,\n  subcomponents: { WindowHeader, WindowContent },\n  decorators: [story => <Wrapper>{story()}</Wrapper>]\n} as ComponentMeta<typeof Window>;\n\nexport function Default() {\n  return (\n    <>\n      <Window resizable className='window'>\n        <WindowHeader className='window-title'>\n          <span>react95.exe</span>\n          <Button>\n            <span className='close-icon' />\n          </Button>\n        </WindowHeader>\n        <Toolbar>\n          <Button variant='menu' size='sm'>\n            File\n          </Button>\n          <Button variant='menu' size='sm'>\n            Edit\n          </Button>\n          <Button variant='menu' size='sm' disabled>\n            Save\n          </Button>\n        </Toolbar>\n        <WindowContent>\n          <p>\n            When you set &quot;resizable&quot; prop, there will be drag handle\n            in the bottom right corner (but resizing itself must be handled by\n            you tho!)\n          </p>\n        </WindowContent>\n        <Frame variant='well' className='footer'>\n          Put some useful information here\n        </Frame>\n      </Window>\n\n      <Window className='window'>\n        <WindowHeader active={false} className='window-title'>\n          <span>not-active.exe</span>\n          <Button>\n            <span className='close-icon' />\n          </Button>\n        </WindowHeader>\n        <WindowContent>I am not active</WindowContent>\n      </Window>\n    </>\n  );\n}\n\nDefault.story = {\n  name: 'default'\n};\n"
  },
  {
    "path": "src/Window/Window.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { createBorderStyles, createBoxStyles } from '../common';\nimport { CommonStyledProps } from '../types';\n\ntype WindowProps = {\n  children?: React.ReactNode;\n  resizable?: boolean;\n  resizeRef?: React.Ref<HTMLSpanElement>;\n  shadow?: boolean;\n} & React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\nconst StyledWindow = styled.div`\n  position: relative;\n  padding: 4px;\n  font-size: 1rem;\n  ${createBorderStyles({ style: 'window' })}\n  ${createBoxStyles()}\n`;\n\nconst ResizeHandle = styled.span`\n  ${({ theme }) => css`\n    display: inline-block;\n    position: absolute;\n    bottom: 10px;\n    right: 10px;\n    width: 25px;\n    height: 25px;\n    background-image: linear-gradient(\n      135deg,\n      ${theme.borderLightest} 16.67%,\n      ${theme.material} 16.67%,\n      ${theme.material} 33.33%,\n      ${theme.borderDark} 33.33%,\n      ${theme.borderDark} 50%,\n      ${theme.borderLightest} 50%,\n      ${theme.borderLightest} 66.67%,\n      ${theme.material} 66.67%,\n      ${theme.material} 83.33%,\n      ${theme.borderDark} 83.33%,\n      ${theme.borderDark} 100%\n    );\n    background-size: 8.49px 8.49px;\n    clip-path: polygon(100% 0px, 0px 100%, 100% 100%);\n    cursor: nwse-resize;\n  `}\n`;\n\nconst Window = forwardRef<HTMLDivElement, WindowProps>(\n  (\n    { children, resizable = false, resizeRef, shadow = true, ...otherProps },\n    ref\n  ) => {\n    return (\n      <StyledWindow ref={ref} shadow={shadow} {...otherProps}>\n        {children}\n        {resizable && (\n          <ResizeHandle data-testid='resizeHandle' ref={resizeRef} />\n        )}\n      </StyledWindow>\n    );\n  }\n);\n\nWindow.displayName = 'Window';\n\nexport * from './WindowContent';\n\nexport * from './WindowHeader';\n\nexport { Window, WindowProps };\n"
  },
  {
    "path": "src/Window/WindowContent.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { WindowContent } from './WindowContent';\n\ndescribe('<WindowContent />', () => {\n  it('renders WindowContent', () => {\n    const { container } = renderWithTheme(<WindowContent />);\n    const windowContent = container.firstChild;\n\n    expect(windowContent).toBeInTheDocument();\n  });\n\n  it('renders children', () => {\n    const textContent = 'Hi there!';\n    const { getByText } = renderWithTheme(\n      <WindowContent>\n        <span>{textContent}</span>\n      </WindowContent>\n    );\n    expect(getByText(textContent)).toBeInTheDocument();\n  });\n});\n"
  },
  {
    "path": "src/Window/WindowContent.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { CommonStyledProps } from '../types';\n\ntype WindowContentProps = {\n  children?: React.ReactNode;\n} & React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\nconst StyledWindowContent = styled.div`\n  padding: 16px;\n`;\n\nconst WindowContent = forwardRef<HTMLDivElement, WindowContentProps>(\n  function WindowContent({ children, ...otherProps }, ref) {\n    return (\n      <StyledWindowContent ref={ref} {...otherProps}>\n        {children}\n      </StyledWindowContent>\n    );\n  }\n);\n\nWindowContent.displayName = 'WindowContent';\n\nexport { WindowContent, WindowContentProps };\n"
  },
  {
    "path": "src/Window/WindowHeader.spec.tsx",
    "content": "import React from 'react';\n\nimport { renderWithTheme, theme } from '../../test/utils';\n\nimport { WindowHeader } from './WindowHeader';\n\ndescribe('<WindowHeader />', () => {\n  it('renders WindowHeader', () => {\n    const { container } = renderWithTheme(<WindowHeader />);\n    const windowHeader = container.firstChild;\n\n    expect(windowHeader).toBeInTheDocument();\n  });\n\n  it('renders children', () => {\n    const textContent = 'Hi there!';\n    const { getByText } = renderWithTheme(\n      <WindowHeader>\n        <span>{textContent}</span>\n      </WindowHeader>\n    );\n    expect(getByText(textContent)).toBeInTheDocument();\n  });\n\n  describe('prop: active', () => {\n    it('displays active header by default', () => {\n      const { container } = renderWithTheme(<WindowHeader />);\n      const windowHeader = container.firstChild as HTMLDivElement;\n\n      expect(windowHeader).toHaveStyleRule('color', theme.headerText);\n      expect(windowHeader).toHaveStyleRule(\n        'background',\n        theme.headerBackground\n      );\n    });\n\n    it('displays active header when set to true', () => {\n      const { container } = renderWithTheme(<WindowHeader active />);\n      const windowHeader = container.firstChild as HTMLDivElement;\n\n      expect(windowHeader).toHaveStyleRule('color', theme.headerText);\n      expect(windowHeader).toHaveStyleRule(\n        'background',\n        theme.headerBackground\n      );\n    });\n\n    it('renders non-active header when set to false', () => {\n      const { container } = renderWithTheme(<WindowHeader active={false} />);\n      const windowHeader = container.firstChild;\n\n      expect(windowHeader).toHaveStyleRule('color', theme.headerNotActiveText);\n      expect(windowHeader).toHaveStyleRule(\n        'background',\n        theme.headerNotActiveBackground\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "src/Window/WindowHeader.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { StyledButton } from '../Button/Button';\nimport { CommonStyledProps } from '../types';\n\ntype WindowHeaderProps = {\n  children?: React.ReactNode;\n  active?: boolean;\n} & React.HTMLAttributes<HTMLDivElement> &\n  CommonStyledProps;\n\nconst StyledWindowHeader = styled.div<Pick<WindowHeaderProps, 'active'>>`\n  height: 33px;\n  line-height: 33px;\n  padding-left: 0.25rem;\n  padding-right: 3px;\n  font-weight: bold;\n  border: 2px solid ${({ theme }) => theme.material};\n  ${({ active }) =>\n    active === false\n      ? css`\n          background: ${({ theme }) => theme.headerNotActiveBackground};\n          color: ${({ theme }) => theme.headerNotActiveText};\n        `\n      : css`\n          background: ${({ theme }) => theme.headerBackground};\n          color: ${({ theme }) => theme.headerText};\n        `}\n\n  ${StyledButton} {\n    padding-left: 0;\n    padding-right: 0;\n    height: 27px;\n    width: 31px;\n  }\n`;\n\n// TODO - should we add some aria label indicating if window is currently active?\nconst WindowHeader = forwardRef<HTMLDivElement, WindowHeaderProps>(\n  function WindowHeader({ active = true, children, ...otherProps }, ref) {\n    return (\n      <StyledWindowHeader active={active} ref={ref} {...otherProps}>\n        {children}\n      </StyledWindowHeader>\n    );\n  }\n);\n\nWindowHeader.displayName = 'WindowHeader';\n\nexport { WindowHeader, WindowHeaderProps };\n"
  },
  {
    "path": "src/assets/fonts/src/ms-sans-serif/license.txt",
    "content": "The FontStruction “MS Sans Serif”\n(https://fontstruct.com/fontstructions/show/1384746) by “lou” is licensed\nunder a Creative Commons Attribution Share Alike license\n(http://creativecommons.org/licenses/by-sa/3.0/).\n"
  },
  {
    "path": "src/assets/fonts/src/ms-sans-serif/readme.txt",
    "content": "The font file in this archive was created using Fontstruct the free, online\nfont-building tool.\nThis font was created by “lou”.\nThis font has a homepage where this archive and other versions may be found:\nhttps://fontstruct.com/fontstructions/show/1384746\n\nTry Fontstruct at http://fontstruct.com\nIt’s easy and it’s fun.\n\nNOTE FOR FLASH USERS: Fontstruct fonts (fontstructions) are optimized for Flash.\nIf the font in this archive is a pixel font, it is best displayed at a font-size\nof 11.\n\nFontstruct is sponsored by FontShop.\nVisit them at https://fontshop.com\nFontShop is the original independent font retailer. We’ve been around since\nthe dawn of digital type. Whether you need the right font or need to create the\nright font from scratch, let our 26 years of experience work for you.\n\nFontstruct is copyright ©2017 Rob Meek\n\nLEGAL NOTICE:\nIn using this font you must comply with the licensing terms described in the\nfile “license.txt” included with this archive.\nIf you redistribute the font file in this archive, it must be accompanied by all\nthe other files from this archive, including this one.\n"
  },
  {
    "path": "src/assets/fonts/src/ms-sans-serif-bold/license.txt",
    "content": "The FontStruction “MS Sans Serif Bold”\n(https://fontstruct.com/fontstructions/show/1384862) by “lou” is licensed\nunder a Creative Commons Attribution Share Alike license\n(http://creativecommons.org/licenses/by-sa/3.0/).\n"
  },
  {
    "path": "src/assets/fonts/src/ms-sans-serif-bold/readme.txt",
    "content": "The font file in this archive was created using Fontstruct the free, online\nfont-building tool.\nThis font was created by “lou”.\nThis font has a homepage where this archive and other versions may be found:\nhttps://fontstruct.com/fontstructions/show/1384862\n\nTry Fontstruct at http://fontstruct.com\nIt’s easy and it’s fun.\n\nNOTE FOR FLASH USERS: Fontstruct fonts (fontstructions) are optimized for Flash.\nIf the font in this archive is a pixel font, it is best displayed at a font-size\nof 11.\n\nFontstruct is sponsored by FontShop.\nVisit them at https://fontshop.com\nFontShop is the original independent font retailer. We’ve been around since\nthe dawn of digital type. Whether you need the right font or need to create the\nright font from scratch, let our 26 years of experience work for you.\n\nFontstruct is copyright ©2017 Rob Meek\n\nLEGAL NOTICE:\nIn using this font you must comply with the licensing terms described in the\nfile “license.txt” included with this archive.\nIf you redistribute the font file in this archive, it must be accompanied by all\nthe other files from this archive, including this one.\n"
  },
  {
    "path": "src/common/SwitchBase.ts",
    "content": "import styled, { css } from 'styled-components';\n\nimport { createDisabledTextStyles, focusOutline } from '.';\nimport { StyledMenuListItem } from '../MenuList/MenuList';\n\nexport const size = 20;\n\nexport const StyledInput = styled.input`\n  position: absolute;\n  left: 0;\n  margin: 0;\n  width: ${size}px;\n  height: ${size}px;\n  opacity: 0;\n  z-index: -1;\n`;\n\nexport const StyledLabel = styled.label<{ $disabled: boolean }>`\n  display: inline-flex;\n  align-items: center;\n  position: relative;\n  margin: 8px 0;\n  cursor: ${({ $disabled }) => (!$disabled ? 'pointer' : 'auto')};\n  user-select: none;\n  font-size: 1rem;\n  color: ${({ theme }) => theme.materialText};\n  ${props => props.$disabled && createDisabledTextStyles()}\n\n  ${StyledMenuListItem} & {\n    margin: 0;\n    height: 100%;\n  }\n  ${StyledMenuListItem}:hover & {\n    ${({ $disabled, theme }) =>\n      !$disabled &&\n      css`\n        color: ${theme.materialTextInvert};\n      `};\n  }\n`;\n\n// TODO how to handle focus styles in 'menu' variant of Checkbox/Radio?\nexport const LabelText = styled.span`\n  display: inline-block;\n  line-height: 1;\n  padding: 2px;\n  ${StyledInput}:focus ~ & {\n    ${focusOutline}\n  }\n  ${StyledInput}:not(:disabled) ~ &:active {\n    ${focusOutline}\n  }\n`;\n"
  },
  {
    "path": "src/common/constants.ts",
    "content": "export const KEYBOARD_KEY_CODES = {\n  ARROW_DOWN: 'ArrowDown',\n  ARROW_LEFT: 'ArrowLeft',\n  ARROW_RIGHT: 'ArrowRight',\n  ARROW_UP: 'ArrowUp',\n  END: 'End',\n  ENTER: 'Enter',\n  ESC: 'Escape',\n  HOME: 'Home',\n  SPACE: 'Space',\n  TAB: 'Tab'\n};\n"
  },
  {
    "path": "src/common/hooks/useControlledOrUncontrolled.ts",
    "content": "import React, { useState, useCallback } from 'react';\n\nexport default function useControlledOrUncontrolled<T>({\n  defaultValue,\n  onChange,\n  onChangePropName = 'onChange',\n  readOnly,\n  value,\n  valuePropName = 'value'\n}: {\n  defaultValue: T;\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  onChange?: (...args: any[]) => void;\n  onChangePropName?: string;\n  readOnly?: boolean;\n  value: T | undefined;\n  valuePropName?: string;\n}): [T, (newValue: React.SetStateAction<T>) => void] {\n  const isControlled = value !== undefined;\n  const [controlledValue, setControlledValue] = useState(defaultValue);\n  const handleChangeIfUncontrolled = useCallback(\n    (newValue: React.SetStateAction<T>) => {\n      if (!isControlled) {\n        setControlledValue(newValue);\n      }\n    },\n    [isControlled]\n  );\n\n  // Because we provide `onChange` even to uncontrolled components, React's\n  // default uncontrolled warning must be reimplemented. This also deals with\n  // props that are different from `value`.\n  if (isControlled && typeof onChange !== 'function' && !readOnly) {\n    const message = `Warning: You provided a \\`${valuePropName}\\` prop to a component without an \\`${onChangePropName}\\` handler.${\n      valuePropName === 'value'\n        ? `This will render a read-only field. If the field should be mutable use \\`defaultValue\\`. Otherwise, set either \\`${onChangePropName}\\` or \\`readOnly\\`.`\n        : `This breaks the component state. You must provide an \\`${onChangePropName}\\` function that updates \\`${valuePropName}\\`.`\n    }`;\n\n    // eslint-disable-next-line no-console\n    console.warn(message);\n  }\n\n  return [isControlled ? value : controlledValue, handleChangeIfUncontrolled];\n}\n"
  },
  {
    "path": "src/common/hooks/useEventCallback.ts",
    "content": "import * as React from 'react';\n\nconst useEnhancedEffect =\n  typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;\n\n/**\n * https://github.com/facebook/react/issues/14099#issuecomment-440013892\n */\nexport default function useEventCallback<Args extends unknown[], Return>(\n  fn: (...args: Args) => Return\n): (...args: Args) => Return {\n  const ref = React.useRef(fn);\n  useEnhancedEffect(() => {\n    ref.current = fn;\n  });\n  return React.useCallback(\n    (...args: Args) =>\n      // @ts-expect-error hide `this`\n      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n      (0, ref.current!)(...args),\n    []\n  );\n}\n"
  },
  {
    "path": "src/common/hooks/useForkRef.spec.tsx",
    "content": "import { render } from '@testing-library/react';\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\n\nimport useForkRef from './useForkRef';\n\nconst consoleError = console.error;\n\nbeforeEach(() => {\n  console.error = jest.fn();\n});\n\nafterEach(() => {\n  console.error = consoleError;\n});\n\ndescribe('useForkRef', () => {\n  it('returns a single ref-setter function that forks the ref to its inputs', () => {\n    function Component(props: { innerRef: React.RefObject<HTMLDivElement> }) {\n      const { innerRef } = props;\n      const ownRef = useRef<HTMLDivElement>();\n      const [, forceUpdate] = useState(true);\n      useEffect(() => forceUpdate(n => !n), []);\n\n      const handleRef = useForkRef(innerRef, ownRef);\n\n      return (\n        <div ref={handleRef}>{ownRef.current ? 'has a ref' : 'has no ref'}</div>\n      );\n    }\n\n    const outerRef = React.createRef<HTMLDivElement>();\n    render(<Component innerRef={outerRef} />);\n\n    expect(outerRef.current?.textContent).toBe('has a ref');\n    expect(console.error).not.toHaveBeenCalled();\n  });\n\n  it('forks if only one of the branches requires a ref', () => {\n    const Component = React.forwardRef<HTMLDivElement>((_, ref) => {\n      const [hasRef, setHasRef] = useState(false);\n      const handleOwnRef = useCallback(() => setHasRef(true), []);\n      const handleRef = useForkRef(handleOwnRef, ref);\n\n      return <div ref={handleRef}>{String(hasRef)}</div>;\n    });\n\n    const { getByText } = render(<Component />);\n\n    expect(getByText('true')).toBeInTheDocument();\n    expect(console.error).not.toHaveBeenCalled();\n  });\n\n  it('does nothing if none of the forked branches requires a ref', () => {\n    const setRef = jest.fn();\n\n    type OuterProps = {\n      children: React.ReactElement;\n    };\n\n    const Outer = React.forwardRef<null, OuterProps>((props, ref) => {\n      const { children } = props;\n\n      // TODO: Fix this test as reading ref from children is not allowed so not available on React types\n      // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n      // @ts-ignore\n      const handleRef = useForkRef(children?.ref, ref);\n      setRef(handleRef);\n\n      return children ? React.cloneElement(children, { ref: handleRef }) : null;\n    });\n\n    function Inner() {\n      return <div />;\n    }\n\n    render(\n      <Outer>\n        <Inner />\n      </Outer>\n    );\n    expect(console.error).not.toHaveBeenCalled();\n    expect(setRef).toHaveBeenCalledWith(null);\n  });\n\n  describe('changing refs', () => {\n    function Div(\n      props: {\n        leftRef?: React.Ref<HTMLDivElement>;\n        rightRef?: React.Ref<HTMLDivElement>;\n      } & React.HTMLAttributes<HTMLDivElement>\n    ) {\n      const { leftRef = null, rightRef = null, ...other } = props;\n      const handleRef = useForkRef(leftRef, rightRef);\n\n      return <div {...other} ref={handleRef} />;\n    }\n\n    it('handles changing from no ref to some ref', () => {\n      const { rerender } = render(<Div id='test' />);\n\n      expect(console.error).not.toHaveBeenCalled();\n\n      const ref = React.createRef<HTMLDivElement>();\n      rerender(<Div id='test' leftRef={ref} />);\n\n      expect(ref.current?.id).toBe('test');\n      expect(console.error).not.toHaveBeenCalled();\n    });\n\n    it('cleans up detached refs', () => {\n      const firstLeftRef = React.createRef<HTMLDivElement>();\n      const firstRightRef = React.createRef<HTMLDivElement>();\n      const secondRightRef = React.createRef<HTMLDivElement>();\n\n      const { rerender } = render(\n        <Div leftRef={firstLeftRef} rightRef={firstRightRef} id='test' />\n      );\n\n      expect(console.error).not.toHaveBeenCalled();\n\n      expect(firstLeftRef.current?.id).toBe('test');\n      expect(firstRightRef.current?.id).toBe('test');\n      expect(secondRightRef.current).toBe(null);\n\n      rerender(\n        <Div leftRef={firstLeftRef} rightRef={secondRightRef} id='test' />\n      );\n\n      expect(firstLeftRef.current?.id).toBe('test');\n      expect(firstRightRef.current).toBe(null);\n      expect(secondRightRef.current?.id).toBe('test');\n    });\n  });\n});\n"
  },
  {
    "path": "src/common/hooks/useForkRef.ts",
    "content": "// Straight out copied from https://github.com/mui-org/material-ui 😂\n\nimport { useMemo } from 'react';\n\nfunction setRef<T>(\n  ref: React.RefCallback<T> | React.MutableRefObject<T> | null,\n  value: T\n) {\n  if (typeof ref === 'function') {\n    ref(value);\n  } else if (ref) {\n    // eslint-disable-next-line no-param-reassign\n    ref.current = value;\n  }\n}\n\nexport default function useForkRef<T>(\n  refA: React.RefCallback<T> | React.MutableRefObject<T> | null,\n  refB: React.RefCallback<T> | React.MutableRefObject<T> | null\n): React.RefCallback<T> | null {\n  /**\n   * This will create a new function if the ref props change and are defined.\n   * This means react will call the old forkRef with `null` and the new forkRef\n   * with the ref. Cleanup naturally emerges from this behavior\n   */\n  return useMemo(() => {\n    if (refA == null && refB == null) {\n      return null;\n    }\n    return refValue => {\n      setRef(refA, refValue);\n      setRef(refB, refValue);\n    };\n  }, [refA, refB]);\n}\n"
  },
  {
    "path": "src/common/hooks/useId.spec.ts",
    "content": "import { renderHook } from '@testing-library/react-hooks';\nimport { useId } from './useId';\n\ndescribe(useId, () => {\n  it('returns a random string of 10 digits', () => {\n    const { result } = renderHook(() => useId());\n    expect(result.current).toMatch(/^[0-9a-z]{10}$/i);\n  });\n\n  it('returns the passed ID', () => {\n    const { result } = renderHook(() => useId('test'));\n    expect(result.current).toEqual('test');\n  });\n});\n"
  },
  {
    "path": "src/common/hooks/useId.ts",
    "content": "import { useMemo } from 'react';\n\nfunction makeId() {\n  const chars =\n    '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n  let id = '';\n  for (let i = 0; i < 10; i += 1) {\n    id += chars[Math.floor(Math.random() * chars.length)];\n  }\n  return id;\n}\n\nexport const useId = (id?: string) => {\n  return useMemo(() => id ?? makeId(), [id]);\n};\n"
  },
  {
    "path": "src/common/hooks/useIsFocusVisible.ts",
    "content": "// Straight out copied from https://github.com/mui-org/material-ui 😂\n// based on https://github.com/WICG/focus-visible/blob/v4.1.5/src/focus-visible.js\n\nimport { useCallback } from 'react';\nimport { findDOMNode } from 'react-dom';\n\nlet hadKeyboardEvent = true;\nlet hadFocusVisibleRecently = false;\nlet hadFocusVisibleRecentlyTimeout: number;\n\nconst inputTypesWhitelist: Record<string, boolean> = {\n  text: true,\n  search: true,\n  url: true,\n  tel: true,\n  email: true,\n  password: true,\n  number: true,\n  date: true,\n  month: true,\n  week: true,\n  time: true,\n  datetime: true,\n  'datetime-local': true\n};\n\n/**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} node\n * @return {boolean}\n */\nfunction focusTriggersKeyboardModality(\n  node: Element | HTMLElement | HTMLInputElement\n) {\n  if ('type' in node) {\n    const { type, tagName } = node;\n\n    if (tagName === 'INPUT' && inputTypesWhitelist[type] && !node.readOnly) {\n      return true;\n    }\n\n    if (tagName === 'TEXTAREA' && !node.readOnly) {\n      return true;\n    }\n  }\n\n  if ('isContentEditable' in node && node.isContentEditable) {\n    return true;\n  }\n\n  return false;\n}\n\n/**\n * Keep track of our keyboard modality state with `hadKeyboardEvent`.\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * @param {KeyboardEvent} event\n */\nfunction handleKeyDown(event: KeyboardEvent) {\n  if (event.metaKey || event.altKey || event.ctrlKey) {\n    return;\n  }\n  hadKeyboardEvent = true;\n}\n\n/**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n */\nfunction handlePointerDown() {\n  hadKeyboardEvent = false;\n}\n\nfunction handleVisibilityChange(this: Document) {\n  if (this.visibilityState === 'hidden') {\n    // If the tab becomes active again, the browser will handle calling focus\n    // on the element (Safari actually calls it twice).\n    // If this tab change caused a blur on an element with focus-visible,\n    // re-apply the class when the user switches back to the tab.\n    if (hadFocusVisibleRecently) {\n      hadKeyboardEvent = true;\n    }\n  }\n}\n\nfunction prepare(doc: Document) {\n  doc.addEventListener('keydown', handleKeyDown, true);\n  doc.addEventListener('mousedown', handlePointerDown, true);\n  doc.addEventListener('pointerdown', handlePointerDown, true);\n  doc.addEventListener('touchstart', handlePointerDown, true);\n  doc.addEventListener('visibilitychange', handleVisibilityChange, true);\n}\n\nexport function teardown(doc: Document) {\n  doc.removeEventListener('keydown', handleKeyDown, true);\n  doc.removeEventListener('mousedown', handlePointerDown, true);\n  doc.removeEventListener('pointerdown', handlePointerDown, true);\n  doc.removeEventListener('touchstart', handlePointerDown, true);\n  doc.removeEventListener('visibilitychange', handleVisibilityChange, true);\n}\n\nfunction isFocusVisible(event: React.FocusEvent) {\n  const { target } = event;\n  try {\n    return target.matches(':focus-visible');\n  } catch (error) {\n    // browsers not implementing :focus-visible will throw a SyntaxError\n    // we use our own heuristic for those browsers\n    // rethrow might be better if it's not the expected error but do we really\n    // want to crash if focus-visible malfunctioned?\n  }\n\n  // no need for validFocusTarget check. the user does that by attaching it to\n  // focusable events only\n  return hadKeyboardEvent || focusTriggersKeyboardModality(target);\n}\n\n/**\n * Should be called if a blur event is fired on a focus-visible element\n */\nfunction handleBlurVisible() {\n  // To detect a tab/window switch, we look for a blur event followed\n  // rapidly by a visibility change.\n  // If we don't see a visibility change within 100ms, it's probably a\n  // regular focus change.\n  hadFocusVisibleRecently = true;\n  window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n  hadFocusVisibleRecentlyTimeout = window.setTimeout(() => {\n    hadFocusVisibleRecently = false;\n  }, 100);\n}\n\nexport function useIsFocusVisible<T extends Element = HTMLElement>() {\n  const ref = useCallback((instance: T) => {\n    // eslint-disable-next-line react/no-find-dom-node\n    const node = findDOMNode(instance);\n    if (node != null) {\n      prepare(node.ownerDocument);\n    }\n  }, []);\n\n  return { isFocusVisible, onBlurVisible: handleBlurVisible, ref };\n}\n"
  },
  {
    "path": "src/common/index.ts",
    "content": "import { css } from 'styled-components';\nimport { Color, CommonThemeProps, Theme } from '../types';\n\nexport const shadow = '4px 4px 10px 0 rgba(0, 0, 0, 0.35)';\nexport const insetShadow = 'inset 2px 2px 3px rgba(0,0,0,0.2)';\n\nexport const createDisabledTextStyles = () => css`\n  -webkit-text-fill-color: ${({ theme }) => theme.materialTextDisabled};\n  color: ${({ theme }) => theme.materialTextDisabled};\n  text-shadow: 1px 1px ${({ theme }) => theme.materialTextDisabledShadow};\n  /* filter: grayscale(100%); */\n`;\n\nexport const createBoxStyles = ({\n  background = 'material',\n  color = 'materialText'\n}: {\n  background?: keyof Theme;\n  color?: keyof Theme;\n} = {}) => css`\n  box-sizing: border-box;\n  display: inline-block;\n  background: ${({ theme }) => theme[background]};\n  color: ${({ theme }) => theme[color]};\n`;\n\n// TODO for flat box styles add checkered background when disabled (not solid color)\nexport const createHatchedBackground = ({\n  mainColor = 'black',\n  secondaryColor = 'transparent',\n  pixelSize = 2\n}) => css`\n  background-image: ${[\n    `linear-gradient(\n      45deg,\n      ${mainColor} 25%,\n      transparent 25%,\n      transparent 75%,\n      ${mainColor} 75%\n    )`,\n    `linear-gradient(\n      45deg,\n      ${mainColor} 25%,\n      transparent 25%,\n      transparent 75%,\n      ${mainColor} 75%\n    )`\n  ].join(',')};\n  background-color: ${secondaryColor};\n  background-size: ${`${pixelSize * 2}px ${pixelSize * 2}px`};\n  background-position: 0 0, ${`${pixelSize}px ${pixelSize}px`};\n`;\n\nexport const createFlatBoxStyles = () => css<CommonThemeProps>`\n  position: relative;\n  box-sizing: border-box;\n  display: inline-block;\n  color: ${({ theme }) => theme.materialText};\n  background: ${({ $disabled, theme }) =>\n    $disabled ? theme.flatLight : theme.canvas};\n  border: 2px solid ${({ theme }) => theme.canvas};\n  outline: 2px solid ${({ theme }) => theme.flatDark};\n  outline-offset: -4px;\n`;\n\nexport type BorderStyles =\n  | 'button'\n  | 'buttonPressed'\n  | 'buttonThin'\n  | 'buttonThinPressed'\n  | 'field'\n  | 'grouping'\n  | 'status'\n  | 'window';\n\ntype BorderStyle = {\n  topLeftOuter: keyof Theme;\n  topLeftInner: keyof Theme | null;\n  bottomRightInner: keyof Theme | null;\n  bottomRightOuter: keyof Theme;\n};\n\nconst borderStyles: Record<BorderStyles, BorderStyle> = {\n  button: {\n    topLeftOuter: 'borderLightest',\n    topLeftInner: 'borderLight',\n    bottomRightInner: 'borderDark',\n    bottomRightOuter: 'borderDarkest'\n  },\n  buttonPressed: {\n    topLeftOuter: 'borderDarkest',\n    topLeftInner: 'borderDark',\n    bottomRightInner: 'borderLight',\n    bottomRightOuter: 'borderLightest'\n  },\n  buttonThin: {\n    topLeftOuter: 'borderLightest',\n    topLeftInner: null,\n    bottomRightInner: null,\n    bottomRightOuter: 'borderDark'\n  },\n  buttonThinPressed: {\n    topLeftOuter: 'borderDark',\n    topLeftInner: null,\n    bottomRightInner: null,\n    bottomRightOuter: 'borderLightest'\n  },\n  field: {\n    topLeftOuter: 'borderDark',\n    topLeftInner: 'borderDarkest',\n    bottomRightInner: 'borderLight',\n    bottomRightOuter: 'borderLightest'\n  },\n  grouping: {\n    topLeftOuter: 'borderDark',\n    topLeftInner: 'borderLightest',\n    bottomRightInner: 'borderDark',\n    bottomRightOuter: 'borderLightest'\n  },\n  status: {\n    topLeftOuter: 'borderDark',\n    topLeftInner: null,\n    bottomRightInner: null,\n    bottomRightOuter: 'borderLightest'\n  },\n  window: {\n    topLeftOuter: 'borderLight',\n    topLeftInner: 'borderLightest',\n    bottomRightInner: 'borderDark',\n    bottomRightOuter: 'borderDarkest'\n  }\n};\n\nexport const createInnerBorderWithShadow = ({\n  theme,\n  topLeftInner,\n  bottomRightInner,\n  hasShadow = false,\n  hasInsetShadow = false\n}: {\n  theme: Theme;\n  topLeftInner: keyof Theme | null;\n  bottomRightInner: keyof Theme | null;\n  hasShadow?: boolean;\n  hasInsetShadow?: boolean;\n}) =>\n  [\n    hasShadow ? shadow : false,\n    hasInsetShadow ? insetShadow : false,\n    topLeftInner !== null\n      ? `inset 1px 1px 0px 1px ${theme[topLeftInner]}`\n      : false,\n    bottomRightInner !== null\n      ? `inset -1px -1px 0 1px ${theme[bottomRightInner]}`\n      : false\n  ]\n    .filter(Boolean)\n    .join(', ');\n\nexport const createBorderStyles = ({\n  invert = false,\n  style = 'button'\n}: { invert?: boolean; style?: BorderStyles } = {}) => {\n  const borders = {\n    topLeftOuter: invert ? 'bottomRightOuter' : 'topLeftOuter',\n    topLeftInner: invert ? 'bottomRightInner' : 'topLeftInner',\n    bottomRightInner: invert ? 'topLeftInner' : 'bottomRightInner',\n    bottomRightOuter: invert ? 'topLeftOuter' : 'bottomRightOuter'\n  } as const;\n  return css<CommonThemeProps>`\n    border-style: solid;\n    border-width: 2px;\n    border-left-color: ${({ theme }) =>\n      theme[borderStyles[style][borders.topLeftOuter]]};\n    border-top-color: ${({ theme }) =>\n      theme[borderStyles[style][borders.topLeftOuter]]};\n    border-right-color: ${({ theme }) =>\n      theme[borderStyles[style][borders.bottomRightOuter]]};\n    border-bottom-color: ${({ theme }) =>\n      theme[borderStyles[style][borders.bottomRightOuter]]};\n    box-shadow: ${({ theme, shadow: hasShadow }) =>\n      createInnerBorderWithShadow({\n        theme,\n        topLeftInner: borderStyles[style][borders.topLeftInner],\n        bottomRightInner: borderStyles[style][borders.bottomRightInner],\n        hasShadow\n      })};\n  `;\n};\n\n/** @deprecated Use `createBorderStyles` instead */\nexport const createWellBorderStyles = (invert = false) =>\n  createBorderStyles({\n    invert: !invert,\n    style: 'status'\n  });\n\nexport const focusOutline = () => css`\n  outline: 2px dotted ${({ theme }) => theme.materialText};\n`;\n\nconst nodeBtoa = (string: string) => Buffer.from(string).toString('base64');\nconst base64encode = typeof btoa !== 'undefined' ? btoa : nodeBtoa;\n\nconst createTriangleSVG = (color: Color, angle = 0) => {\n  const svg = `<svg height=\"26\" width=\"26\" viewBox=\"0 0 26 26\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n    <g transform=\"rotate(${angle} 13 13)\">\n      <polygon fill=\"${color}\" points=\"6,10 20,10 13,17\"/>\n    </g>\n  </svg>`;\n  const encoded = base64encode(svg);\n  return `url(data:image/svg+xml;base64,${encoded})`;\n};\n\nexport const createScrollbars = (variant = 'default') => css`\n  ::-webkit-scrollbar {\n    width: 26px;\n    height: 26px;\n  }\n  ::-webkit-scrollbar-track {\n    ${({ theme }) =>\n      createHatchedBackground({\n        mainColor: variant === 'flat' ? theme.flatLight : theme.material,\n        secondaryColor: variant === 'flat' ? theme.canvas : theme.borderLightest\n      })}\n  }\n  ::-webkit-scrollbar-thumb {\n    ${createBoxStyles()}\n    ${variant === 'flat'\n      ? createFlatBoxStyles()\n      : createBorderStyles({ style: 'window' })}\n      outline-offset: -2px;\n  }\n\n  ::-webkit-scrollbar-corner {\n    background-color: ${({ theme }) => theme.material};\n  }\n  ::-webkit-scrollbar-button {\n    ${createBoxStyles()}\n    ${variant === 'flat'\n      ? createFlatBoxStyles()\n      : createBorderStyles({ style: 'window' })}\n      display: block;\n    outline-offset: -2px;\n    height: 26px;\n    width: 26px;\n    background-repeat: no-repeat;\n    background-size: 100%;\n    background-position: 0 0;\n  }\n  ::-webkit-scrollbar-button:active,\n  ::-webkit-scrollbar-button:active {\n    background-position: 0 1px;\n    ${variant === 'default'\n      ? createBorderStyles({ style: 'window', invert: true })\n      : ''}\n  }\n\n  ::-webkit-scrollbar-button:horizontal:increment:start,\n  ::-webkit-scrollbar-button:horizontal:decrement:end,\n  ::-webkit-scrollbar-button:vertical:increment:start,\n  ::-webkit-scrollbar-button:vertical:decrement:end {\n    display: none;\n  }\n\n  ::-webkit-scrollbar-button:horizontal:decrement {\n    background-image: ${({ theme }) =>\n      createTriangleSVG(theme.materialText, 90)};\n  }\n\n  ::-webkit-scrollbar-button:horizontal:increment {\n    background-image: ${({ theme }) =>\n      createTriangleSVG(theme.materialText, 270)};\n  }\n\n  ::-webkit-scrollbar-button:vertical:decrement {\n    background-image: ${({ theme }) =>\n      createTriangleSVG(theme.materialText, 180)};\n  }\n\n  ::-webkit-scrollbar-button:vertical:increment {\n    background-image: ${({ theme }) =>\n      createTriangleSVG(theme.materialText, 0)};\n  }\n`;\n"
  },
  {
    "path": "src/common/styleReset.ts",
    "content": "export default `\n  html,\nbody,\ndiv,\nspan,\napplet,\nobject,\niframe,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\np,\nblockquote,\npre,\na,\nabbr,\nacronym,\naddress,\nbig,\ncite,\ncode,\ndel,\ndfn,\nem,\nimg,\nins,\nkbd,\nq,\ns,\nsamp,\nsmall,\nstrike,\nstrong,\nsub,\nsup,\ntt,\nvar,\nb,\nu,\ni,\ncenter,\ndl,\ndt,\ndd,\nol,\nul,\nli,\nfieldset,\nform,\nlabel,\nlegend,\ntable,\ncaption,\ntbody,\ntfoot,\nthead,\ntr,\nth,\ntd,\narticle,\naside,\ncanvas,\ndetails,\nembed,\nfigure,\nfigcaption,\nfooter,\nheader,\nhgroup,\nmenu,\nnav,\noutput,\nruby,\nsection,\nsummary,\ntime,\nmark,\naudio,\nvideo {\n  margin: 0;\n  padding: 0;\n  border: 0;\n  font-size: 100%;\n  font: inherit;\n  vertical-align: baseline;\n}\n/* HTML5 display-role reset for older browsers */\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmenu,\nnav,\nsection {\n  display: block;\n}\nbody {\n  line-height: 1.5;\n}\nol,\nul {\n  list-style: none;\n}\nblockquote,\nq {\n  quotes: none;\n}\nblockquote:before,\nblockquote:after,\nq:before,\nq:after {\n  content: \"\";\n  content: none;\n}\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\na {\n  color: inherit;\n  text-decoration: none;\n}\nul,\nli {\n  list-style-type: none;\n}\nbutton {\n  outline: none;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n  margin: 0;\n  padding: 0;\n  font-family: sans-serif;\n  color: black;\n}\n\ncode {\n  font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n    monospace;\n}\n\ninput[type=\"number\"]::-webkit-outer-spin-button,\ninput[type=\"number\"]::-webkit-inner-spin-button {\n  -webkit-appearance: none;\n  margin: 0;\n}\n\ninput[type=\"number\"] {\n  -moz-appearance: textfield;\n}\n\n`;\n"
  },
  {
    "path": "src/common/system.ts",
    "content": "// TODO - implement styled-system\n\nimport { Sizes } from '../types';\n\nexport const blockSizes: Record<Sizes, string> = {\n  sm: '28px',\n  md: '36px',\n  lg: '44px'\n};\n"
  },
  {
    "path": "src/common/themes/aiee.ts",
    "content": "/* \"AIEE\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Aiee-668092636\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'aiee',\n  anchor: 'rgb(0,0,128)',\n  anchorVisited: 'rgb(0,0,128)',\n  borderDark: 'rgb(211,214,217)',\n  borderDarkest: 'rgb(65,187,241)',\n  borderLight: 'rgb(238,244,252)',\n  borderLightest: 'rgb(250,254,255)',\n  canvas: 'rgb(255,255,255)',\n  canvasText: 'rgb(0,62,1090)',\n  canvasTextDisabled: 'rgb(211,214,217)',\n  canvasTextDisabledShadow: 'rgb(250,254,255)',\n  canvasTextInvert: 'rgb(0,62,109)',\n  checkmark: 'rgb(0,62,1090)',\n  checkmarkDisabled: 'rgb(88,152,231)',\n  desktopBackground: 'rgb(68,125,183)',\n  flatDark: 'rgb(211,214,217)',\n  flatLight: 'rgb(238,244,252)',\n  focusSecondary: 'rgb(250,254,255)',\n  headerBackground:\n    'linear-gradient(to right, rgb(4,118,180), rgb(251,211,61))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(0,159,223), rgb(0,207,247))',\n  headerNotActiveText: 'rgb(0,62,109)',\n  headerText: 'rgb(255,255,255)',\n  hoverBackground: 'rgb(251,211,61)',\n  material: 'rgb(227,238,251)',\n  materialDark: 'rgb(0,159,223)',\n  materialText: 'rgb(0,62,109)',\n  materialTextDisabled: 'rgb(211,214,217)',\n  materialTextDisabledShadow: 'rgb(250,254,255)',\n  materialTextInvert: 'rgb(0,62,109)',\n  progress: 'rgb(251,211,61)',\n  tooltip: 'rgb(255,243,185)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/ash.ts",
    "content": "/* \"Ash\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Ash-575566643\n */\nimport { Theme } from './types';\n\nexport default {\n  name: 'ash',\n  anchor: 'rgb(192, 192, 192)',\n  anchorVisited: 'rgb(192, 192, 192)',\n  borderDark: 'rgb(63, 63, 63)',\n  borderDarkest: 'rgb(0, 0, 0)',\n  borderLight: 'rgb(115, 115, 115)',\n  borderLightest: 'rgb(175, 175, 175)',\n  canvas: 'rgb(64, 64, 64)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(63, 63, 63)',\n  canvasTextDisabledShadow: 'rgb(175, 175, 175)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(128, 128, 128)',\n  desktopBackground: 'rgb(0, 0, 0)',\n  flatDark: 'rgb(63, 63, 63)',\n  flatLight: 'rgb(96, 96, 96)',\n  focusSecondary: 'rgb(175, 175, 175)',\n  headerBackground: 'linear-gradient(to right, rgb(0, 0, 0), rgb(0, 0, 0))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(63, 63, 63), rgb(0, 0, 0))',\n  headerNotActiveText: 'rgb(128, 128, 128)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(0, 0, 0)',\n  material: 'rgb(96, 96, 96)',\n  materialDark: 'rgb(63, 63, 63)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(63, 63, 63)',\n  materialTextDisabledShadow: 'rgb(175, 175, 175)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(0, 0, 0)',\n  tooltip: 'rgb(0, 0, 0)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/azureOrange.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'azureOrange',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#05427f',\n  borderDarkest: '#000000',\n  borderLight: '#2b8fff',\n  borderLightest: '#7ebfff',\n  canvas: '#ffffff',\n  canvasText: '#000000',\n  canvasTextDisabled: '#05427f',\n  canvasTextDisabledShadow: '#7ebfff',\n  canvasTextInvert: '#000000',\n  checkmark: '#000000',\n  checkmarkDisabled: '#05427f',\n  desktopBackground: '#ff7d01',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#171123',\n  headerBackground: '#171123',\n  headerNotActiveBackground: '#4E6766',\n  headerNotActiveText: '#0180ff',\n  headerText: '#ffffff',\n  hoverBackground: '#F46036',\n  material: '#0180ff',\n  materialDark: '#9a9e9c',\n  materialText: '#000000',\n  materialTextDisabled: '#05427f',\n  materialTextDisabledShadow: '#7ebfff',\n  materialTextInvert: '#000000',\n  progress: '#F46036',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/bee.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'bee',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#846d06',\n  borderDarkest: '#0C1618',\n  borderLight: '#e7c221',\n  borderLightest: '#f8df6e',\n  canvas: '#ffffff',\n  canvasText: '#0C1618',\n  canvasTextDisabled: '#846d06',\n  canvasTextDisabledShadow: '#f8df6e',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#0C1618',\n  checkmarkDisabled: '#846d06',\n  desktopBackground: '#977800',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#0C1618',\n  headerNotActiveBackground: '#7F7B82',\n  headerNotActiveText: '#e5bd03',\n  headerText: '#f8df6e',\n  hoverBackground: '#0C1618',\n  material: '#e5bd03',\n  materialDark: '#7F7B82',\n  materialText: '#0C1618',\n  materialTextDisabled: '#846d06',\n  materialTextDisabledShadow: '#f8df6e',\n  materialTextInvert: '#ffffff',\n  progress: '#0C1618',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/blackAndWhite.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'blackAndWhite',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#888c8f',\n  borderDarkest: '#000000',\n  borderLight: '#dfe0e3',\n  borderLightest: '#888c8f',\n  canvas: '#ffffff',\n  canvasText: '#000000',\n  canvasTextDisabled: '#888c8f',\n  canvasTextDisabledShadow: '#ffffff',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#000000',\n  checkmarkDisabled: '#888c8f',\n  desktopBackground: '#ffffff',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#000000',\n  headerNotActiveBackground: '#ffffff',\n  headerNotActiveText: '#000000',\n  headerText: '#ffffff',\n  hoverBackground: '#000000',\n  material: '#ffffff',\n  materialDark: '#9a9e9c',\n  materialText: '#000000',\n  materialTextDisabled: '#888c8f',\n  materialTextDisabledShadow: '#ffffff',\n  materialTextInvert: '#ffffff',\n  progress: '#000000',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/blue.ts",
    "content": "/* \"Blue\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Blue-525167751\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'blue',\n  anchor: 'rgb(0, 0, 128)',\n  anchorVisited: 'rgb(0, 0, 128)',\n  borderDark: 'rgb(49, 131, 221)',\n  borderDarkest: 'rgb(0, 0, 0)',\n  borderLight: 'rgb(166, 202, 240)',\n  borderLightest: 'rgb(211, 228, 248)',\n  canvas: 'rgb(255, 255, 255)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(49, 131, 221)',\n  canvasTextDisabledShadow: 'rgb(211, 228, 248)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(49, 131, 221)',\n  desktopBackground: 'rgb(58, 110, 165)',\n  flatDark: 'rgb(49, 131, 221)',\n  flatLight: 'rgb(166, 202, 240)',\n  focusSecondary: 'rgb(211, 228, 248)',\n  headerBackground:\n    'linear-gradient(to right, rgb(0, 0, 128), rgb(16, 132, 208))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(49, 131, 221), rgb(49, 131, 221))',\n  headerNotActiveText: 'rgb(0, 0, 128)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(51, 153, 255)',\n  material: 'rgb(166, 202, 240)',\n  materialDark: 'rgb(49, 131, 221)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(49, 131, 221)',\n  materialTextDisabledShadow: 'rgb(211, 228, 248)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(51, 153, 255)',\n  tooltip: 'rgb(225, 225, 255)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/brick.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'brick',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#6c684b',\n  borderDarkest: '#020000',\n  borderLight: '#e2ddc9',\n  borderLightest: '#ffffff',\n  canvas: '#ffffff',\n  canvasText: '#020000',\n  canvasTextDisabled: '#6c684b',\n  canvasTextDisabledShadow: '#ffffff',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#020000',\n  checkmarkDisabled: '#6c684b',\n  desktopBackground: '#420000',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#8e0101',\n  headerNotActiveBackground: '#90885c',\n  headerNotActiveText: '#c2bfa3',\n  headerText: '#ffffff',\n  hoverBackground: '#8e0101',\n  material: '#c2bfa3',\n  materialDark: '#9a9e9c',\n  materialText: '#020000',\n  materialTextDisabled: '#6c684b',\n  materialTextDisabledShadow: '#ffffff',\n  materialTextInvert: '#ffffff',\n  progress: '#8e0101',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/candy.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'candy',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#d1579e',\n  borderDarkest: '#44132f',\n  borderLight: '#f1acd5',\n  borderLightest: '#EFF1F3',\n  canvas: '#EFF1F3',\n  canvasText: '#000000',\n  canvasTextDisabled: '#d1579e',\n  canvasTextDisabledShadow: '#EFF1F3',\n  canvasTextInvert: '#EFF1F3',\n  checkmark: '#000000',\n  checkmarkDisabled: '#d1579e',\n  desktopBackground: '#b477bd',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#87255B',\n  headerNotActiveBackground: '#a08796',\n  headerNotActiveText: '#EBD2BE',\n  headerText: '#EFF1F3',\n  hoverBackground: '#256EFF',\n  material: '#E5A4CB',\n  materialDark: '#9a9e9c',\n  materialText: '#000000',\n  materialTextDisabled: '#d1579e',\n  materialTextDisabledShadow: '#EFF1F3',\n  materialTextInvert: '#EFF1F3',\n  progress: '#256EFF',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/cherry.ts",
    "content": "/* \"Cherry\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Cherry-747961418\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'cherry',\n  anchor: 'rgb(128, 0, 1)',\n  anchorVisited: 'rgb(128, 0, 1)',\n  borderDark: 'rgb(128, 128, 128)',\n  borderDarkest: 'rgb(64, 64, 64)',\n  borderLight: 'rgb(200, 212, 208)',\n  borderLightest: 'rgb(255, 255, 255)',\n  canvas: 'rgb(255, 255, 255)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(128, 128, 128)',\n  canvasTextDisabledShadow: 'rgb(255, 255, 255)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(128, 128, 128)',\n  desktopBackground: 'rgb(165, 58, 109)',\n  flatDark: 'rgb(128, 128, 128)',\n  flatLight: 'rgb(200, 212, 208)',\n  focusSecondary: 'rgb(255, 255, 255)',\n  headerBackground:\n    'linear-gradient(to right, rgb(106, 10, 36), rgb(240, 166, 202))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(128, 128, 128), rgb(192, 192, 192))',\n  headerNotActiveText: 'rgb(212, 208, 200)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(106, 10, 36)',\n  material: 'rgb(200, 212, 208)',\n  materialDark: 'rgb(128, 128, 128)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(128, 128, 128)',\n  materialTextDisabledShadow: 'rgb(255, 255, 255)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(106, 10, 36)',\n  tooltip: 'rgb(225, 254, 255)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/coldGray.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'coldGray',\n\n  anchor: '#8d88c2',\n  anchorVisited: '#440381',\n  background: '#4C6663',\n  borderDark: '#5b57a1',\n  borderDarkest: '#010601',\n  borderLight: '#a4a7c8',\n  borderLightest: '#c7c7df',\n  canvas: '#c7c7df',\n  canvasText: '#050608',\n  canvasTextDisabled: '#888c8f',\n  canvasTextDisabledShadow: '#ffffff',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#010601',\n  checkmarkDisabled: '#5b57a1',\n  desktopBackground: '#606286',\n  flatDark: '#5b57a1',\n  flatLight: '#a4a7c8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#3B3D64',\n  headerNotActiveBackground: '#6063a5',\n  headerNotActiveText: '#a1a3ca',\n  headerText: '#010601',\n  hoverBackground: '#8d88c2',\n  material: '#a1a3ca',\n  materialDark: '#6063a5',\n  materialText: '#010601',\n  materialTextDisabled: '#5b57a1',\n  materialTextDisabledShadow: '#c7c7df',\n  materialTextInvert: '#c7c7df',\n  progress: '#8d88c2',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/counterStrike.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'counterStrike',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#2c3125',\n  borderDarkest: '#0a0a0a',\n  borderLight: '#5d6d54',\n  borderLightest: '#788475',\n  canvas: '#3e4639',\n  canvasText: '#f6fbf5',\n  canvasTextDisabled: '#2c3125',\n  canvasTextDisabledShadow: '#788475',\n  canvasTextInvert: '#f6fbf5',\n  checkmark: '#f6fbf5',\n  checkmarkDisabled: '#2c3125',\n  desktopBackground: '#bcbd52',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#4b5844',\n  headerNotActiveBackground: '#4b5844',\n  headerNotActiveText: '#74806e',\n  headerText: '#fefefe',\n  hoverBackground: '#978830',\n  material: '#4b5844',\n  materialDark: '#2f3428',\n  materialText: '#f6fbf5',\n  materialTextDisabled: '#2c3125',\n  materialTextDisabledShadow: '#788475',\n  materialTextInvert: '#fefefe',\n  progress: '#978830',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/darkTeal.ts",
    "content": "/* \"Teal for Shelbi - Dark\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Teal-for-Shelbi-Dark-631177772\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'darkTeal',\n  anchor: 'rgb(0, 132, 132)',\n  anchorVisited: 'rgb(0, 132, 132)',\n  borderDark: 'rgb(21, 43, 43)',\n  borderDarkest: 'rgb(0, 0, 0)',\n  borderLight: 'rgb(32, 64, 64)',\n  borderLightest: 'rgb(88, 139, 139)',\n  canvas: 'rgb(0, 48, 48)',\n  canvasText: 'rgb(255, 255, 255)',\n  canvasTextDisabled: 'rgb(21, 43, 43)',\n  canvasTextDisabledShadow: 'rgb(88, 139, 139)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(255, 255, 255)',\n  checkmarkDisabled: 'rgb(64, 128, 128)',\n  desktopBackground: 'rgb(0, 64, 64)',\n  flatDark: 'rgb(21, 43, 43)',\n  flatLight: 'rgb(32, 64, 64)',\n  focusSecondary: 'rgb(88, 139, 139)',\n  headerBackground:\n    'linear-gradient(to right, rgb(0, 96, 96), rgb(0, 132, 132))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(24, 50, 50), rgb(92, 112, 112))',\n  headerNotActiveText: 'rgb(192, 204, 204)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(0, 128, 128)',\n  material: 'rgb(32, 64, 64)',\n  materialDark: 'rgb(24, 50, 50)',\n  materialText: 'rgb(255, 255, 255)',\n  materialTextDisabled: 'rgb(21, 43, 43)',\n  materialTextDisabledShadow: 'rgb(88, 139, 139)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(0, 128, 128)',\n  tooltip: 'rgb(0, 32, 32)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/denim.ts",
    "content": "/* \"Denim\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Denim-870494744\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'denim',\n  anchor: 'rgb(0, 0, 128)',\n  anchorVisited: 'rgb(0, 0, 128)',\n  borderDark: 'rgb(0, 0, 255)',\n  borderDarkest: 'rgb(64, 64, 64)',\n  borderLight: 'rgb(128, 128, 255)',\n  borderLightest: 'rgb(191, 191, 255)',\n  canvas: 'rgb(255, 255, 255)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(0, 0, 255)',\n  canvasTextDisabledShadow: 'rgb(191, 191, 255)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(0, 0, 255)',\n  desktopBackground: 'rgb(0, 64, 128)',\n  flatDark: 'rgb(0, 0, 255)',\n  flatLight: 'rgb(128, 128, 255)',\n  focusSecondary: 'rgb(191, 191, 255)',\n  headerBackground:\n    'linear-gradient(to right, rgb(10, 36, 106), rgb(148, 190, 237))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(0, 0, 255), rgb(180, 180, 180))',\n  headerNotActiveText: 'rgb(192, 192, 192)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(10, 36, 106)',\n  material: 'rgb(128, 128, 255)',\n  materialDark: 'rgb(0, 0, 255)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(0, 0, 255)',\n  materialTextDisabledShadow: 'rgb(191, 191, 255)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(10, 36, 106)',\n  tooltip: 'rgb(255, 255, 225)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/eggplant.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'eggplant',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#526d67',\n  borderDarkest: '#050608',\n  borderLight: '#a2c7c0',\n  borderLightest: '#cee8e3',\n  canvas: '#ffffff',\n  canvasText: '#050608',\n  canvasTextDisabled: '#526d67',\n  canvasTextDisabledShadow: '#cee8e3',\n  canvasTextInvert: '#cee8e3',\n  checkmark: '#050608',\n  checkmarkDisabled: '#526d67',\n  desktopBackground: '#400040',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#4b8178',\n  headerNotActiveBackground: '#89b0a8',\n  headerNotActiveText: '#4b8178',\n  headerText: '#ffffff',\n  hoverBackground: '#4b8178',\n  material: '#89b0a8',\n  materialDark: '#9a9e9c',\n  materialText: '#050608',\n  materialTextDisabled: '#526d67',\n  materialTextDisabledShadow: '#cee8e3',\n  materialTextInvert: '#ffffff',\n  progress: '#4b8178',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/fxDev.ts",
    "content": "/* \"FxDev\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/FxDev-701274128\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'fxDev',\n  anchor: 'rgb(47, 138, 202)',\n  anchorVisited: 'rgb(47, 138, 202)',\n  borderDark: 'rgb(27, 33, 39)',\n  borderDarkest: 'rgb(7, 9, 10)',\n  borderLight: 'rgb(69, 82, 94)',\n  borderLightest: 'rgb(107, 113, 122)',\n  canvas: 'rgb(23, 27, 31)',\n  canvasText: 'rgb(245, 247, 250)',\n  canvasTextDisabled: 'rgb(27, 33, 39)',\n  canvasTextDisabledShadow: 'rgb(107, 113, 122)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(245, 247, 250)',\n  checkmarkDisabled: 'rgb(133, 136, 140)',\n  desktopBackground: 'rgb(26, 58, 99)',\n  flatDark: 'rgb(27, 33, 39)',\n  flatLight: 'rgb(57, 66, 77)',\n  focusSecondary: 'rgb(107, 113, 122)',\n  headerBackground:\n    'linear-gradient(to right, rgb(26, 70, 102), rgb(26, 70, 102))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(28, 33, 38), rgb(28, 33, 38))',\n  headerNotActiveText: 'rgb(245, 247, 250)',\n  headerText: 'rgb(245, 247, 250)',\n  hoverBackground: 'rgb(7, 77, 117)',\n  material: 'rgb(57, 66, 77)',\n  materialDark: 'rgb(28, 33, 38)',\n  materialText: 'rgb(255, 255, 255)',\n  materialTextDisabled: 'rgb(27, 33, 39)',\n  materialTextDisabledShadow: 'rgb(107, 113, 122)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(7, 77, 117)',\n  tooltip: 'rgb(243, 242, 219)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/highContrast.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'highContrast',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#888c8f',\n  borderDarkest: '#ffffff',\n  borderLight: '#dfe0e3',\n  borderLightest: '#ffffff',\n  canvas: '#353535',\n  canvasText: '#ffffff',\n  canvasTextDisabled: '#888c8f',\n  canvasTextDisabledShadow: '#ffffff',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#ffffff',\n  checkmarkDisabled: '#888c8f',\n  desktopBackground: '#000000',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#8e0284',\n  headerNotActiveBackground: '#7f7f7f',\n  headerNotActiveText: '#ced0cf',\n  headerText: '#ffffff',\n  hoverBackground: '#8e0284',\n  material: '#000000',\n  materialDark: '#9a9e9c',\n  materialText: '#ffffff',\n  materialTextDisabled: '#888c8f',\n  materialTextDisabledShadow: '#ffffff',\n  materialTextInvert: '#ffffff',\n  progress: '#8e0284',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/honey.ts",
    "content": "/* \"Honey\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Honey-632126512\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'honey',\n  anchor: 'rgb(128, 128, 0)',\n  anchorVisited: 'rgb(128, 128, 0)',\n  borderDark: 'rgb(170, 123, 0)',\n  borderDarkest: 'rgb(64, 64, 64)',\n  borderLight: 'rgb(255, 186, 0)',\n  borderLightest: 'rgb(255, 220, 128)',\n  canvas: 'rgb(255, 220, 128)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(170, 123, 0)',\n  canvasTextDisabledShadow: 'rgb(255, 220, 128)',\n  canvasTextInvert: 'rgb(255, 255, 0)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(170, 123, 0)',\n  desktopBackground: 'rgb(170, 123, 0)',\n  flatDark: 'rgb(170, 123, 0)',\n  flatLight: 'rgb(255, 186, 0)',\n  focusSecondary: 'rgb(255, 220, 128)',\n  headerBackground:\n    'linear-gradient(to right, rgb(255, 255, 0), rgb(255, 186, 0))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(255, 186, 0), rgb(255, 220, 128))',\n  headerNotActiveText: 'rgb(170, 123, 0)',\n  headerText: 'rgb(0, 0, 0)',\n  hoverBackground: 'rgb(170, 123, 0)',\n  material: 'rgb(255, 186, 0)',\n  materialDark: 'rgb(255, 186, 0)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(170, 123, 0)',\n  materialTextDisabledShadow: 'rgb(255, 220, 128)',\n  materialTextInvert: 'rgb(255, 255, 0)',\n  progress: 'rgb(170, 123, 0)',\n  tooltip: 'rgb(255, 220, 128)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/hotChocolate.ts",
    "content": "/* \"Hot Chocolate\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Hot-Chocolate-654380979\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'hotChocolate',\n  anchor: 'rgb(128, 96, 64)',\n  anchorVisited: 'rgb(128, 96, 64)',\n  borderDark: 'rgb(128, 96, 64)',\n  borderDarkest: 'rgb(64, 64, 64)',\n  borderLight: 'rgb(181, 143, 106)',\n  borderLightest: 'rgb(219, 200, 181)',\n  canvas: 'rgb(219, 200, 181)',\n  canvasText: 'rgb(57, 43, 28)',\n  canvasTextDisabled: 'rgb(128, 96, 64)',\n  canvasTextDisabledShadow: 'rgb(219, 200, 181)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(57, 43, 28)',\n  checkmarkDisabled: 'rgb(128, 96, 64)',\n  desktopBackground: 'rgb(109, 82, 54)',\n  flatDark: 'rgb(128, 96, 64)',\n  flatLight: 'rgb(181, 143, 106)',\n  focusSecondary: 'rgb(219, 200, 181)',\n  headerBackground:\n    'linear-gradient(to right, rgb(128, 96, 64), rgb(208, 183, 157))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(128, 96, 64), rgb(128, 96, 64))',\n  headerNotActiveText: 'rgb(219, 200, 181)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(128, 96, 64)',\n  material: 'rgb(181, 143, 106)',\n  materialDark: 'rgb(128, 96, 64)',\n  materialText: 'rgb(57, 43, 28)',\n  materialTextDisabled: 'rgb(128, 96, 64)',\n  materialTextDisabledShadow: 'rgb(219, 200, 181)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(128, 96, 64)',\n  tooltip: 'rgb(219, 200, 181)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/hotdogStand.ts",
    "content": "/* \"Hotdog Stand\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Hotdog-Stand-525606846\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'hotdogStand',\n  anchor: 'rgb(255, 255, 0)',\n  anchorVisited: 'rgb(255, 255, 0)',\n  borderDark: 'rgb(0, 0, 0)',\n  borderDarkest: 'rgb(0, 0, 0)',\n  borderLight: 'rgb(0, 0, 0)',\n  borderLightest: 'rgb(0, 0, 0)',\n  canvas: 'rgb(255, 0, 0)',\n  canvasText: 'rgb(255, 255, 255)',\n  canvasTextDisabled: 'rgb(0, 0, 0)',\n  canvasTextDisabledShadow: 'rgb(0, 0, 0)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(255, 255, 255)',\n  checkmarkDisabled: 'rgb(128, 128, 128)',\n  desktopBackground: 'rgb(255, 255, 0)',\n  flatDark: 'rgb(0, 0, 0)',\n  flatLight: 'rgb(0, 0, 0)',\n  focusSecondary: 'rgb(0, 0, 0)',\n  headerBackground: 'linear-gradient(to right, rgb(0, 0, 0), rgb(0, 0, 0))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(255, 0, 0), rgb(255, 0, 0))',\n  headerNotActiveText: 'rgb(255, 255, 255)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(0, 0, 0)',\n  material: 'rgb(255, 0, 0)',\n  materialDark: 'rgb(255, 0, 0)',\n  materialText: 'rgb(255, 255, 255)',\n  materialTextDisabled: 'rgb(0, 0, 0)',\n  materialTextDisabledShadow: 'rgb(0, 0, 0)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(0, 0, 0)',\n  tooltip: 'rgb(255, 255, 225)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/index.ts",
    "content": "import aiee from './aiee';\nimport ash from './ash';\nimport azureOrange from './azureOrange';\nimport bee from './bee';\nimport blackAndWhite from './blackAndWhite';\nimport blue from './blue';\nimport brick from './brick';\nimport candy from './candy';\nimport cherry from './cherry';\nimport coldGray from './coldGray';\nimport counterStrike from './counterStrike';\nimport darkTeal from './darkTeal';\nimport denim from './denim';\nimport eggplant from './eggplant';\nimport fxDev from './fxDev';\nimport highContrast from './highContrast';\nimport honey from './honey';\nimport hotChocolate from './hotChocolate';\nimport hotdogStand from './hotdogStand';\nimport lilac from './lilac';\nimport lilacRoseDark from './lilacRoseDark';\nimport maple from './maple';\nimport marine from './marine';\nimport matrix from './matrix';\nimport millenium from './millenium';\nimport modernDark from './modernDark';\nimport molecule from './molecule';\nimport ninjaTurtles from './ninjaTurtles';\nimport olive from './olive';\nimport original from './original';\nimport pamelaAnderson from './pamelaAnderson';\nimport peggysPastels from './peggysPastels';\nimport plum from './plum';\nimport polarized from './polarized';\nimport powerShell from './powerShell';\nimport rainyDay from './rainyDay';\nimport raspberry from './raspberry';\nimport redWine from './redWine';\nimport rose from './rose';\nimport seawater from './seawater';\nimport shelbiTeal from './shelbiTeal';\nimport slate from './slate';\nimport solarizedDark from './solarizedDark';\nimport solarizedLight from './solarizedLight';\nimport spruce from './spruce';\nimport stormClouds from './stormClouds';\nimport theSixtiesUSA from './theSixtiesUSA';\nimport tokyoDark from './tokyoDark';\nimport toner from './toner';\nimport tooSexy from './tooSexy';\nimport travel from './travel';\nimport vaporTeal from './vaporTeal';\nimport vermillion from './vermillion';\nimport violetDark from './violetDark';\nimport vistaesqueMidnight from './vistaesqueMidnight';\nimport water from './water';\nimport white from './white';\nimport windows1 from './windows1';\nimport wmii from './wmii';\n\nexport default {\n  aiee,\n  ash,\n  azureOrange,\n  bee,\n  blackAndWhite,\n  blue,\n  brick,\n  candy,\n  cherry,\n  coldGray,\n  counterStrike,\n  darkTeal,\n  denim,\n  eggplant,\n  fxDev,\n  highContrast,\n  honey,\n  hotChocolate,\n  hotdogStand,\n  lilac,\n  lilacRoseDark,\n  maple,\n  marine,\n  matrix,\n  millenium,\n  modernDark,\n  molecule,\n  ninjaTurtles,\n  olive,\n  original,\n  pamelaAnderson,\n  peggysPastels,\n  plum,\n  polarized,\n  powerShell,\n  rainyDay,\n  raspberry,\n  redWine,\n  rose,\n  seawater,\n  shelbiTeal,\n  slate,\n  solarizedDark,\n  solarizedLight,\n  spruce,\n  stormClouds,\n  theSixtiesUSA,\n  tokyoDark,\n  toner,\n  tooSexy,\n  travel,\n  vaporTeal,\n  vermillion,\n  violetDark,\n  vistaesqueMidnight,\n  water,\n  white,\n  windows1,\n  wmii\n};\n"
  },
  {
    "path": "src/common/themes/lilac.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'lilac',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#5f549b',\n  borderDarkest: '#1c1449',\n  borderLight: '#bcb4e9',\n  borderLightest: '#d3ccf4',\n  canvas: '#ffffff',\n  canvasText: '#050608',\n  canvasTextDisabled: '#5f549b',\n  canvasTextDisabledShadow: '#ffffff',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#050608',\n  checkmarkDisabled: '#5f549b',\n  desktopBackground: '#000000',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#5e4dba',\n  headerNotActiveBackground: '#7f7f81',\n  headerNotActiveText: '#ced0cf',\n  headerText: '#ffffff',\n  hoverBackground: '#5e4dba',\n  material: '#b1a7df',\n  materialDark: '#9a9e9c',\n  materialText: '#050608',\n  materialTextDisabled: '#5f549b',\n  materialTextDisabledShadow: '#ffffff',\n  materialTextInvert: '#ffffff',\n  progress: '#5e4dba',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/lilacRoseDark.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'lilacRoseDark',\n\n  anchor: '#a65387',\n  anchorVisited: '#440381',\n  background: '#3B3B58',\n  borderDark: '#7F3163',\n  borderDarkest: '#190000',\n  borderLight: '#E597C9',\n  borderLightest: '#FFCAFC',\n  canvas: '#dab1c7',\n  canvasText: '#000000',\n  canvasTextDisabled: '#000000',\n  canvasTextDisabledShadow: '#000000',\n  canvasTextInvert: '#ecbfe3',\n  checkmark: '#010601',\n  checkmarkDisabled: '#7F3163',\n  desktopBackground: '#663956',\n  flatDark: '#7F3163',\n  flatLight: '#E597C9',\n  focusSecondary: '#fefe03',\n  headerBackground: '#4C0030',\n  headerNotActiveBackground: '#763a60',\n  headerNotActiveText: '#b26496',\n  headerText: '#010601',\n  hoverBackground: '#713259',\n  material: '#b26496',\n  materialDark: '#763a60',\n  materialText: '#000000',\n  materialTextDisabled: '#82416d',\n  materialTextDisabledShadow: '#ecbfe3',\n  materialTextInvert: '#ecbfe3',\n  progress: '#713259',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/maple.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'maple',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#ab9042',\n  borderDarkest: '#2a1801',\n  borderLight: '#f5e2bb',\n  borderLightest: '#ffffff',\n  canvas: '#ffffff',\n  canvasText: '#2a1801',\n  canvasTextDisabled: '#ab9042',\n  canvasTextDisabledShadow: '#ffffff',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#2a1801',\n  checkmarkDisabled: '#ab9042',\n  desktopBackground: '#000000',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#8e0101',\n  headerNotActiveBackground: '#a1a0a5',\n  headerNotActiveText: '#f5e2bb',\n  headerText: '#ffffff',\n  hoverBackground: '#8e0101',\n  material: '#e5cc90',\n  materialDark: '#9a9e9c',\n  materialText: '#2a1801',\n  materialTextDisabled: '#ab9042',\n  materialTextDisabledShadow: '#ffffff',\n  materialTextInvert: '#ffffff',\n  progress: '#8e0101',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/marine.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'marine',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#3c8d88',\n  borderDarkest: '#050608',\n  borderLight: '#98d2cb',\n  borderLightest: '#b1dfdf',\n  canvas: '#c3e2da',\n  canvasText: '#050608',\n  canvasTextDisabled: '#3c8d88',\n  canvasTextDisabledShadow: '#ffffff',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#050608',\n  checkmarkDisabled: '#3c8d88',\n  desktopBackground: '#2c4e47',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#000080',\n  headerNotActiveBackground: '#7f7f7f',\n  headerNotActiveText: '#ced0cf',\n  headerText: '#ffffff',\n  hoverBackground: '#000080',\n  material: '#75c1ba',\n  materialDark: '#9a9e9c',\n  materialText: '#050608',\n  materialTextDisabled: '#3c8d88',\n  materialTextDisabledShadow: '#ffffff',\n  materialTextInvert: '#ffffff',\n  progress: '#000080',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/matrix.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'matrix',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#282828',\n  borderDarkest: '#000000',\n  borderLight: '#656565',\n  borderLightest: '#a7a7a7',\n  canvas: '#c0c0c0',\n  canvasText: '#000000',\n  canvasTextDisabled: '#282828',\n  canvasTextDisabledShadow: '#ff0000',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#000000',\n  checkmarkDisabled: '#282828',\n  desktopBackground: '#000000',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#35FF69',\n  headerBackground: '#000000',\n  headerNotActiveBackground: '#7f7f7f',\n  headerNotActiveText: '#535353',\n  headerText: '#a7a7a7',\n  hoverBackground: '#000000',\n  material: '#535353',\n  materialDark: '#282828',\n  materialText: '#35FF69',\n  materialTextDisabled: '#282828',\n  materialTextDisabledShadow: '#a7a7a7',\n  materialTextInvert: '#ffffff',\n  progress: '#000000',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/millenium.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'millenium',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#828282',\n  borderDarkest: 'black',\n  borderLight: '#e5e5e5',\n  borderLightest: '#ffffff',\n  canvas: '#ffffff',\n  canvasText: 'black',\n  canvasTextDisabled: '#828282',\n  canvasTextDisabledShadow: '#ffffff',\n  canvasTextInvert: '#ffffff',\n  checkmark: 'black',\n  checkmarkDisabled: '#828282',\n  desktopBackground: '#3a6ea5',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: 'linear-gradient(to right, #012470, #a5c7e7)',\n  headerNotActiveBackground: '#7f7f7f',\n  headerNotActiveText: '#d6cfc7',\n  headerText: '#ffffff',\n  hoverBackground: '#00256e',\n  material: '#d6cfc7',\n  materialDark: '#9a9e9c',\n  materialText: 'black',\n  materialTextDisabled: '#828282',\n  materialTextDisabledShadow: '#ffffff',\n  materialTextInvert: '#ffffff',\n  progress: '#00256e',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/modernDark.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'modernDark',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#121317',\n  borderDarkest: '#000000',\n  borderLight: '#31323c',\n  borderLightest: '#4b4d58',\n  canvas: '#4b4d58',\n  canvasText: '#000000',\n  canvasTextDisabled: '#4b4d58',\n  canvasTextDisabledShadow: '#4b4d58',\n  canvasTextInvert: '#202127',\n  checkmark: '#000000',\n  checkmarkDisabled: '#121317',\n  desktopBackground: '#000000',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#4b4d58',\n  headerNotActiveBackground: 'transparent',\n  headerNotActiveText: '#4b4d58',\n  headerText: '#202127',\n  hoverBackground: '#f88702',\n  material: '#202127',\n  materialDark: '#9a9e9c',\n  materialText: '#f88702',\n  materialTextDisabled: '#4b4d58',\n  materialTextDisabledShadow: '#121317',\n  materialTextInvert: '#202127',\n  progress: '#f88702',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/molecule.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'molecule',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#993845',\n  borderDarkest: '#4b4d4e',\n  borderLight: '#dfe0e3',\n  borderLightest: '#d79099',\n  canvas: '#f1f5f6',\n  canvasText: '#020102',\n  canvasTextDisabled: '#993845',\n  canvasTextDisabledShadow: '#d79099',\n  canvasTextInvert: '#f1f5f6',\n  checkmark: '#020102',\n  checkmarkDisabled: '#993845',\n  desktopBackground: '#3a6ea5',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#a03d49',\n  headerNotActiveBackground: '#7f7f7f',\n  headerNotActiveText: '#c2c1c2',\n  headerText: '#f1f5f6',\n  hoverBackground: '#70a3ce',\n  material: '#c2c1c2',\n  materialDark: '#9a9e9c',\n  materialText: '#020102',\n  materialTextDisabled: '#993845',\n  materialTextDisabledShadow: '#d79099',\n  materialTextInvert: '#f1f5f6',\n  progress: '#a03d49',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/monochrome.ts",
    "content": ""
  },
  {
    "path": "src/common/themes/ninjaTurtles.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'ninjaTurtles',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#017401',\n  borderDarkest: '#000000',\n  borderLight: '#1dbc1b',\n  borderLightest: '#55fd55',\n  canvas: '#ffffff',\n  canvasText: '#000000',\n  canvasTextDisabled: '#017401',\n  canvasTextDisabledShadow: '#55fd55',\n  canvasTextInvert: '#000000',\n  checkmark: '#000000',\n  checkmarkDisabled: '#017401',\n  desktopBackground: '#045424',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#FF1D15',\n  headerNotActiveBackground: '#7f7f7f',\n  headerNotActiveText: '#000000',\n  headerText: '#ffffff',\n  hoverBackground: '#FABC3C',\n  material: '#00a800',\n  materialDark: '#9a9e9c',\n  materialText: '#000000',\n  materialTextDisabled: '#017401',\n  materialTextDisabledShadow: '#55fd55',\n  materialTextInvert: '#000000',\n  progress: '#FF1D15',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/olive.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'olive',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#4f4c02',\n  borderDarkest: '#000000',\n  borderLight: '#9d9d11',\n  borderLightest: '#fcfd3e',\n  canvas: '#ffffff',\n  canvasText: '#000000',\n  canvasTextDisabled: '#4f4c02',\n  canvasTextDisabledShadow: '#fcfd3e',\n  canvasTextInvert: '#000000',\n  checkmark: '#000000',\n  checkmarkDisabled: '#4f4c02',\n  desktopBackground: '#666633',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#000000',\n  headerBackground: '#F3DE2C',\n  headerNotActiveBackground: '#4f4c02',\n  headerNotActiveText: '#807f00',\n  headerText: '#000000',\n  hoverBackground: '#F3DE2C',\n  material: '#807f00',\n  materialDark: '#4f4c02',\n  materialText: '#000000',\n  materialTextDisabled: '#4f4c02',\n  materialTextDisabledShadow: '#fcfd3e',\n  materialTextInvert: '#000000',\n  progress: '#F3DE2C',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/original.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'original',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#848584',\n  borderDarkest: '#0a0a0a',\n  borderLight: '#dfdfdf',\n  borderLightest: '#fefefe',\n  canvas: '#ffffff',\n  canvasText: '#0a0a0a',\n  canvasTextDisabled: '#848584',\n  canvasTextDisabledShadow: '#fefefe',\n  canvasTextInvert: '#fefefe',\n  checkmark: '#0a0a0a',\n  checkmarkDisabled: '#848584',\n  desktopBackground: '#008080',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#060084',\n  headerNotActiveBackground: '#7f787f',\n  headerNotActiveText: '#c6c6c6',\n  headerText: '#fefefe',\n  hoverBackground: '#060084',\n  material: '#c6c6c6',\n  materialDark: '#9a9e9c',\n  materialText: '#0a0a0a',\n  materialTextDisabled: '#848584',\n  materialTextDisabledShadow: '#fefefe',\n  materialTextInvert: '#fefefe',\n  progress: '#060084',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/pamelaAnderson.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'pamelaAnderson',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#7e0541',\n  borderDarkest: '#000000',\n  borderLight: '#ff308f',\n  borderLightest: '#ff7ebf',\n  canvas: '#F5CCE8',\n  canvasText: '#000000',\n  canvasTextDisabled: '#7e0541',\n  canvasTextDisabledShadow: '#ff7ebf',\n  canvasTextInvert: '#F1E4E8',\n  checkmark: '#000000',\n  checkmarkDisabled: '#7e0541',\n  desktopBackground: '#000000',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#FF8CC6',\n  headerNotActiveBackground: '#95818D',\n  headerNotActiveText: '#ff0080',\n  headerText: '#000000',\n  hoverBackground: '#004FFF',\n  material: '#ff0080',\n  materialDark: '#95818D',\n  materialText: '#000000',\n  materialTextDisabled: '#7e0541',\n  materialTextDisabledShadow: '#ff7ebf',\n  materialTextInvert: '#F1E4E8',\n  progress: '#004FFF',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/peggysPastels.ts",
    "content": "/* \"Peggy's Pastels\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Peggy-s-Pastels-505540096\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'peggysPastels',\n  anchor: 'rgb(0, 128, 128)',\n  anchorVisited: 'rgb(0, 128, 128)',\n  borderDark: 'rgb(222, 69, 96)',\n  borderDarkest: 'rgb(64, 64, 64)',\n  borderLight: 'rgb(247, 219, 215)',\n  borderLightest: 'rgb(250, 224, 228)',\n  canvas: 'rgb(244, 255, 255)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(222, 69, 96)',\n  canvasTextDisabledShadow: 'rgb(250, 224, 228)',\n  canvasTextInvert: 'rgb(0, 0, 0)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(222, 69, 96)',\n  desktopBackground: 'rgb(162, 219, 210)',\n  flatDark: 'rgb(222, 69, 96)',\n  flatLight: 'rgb(247, 219, 215)',\n  focusSecondary: 'rgb(250, 224, 228)',\n  headerBackground:\n    'linear-gradient(to right, rgb(0, 191, 188), rgb(202, 156, 185))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(0, 187, 169), rgb(236, 145, 162))',\n  headerNotActiveText: 'rgb(0, 85, 77)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(162, 219, 210)',\n  material: 'rgb(244, 193, 202)',\n  materialDark: 'rgb(0, 187, 169)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(222, 69, 96)',\n  materialTextDisabledShadow: 'rgb(250, 224, 228)',\n  materialTextInvert: 'rgb(0, 0, 0)',\n  progress: 'rgb(162, 219, 210)',\n  tooltip: 'rgb(204, 255, 255)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/plum.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'plum',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#7b5f5b',\n  borderDarkest: '#050608',\n  borderLight: '#c3b1aa',\n  borderLightest: '#e8dad6',\n  canvas: '#dad0c7',\n  canvasText: '#050608',\n  canvasTextDisabled: '#7b5f5b',\n  canvasTextDisabledShadow: '#e8dad6',\n  canvasTextInvert: '#e8dad6',\n  checkmark: '#050608',\n  checkmarkDisabled: '#7b5f5b',\n  desktopBackground: '#402840',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#483f63',\n  headerNotActiveBackground: '#7d5e58',\n  headerNotActiveText: '#e8dad6',\n  headerText: '#ffffff',\n  hoverBackground: '#483f63',\n  material: '#ac978f',\n  materialDark: '#9a9e9c',\n  materialText: '#050608',\n  materialTextDisabled: '#7b5f5b',\n  materialTextDisabledShadow: '#e8dad6',\n  materialTextInvert: '#ffffff',\n  progress: '#483f63',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/polarized.ts",
    "content": "/* \"Polarized\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Polarized-557712217\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'polarized',\n  anchor: 'rgb(160, 160, 128)',\n  anchorVisited: 'rgb(160, 160, 128)',\n  borderDark: 'rgb(60, 60, 194)',\n  borderDarkest: 'rgb(60, 60, 194)',\n  borderLight: 'rgb(60, 60, 194)',\n  borderLightest: 'rgb(60, 60, 194)',\n  canvas: 'rgb(52, 52, 204)',\n  canvasText: 'rgb(255, 255, 0)',\n  canvasTextDisabled: 'rgb(60, 60, 194)',\n  canvasTextDisabledShadow: 'rgb(60, 60, 194)',\n  canvasTextInvert: 'rgb(0, 0, 255)',\n  checkmark: 'rgb(255, 255, 0)',\n  checkmarkDisabled: 'rgb(101, 101, 154)',\n  desktopBackground: 'rgb(180, 180, 76)',\n  flatDark: 'rgb(60, 60, 194)',\n  flatLight: 'rgb(60, 60, 194)',\n  focusSecondary: 'rgb(60, 60, 194)',\n  headerBackground:\n    'linear-gradient(to right, rgb(192, 192, 64), rgb(192, 192, 64))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(64, 64, 192), rgb(170, 170, 84))',\n  headerNotActiveText: 'rgb(192, 192, 192)',\n  headerText: 'rgb(0, 0, 255)',\n  hoverBackground: 'rgb(192, 192, 64)',\n  material: 'rgb(120, 120, 136)',\n  materialDark: 'rgb(64, 64, 192)',\n  materialText: 'rgb(255, 255, 0)',\n  materialTextDisabled: 'rgb(60, 60, 194)',\n  materialTextDisabledShadow: 'rgb(60, 60, 194)',\n  materialTextInvert: 'rgb(0, 0, 255)',\n  progress: 'rgb(192, 192, 64)',\n  tooltip: 'rgb(16, 16, 240)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/powerShell.ts",
    "content": "/* \"PowerShell\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/PowerShell-646065752\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'powerShell',\n  anchor: 'rgb(0, 192, 0)',\n  anchorVisited: 'rgb(0, 192, 0)',\n  borderDark: 'rgb(128, 128, 128)',\n  borderDarkest: 'rgb(128, 128, 128)',\n  borderLight: 'rgb(128, 128, 128)',\n  borderLightest: 'rgb(128, 128, 128)',\n  canvas: 'rgb(1, 36, 86)',\n  canvasText: 'rgb(238, 237, 240)',\n  canvasTextDisabled: 'rgb(128, 128, 128)',\n  canvasTextDisabledShadow: 'rgb(128, 128, 128)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(238, 237, 240)',\n  checkmarkDisabled: 'rgb(180, 180, 180)',\n  desktopBackground: 'rgb(1, 36, 86)',\n  flatDark: 'rgb(128, 128, 128)',\n  flatLight: 'rgb(128, 128, 128)',\n  focusSecondary: 'rgb(128, 128, 128)',\n  headerBackground: 'linear-gradient(to right, rgb(1, 36, 86), rgb(1, 36, 86))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(1, 36, 86), rgb(1, 36, 86))',\n  headerNotActiveText: 'rgb(192, 192, 192)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(0, 128, 128)',\n  material: 'rgb(1, 36, 86)',\n  materialDark: 'rgb(1, 36, 86)',\n  materialText: 'rgb(255, 255, 255)',\n  materialTextDisabled: 'rgb(128, 128, 128)',\n  materialTextDisabledShadow: 'rgb(128, 128, 128)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(0, 128, 128)',\n  tooltip: 'rgb(255, 255, 225)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/rainyDay.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'rainyDay',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#3d5367',\n  borderDarkest: '#16233b',\n  borderLight: '#91abc2',\n  borderLightest: '#b7cee5',\n  canvas: '#ffffff',\n  canvasText: '#050608',\n  canvasTextDisabled: '#3d5367',\n  canvasTextDisabledShadow: '#b7cee5',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#050608',\n  checkmarkDisabled: '#3d5367',\n  desktopBackground: '#000000',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#4b6480',\n  headerNotActiveBackground: '#7f7f81',\n  headerNotActiveText: '#ced0d9',\n  headerText: '#ffffff',\n  hoverBackground: '#4b6480',\n  material: '#7a99b3',\n  materialDark: '#9a9e9c',\n  materialText: '#050608',\n  materialTextDisabled: '#3d5367',\n  materialTextDisabledShadow: '#b7cee5',\n  materialTextInvert: '#ffffff',\n  progress: '#4b6480',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/raspberry.ts",
    "content": "/* \"Raspberry\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Raspberry-539289720\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'raspberry',\n  anchor: 'rgb(10, 36, 106)',\n  anchorVisited: 'rgb(10, 36, 106)',\n  borderDark: 'rgb(92, 101, 107)',\n  borderDarkest: 'rgb(64, 64, 64)',\n  borderLight: 'rgb(142, 151, 157)',\n  borderLightest: 'rgb(200, 204, 206)',\n  canvas: 'rgb(255, 255, 255)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(92, 101, 107)',\n  canvasTextDisabledShadow: 'rgb(200, 204, 206)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(92, 101, 107)',\n  desktopBackground: 'rgb(0, 64, 128)',\n  flatDark: 'rgb(92, 101, 107)',\n  flatLight: 'rgb(142, 151, 157)',\n  focusSecondary: 'rgb(200, 204, 206)',\n  headerBackground:\n    'linear-gradient(to right, rgb(10, 36, 106), rgb(153, 0, 51))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(92, 101, 107), rgb(92, 101, 107))',\n  headerNotActiveText: 'rgb(212, 208, 200)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(10, 36, 106)',\n  material: 'rgb(142, 151, 157)',\n  materialDark: 'rgb(92, 101, 107)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(92, 101, 107)',\n  materialTextDisabledShadow: 'rgb(200, 204, 206)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(10, 36, 106)',\n  tooltip: 'rgb(255, 255, 225)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/redWine.ts",
    "content": "/* \"Red Wine\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Red-Wine-545729607\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'redWine',\n  anchor: 'rgb(255, 0, 0)',\n  anchorVisited: 'rgb(255, 0, 0)',\n  borderDark: 'rgb(67, 9, 5)',\n  borderDarkest: 'rgb(64, 32, 32)',\n  borderLight: 'rgb(102, 12, 8)',\n  borderLightest: 'rgb(192, 64, 56)',\n  canvas: 'rgb(64, 0, 0)',\n  canvasText: 'rgb(255, 255, 255)',\n  canvasTextDisabled: 'rgb(67, 9, 5)',\n  canvasTextDisabledShadow: 'rgb(192, 64, 56)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(255, 255, 255)',\n  checkmarkDisabled: 'rgb(192, 64, 64)',\n  desktopBackground: 'rgb(147, 9, 0)',\n  flatDark: 'rgb(67, 9, 5)',\n  flatLight: 'rgb(102, 12, 8)',\n  focusSecondary: 'rgb(192, 64, 56)',\n  headerBackground:\n    'linear-gradient(to right, rgb(64, 0, 12), rgb(217, 11, 0))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(0, 0, 0), rgb(134, 4, 2))',\n  headerNotActiveText: 'rgb(239, 44, 33)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(192, 0, 0)',\n  material: 'rgb(102, 12, 8)',\n  materialDark: 'rgb(0, 0, 0)',\n  materialText: 'rgb(255, 255, 255)',\n  materialTextDisabled: 'rgb(67, 9, 5)',\n  materialTextDisabledShadow: 'rgb(192, 64, 56)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(192, 0, 0)',\n  tooltip: 'rgb(64, 0, 0)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/rose.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'rose',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#8a5b68',\n  borderDarkest: '#26030b',\n  borderLight: '#e5bec8',\n  borderLightest: '#f1d4dc',\n  canvas: '#ffffff',\n  canvasText: '#050608',\n  canvasTextDisabled: '#8a5b68',\n  canvasTextDisabledShadow: '#f1d4dc',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#050608',\n  checkmarkDisabled: '#8a5b68',\n  desktopBackground: '#808080',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#ab5a71',\n  headerNotActiveBackground: '#a19fa5',\n  headerNotActiveText: '#615f68',\n  headerText: '#ffffff',\n  hoverBackground: '#ab5a71',\n  material: '#d6adb8',\n  materialDark: '#9a9e9c',\n  materialText: '#050608',\n  materialTextDisabled: '#8a5b68',\n  materialTextDisabledShadow: '#f1d4dc',\n  materialTextInvert: '#ffffff',\n  progress: '#ab5a71',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/seawater.ts",
    "content": "/* \"Seawater\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Seawater-736002425\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'seawater',\n  anchor: 'rgb(0, 0, 128)',\n  anchorVisited: 'rgb(0, 0, 128)',\n  borderDark: 'rgb(167, 194, 224)',\n  borderDarkest: 'rgb(167, 194, 224)',\n  borderLight: 'rgb(167, 194, 224)',\n  borderLightest: 'rgb(167, 194, 224)',\n  canvas: 'rgb(255, 255, 255)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(167, 194, 224)',\n  canvasTextDisabledShadow: 'rgb(167, 194, 224)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(47, 88, 134)',\n  desktopBackground: 'rgb(0, 128, 128)',\n  flatDark: 'rgb(167, 194, 224)',\n  flatLight: 'rgb(167, 194, 224)',\n  focusSecondary: 'rgb(167, 194, 224)',\n  headerBackground: 'linear-gradient(to right, rgb(0, 0, 128), rgb(0, 0, 128))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(0, 85, 170), rgb(0, 85, 170))',\n  headerNotActiveText: 'rgb(192, 192, 192)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(0, 128, 128)',\n  material: 'rgb(79, 133, 193)',\n  materialDark: 'rgb(0, 85, 170)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(167, 194, 224)',\n  materialTextDisabledShadow: 'rgb(167, 194, 224)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(0, 128, 128)',\n  tooltip: 'rgb(255, 255, 225)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/shelbiTeal.ts",
    "content": "/* \"Teal for Shelbi\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Teal-for-Shelbi-506118460\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'shelbiTeal',\n  anchor: 'rgb(0, 128, 128)',\n  anchorVisited: 'rgb(0, 128, 128)',\n  borderDark: 'rgb(96, 128, 128)',\n  borderDarkest: 'rgb(0, 0, 0)',\n  borderLight: 'rgb(192, 204, 204)',\n  borderLightest: 'rgb(204, 224, 224)',\n  canvas: 'rgb(255, 255, 255)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(96, 128, 128)',\n  canvasTextDisabledShadow: 'rgb(204, 224, 224)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(96, 128, 128)',\n  desktopBackground: 'rgb(0, 128, 128)',\n  flatDark: 'rgb(96, 128, 128)',\n  flatLight: 'rgb(192, 204, 204)',\n  focusSecondary: 'rgb(204, 224, 224)',\n  headerBackground:\n    'linear-gradient(to right, rgb(0, 128, 128), rgb(0, 204, 204))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(96, 128, 128), rgb(160, 192, 192))',\n  headerNotActiveText: 'rgb(192, 204, 204)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(0, 128, 128)',\n  material: 'rgb(168, 192, 192)',\n  materialDark: 'rgb(96, 128, 128)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(96, 128, 128)',\n  materialTextDisabledShadow: 'rgb(204, 224, 224)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(0, 128, 128)',\n  tooltip: 'rgb(224, 255, 255)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/slate.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'slate',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#446b7c',\n  borderDarkest: '#000814',\n  borderLight: '#adc8da',\n  borderLightest: '#c3d9e9',\n  canvas: '#f2ffff',\n  canvasText: '#00010f',\n  canvasTextDisabled: '#446b7c',\n  canvasTextDisabledShadow: '#c3d9e9',\n  canvasTextInvert: '#f2ffff',\n  checkmark: '#00010f',\n  checkmarkDisabled: '#446b7c',\n  desktopBackground: '#414141',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#448199',\n  headerNotActiveBackground: '#807f80',\n  headerNotActiveText: '#c2c1c2',\n  headerText: '#f2ffff',\n  hoverBackground: '#448199',\n  material: '#97b9cb',\n  materialDark: '#9a9e9c',\n  materialText: '#00010f',\n  materialTextDisabled: '#446b7c',\n  materialTextDisabledShadow: '#c3d9e9',\n  materialTextInvert: '#f2ffff',\n  progress: '#448199',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/solarizedDark.ts",
    "content": "/* \"Solarized Dark\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Solarized-Dark-592122068\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'solarizedDark',\n  anchor: 'rgb(38, 139, 210)',\n  anchorVisited: 'rgb(38, 139, 210)',\n  borderDark: 'rgb(0, 43, 54)',\n  borderDarkest: 'rgb(0, 43, 54)',\n  borderLight: 'rgb(0, 43, 54)',\n  borderLightest: 'rgb(0, 43, 54)',\n  canvas: 'rgb(0, 43, 54)',\n  canvasText: 'rgb(131, 148, 150)',\n  canvasTextDisabled: 'rgb(0, 43, 54)',\n  canvasTextDisabledShadow: 'rgb(0, 43, 54)',\n  canvasTextInvert: 'rgb(238, 232, 213)',\n  checkmark: 'rgb(131, 148, 150)',\n  checkmarkDisabled: 'rgb(88, 110, 117)',\n  desktopBackground: 'rgb(0, 43, 54)',\n  flatDark: 'rgb(0, 43, 54)',\n  flatLight: 'rgb(0, 43, 54)',\n  focusSecondary: 'rgb(0, 43, 54)',\n  headerBackground: 'linear-gradient(to right, rgb(0, 43, 54), rgb(0, 43, 54))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(7, 54, 66), rgb(7, 54, 66))',\n  headerNotActiveText: 'rgb(131, 148, 150)',\n  headerText: 'rgb(108, 113, 196)',\n  hoverBackground: 'rgb(211, 54, 130)',\n  material: 'rgb(7, 54, 66)',\n  materialDark: 'rgb(7, 54, 66)',\n  materialText: 'rgb(131, 148, 150)',\n  materialTextDisabled: 'rgb(0, 43, 54)',\n  materialTextDisabledShadow: 'rgb(0, 43, 54)',\n  materialTextInvert: 'rgb(238, 232, 213)',\n  progress: 'rgb(211, 54, 130)',\n  tooltip: 'rgb(253, 246, 227)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/solarizedLight.ts",
    "content": "/* \"Solarized Light\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Solarized-Light-592124935\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'solarizedLight',\n  anchor: 'rgb(38, 139, 210)',\n  anchorVisited: 'rgb(38, 139, 210)',\n  borderDark: 'rgb(253, 246, 227)',\n  borderDarkest: 'rgb(253, 246, 227)',\n  borderLight: 'rgb(253, 246, 227)',\n  borderLightest: 'rgb(253, 246, 227)',\n  canvas: 'rgb(253, 246, 227)',\n  canvasText: 'rgb(101, 123, 131)',\n  canvasTextDisabled: 'rgb(253, 246, 227)',\n  canvasTextDisabledShadow: 'rgb(253, 246, 227)',\n  canvasTextInvert: 'rgb(238, 232, 213)',\n  checkmark: 'rgb(101, 123, 131)',\n  checkmarkDisabled: 'rgb(88, 110, 117)',\n  desktopBackground: 'rgb(253, 246, 227)',\n  flatDark: 'rgb(253, 246, 227)',\n  flatLight: 'rgb(253, 246, 227)',\n  focusSecondary: 'rgb(253, 246, 227)',\n  headerBackground:\n    'linear-gradient(to right, rgb(253, 246, 227), rgb(253, 246, 227))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(238, 232, 213), rgb(238, 232, 213))',\n  headerNotActiveText: 'rgb(101, 123, 131)',\n  headerText: 'rgb(108, 113, 196)',\n  hoverBackground: 'rgb(211, 54, 130)',\n  material: 'rgb(238, 232, 213)',\n  materialDark: 'rgb(238, 232, 213)',\n  materialText: 'rgb(101, 123, 131)',\n  materialTextDisabled: 'rgb(253, 246, 227)',\n  materialTextDisabledShadow: 'rgb(253, 246, 227)',\n  materialTextInvert: 'rgb(238, 232, 213)',\n  progress: 'rgb(211, 54, 130)',\n  tooltip: 'rgb(253, 246, 227)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/spruce.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'spruce',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#477b5e',\n  borderDarkest: '#001004',\n  borderLight: '#b0d2bb',\n  borderLightest: '#cdead2',\n  canvas: '#fcfff6',\n  canvasText: '#050608',\n  canvasTextDisabled: '#3d5367',\n  canvasTextDisabledShadow: '#cdead2',\n  canvasTextInvert: '#fcfff6',\n  checkmark: '#050608',\n  checkmarkDisabled: '#477b5e',\n  desktopBackground: '#213f21',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#3d9961',\n  headerNotActiveBackground: '#807f80',\n  headerNotActiveText: '#d4deda',\n  headerText: '#fcfff6',\n  hoverBackground: '#3d9961',\n  material: '#99c9a8',\n  materialDark: '#9a9e9c',\n  materialText: '#050608',\n  materialTextDisabled: '#3d5367',\n  materialTextDisabledShadow: '#cdead2',\n  materialTextInvert: '#fcfff6',\n  progress: '#3d9961',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/stormClouds.ts",
    "content": "/* \"Storm Clouds\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Storm-Clouds-613198674\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'stormClouds',\n  anchor: 'rgb(64, 64, 255)',\n  anchorVisited: 'rgb(64, 64, 255)',\n  borderDark: 'rgb(47, 52, 53)',\n  borderDarkest: 'rgb(0, 0, 0)',\n  borderLight: 'rgb(70, 80, 81)',\n  borderLightest: 'rgb(159, 171, 172)',\n  canvas: 'rgb(47, 52, 53)',\n  canvasText: 'rgb(255, 255, 255)',\n  canvasTextDisabled: 'rgb(47, 52, 53)',\n  canvasTextDisabledShadow: 'rgb(159, 171, 172)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(255, 255, 255)',\n  checkmarkDisabled: 'rgb(128, 128, 128)',\n  desktopBackground: 'rgb(45, 87, 87)',\n  flatDark: 'rgb(47, 52, 53)',\n  flatLight: 'rgb(70, 80, 81)',\n  focusSecondary: 'rgb(159, 171, 172)',\n  headerBackground:\n    'linear-gradient(to right, rgb(0, 0, 168), rgb(16, 132, 208))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(47, 52, 53), rgb(128, 128, 128))',\n  headerNotActiveText: 'rgb(192, 199, 200)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(128, 128, 0)',\n  material: 'rgb(70, 80, 81)',\n  materialDark: 'rgb(47, 52, 53)',\n  materialText: 'rgb(255, 255, 255)',\n  materialTextDisabled: 'rgb(47, 52, 53)',\n  materialTextDisabledShadow: 'rgb(159, 171, 172)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(128, 128, 0)',\n  tooltip: 'rgb(48, 64, 80)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/theSixtiesUSA.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'theSixtiesUSA',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#6c1f71',\n  borderDarkest: '#010001',\n  borderLight: '#d982de',\n  borderLightest: '#df9be7',\n  canvas: '#ffffff',\n  canvasText: '#010001',\n  canvasTextDisabled: '#6c1f71',\n  canvasTextDisabledShadow: '#df9be7',\n  canvasTextInvert: '#010001',\n  checkmark: '#010001',\n  checkmarkDisabled: '#6c1f71',\n  desktopBackground: '#92458a', // original: #000000\n  flatDark: '#d067d7',\n  flatLight: '#df9be7',\n  focusSecondary: '#fefe03',\n  headerBackground: '#050080',\n  headerNotActiveBackground: '#a130a9',\n  headerNotActiveText: '#df9be7',\n  headerText: '#ffffff',\n  hoverBackground: '#0f0',\n  material: '#d067d7',\n  materialDark: '#9a9e9c',\n  materialText: '#010001',\n  materialTextDisabled: '#6c1f71',\n  materialTextDisabledShadow: '#df9be7',\n  materialTextInvert: '#010001',\n  progress: '#0f0',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/tokyoDark.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'tokyoDark',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#1f2223',\n  borderDarkest: '#070809',\n  borderLight: '#5e696a',\n  borderLightest: '#93a0a1',\n  canvas: '#2f3435',\n  canvasText: '#F4F4ED',\n  canvasTextDisabled: '#1f2223',\n  canvasTextDisabledShadow: '#93a0a1',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#F4F4ED',\n  checkmarkDisabled: '#1f2223',\n  desktopBackground: '#181a1b',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#20FC8F',\n  headerBackground: '#1f2223',\n  headerNotActiveBackground: '#5e696a',\n  headerNotActiveText: '#F4F4ED',\n  headerText: '#F4F4ED',\n  hoverBackground: '#F61067',\n  material: '#465051',\n  materialDark: '#1f2223',\n  materialText: '#F4F4ED',\n  materialTextDisabled: '#1f2223',\n  materialTextDisabledShadow: '#93a0a1',\n  materialTextInvert: '#ffffff',\n  progress: '#F61067',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/toner.ts",
    "content": "/* \"Toner\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Toner-871968986\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'toner',\n  anchor: 'rgb(0, 0, 128)',\n  anchorVisited: 'rgb(0, 0, 128)',\n  borderDark: 'rgb(0, 0, 0)',\n  borderDarkest: 'rgb(128, 128, 128)',\n  borderLight: 'rgb(0, 0, 0)',\n  borderLightest: 'rgb(127, 127, 127)',\n  canvas: 'rgb(128, 128, 128)',\n  canvasText: 'rgb(255, 255, 255)',\n  canvasTextDisabled: 'rgb(0, 0, 0)',\n  canvasTextDisabledShadow: 'rgb(127, 127, 127)',\n  canvasTextInvert: 'rgb(0, 0, 0)',\n  checkmark: 'rgb(255, 255, 255)',\n  checkmarkDisabled: 'rgb(64, 64, 64)',\n  desktopBackground: 'rgb(128, 128, 128)',\n  flatDark: 'rgb(0, 0, 0)',\n  flatLight: 'rgb(0, 0, 0)',\n  focusSecondary: 'rgb(127, 127, 127)',\n  headerBackground:\n    'linear-gradient(to right, rgb(128, 128, 128), rgb(128, 128, 128))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(0, 0, 0), rgb(128, 128, 128))',\n  headerNotActiveText: 'rgb(128, 128, 128)',\n  headerText: 'rgb(0, 0, 0)',\n  hoverBackground: 'rgb(255, 255, 255)',\n  material: 'rgb(0, 0, 0)',\n  materialDark: 'rgb(0, 0, 0)',\n  materialText: 'rgb(255, 255, 255)',\n  materialTextDisabled: 'rgb(0, 0, 0)',\n  materialTextDisabledShadow: 'rgb(127, 127, 127)',\n  materialTextInvert: 'rgb(0, 0, 0)',\n  progress: 'rgb(255, 255, 255)',\n  tooltip: 'rgb(128, 128, 128)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/tooSexy.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'tooSexy',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#5a0302',\n  borderDarkest: '#000000',\n  borderLight: '#c81d19',\n  borderLightest: '#fe5757',\n  canvas: '#FFF1D0',\n  canvasText: '#000000',\n  canvasTextDisabled: '#5a0302',\n  canvasTextDisabledShadow: '#FFF1D0',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#000000',\n  checkmarkDisabled: '#5a0302',\n  desktopBackground: '#000000',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#161B33',\n  headerNotActiveBackground: '#5a0302',\n  headerNotActiveText: '#B80100',\n  headerText: '#FFF1D0',\n  hoverBackground: '#474973',\n  material: '#B80100',\n  materialDark: '#9a9e9c',\n  materialText: '#000000',\n  materialTextDisabled: '#5a0302',\n  materialTextDisabledShadow: '#fe5757',\n  materialTextInvert: '#ffffff',\n  progress: '#474973',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/travel.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'travel',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#695f50',\n  borderDarkest: '#28251e',\n  borderLight: '#9d8f80',\n  borderLightest: '#baae9f',\n  canvas: '#d8d0c8',\n  canvasText: '#28251e',\n  canvasTextDisabled: '#695f50',\n  canvasTextDisabledShadow: '#baae9f',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#28251e',\n  checkmarkDisabled: '#695f50',\n  desktopBackground: '#7c654c', // original: #000000\n  flatDark: '#695f50',\n  flatLight: '#9d8f80',\n  focusSecondary: '#fefe03',\n  headerBackground: '#404878',\n  headerNotActiveBackground: '#605848',\n  headerNotActiveText: '#908070',\n  headerText: '#d8d0c8',\n  hoverBackground: '#48604f',\n  material: '#908070',\n  materialDark: '#9a9e9c',\n  materialText: '#28251e',\n  materialTextDisabled: '#695f50',\n  materialTextDisabledShadow: '#baae9f',\n  materialTextInvert: '#ffffff',\n  progress: '#48604f',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/types.ts",
    "content": "export type Color = string;\n\nexport type Theme = {\n  name: string;\n  anchor: Color;\n  anchorVisited: Color;\n  borderDark: Color;\n  borderDarkest: Color;\n  borderLight: Color;\n  borderLightest: Color;\n  canvas: Color;\n  canvasText: Color;\n  canvasTextDisabled: Color;\n  canvasTextDisabledShadow: Color;\n  canvasTextInvert: Color;\n  checkmark: Color;\n  checkmarkDisabled: Color;\n  desktopBackground: Color;\n  flatDark: Color;\n  flatLight: Color;\n  focusSecondary: Color;\n  headerBackground: Color;\n  headerNotActiveBackground: Color;\n  headerNotActiveText: Color;\n  headerText: Color;\n  hoverBackground: Color;\n  material: Color;\n  materialDark: Color;\n  materialText: Color;\n  materialTextDisabled: Color;\n  materialTextDisabledShadow: Color;\n  materialTextInvert: Color;\n  progress: Color;\n  tooltip: Color;\n};\n\nexport type WindowsTheme = {\n  ActiveBorder: Color;\n  ActiveTitle: Color;\n  AppWorkspace: Color;\n  Background: Color;\n  ButtonAlternateFace: Color;\n  ButtonDkShadow: Color;\n  ButtonFace: Color;\n  ButtonHilight: Color;\n  ButtonLight: Color;\n  ButtonShadow: Color;\n  ButtonText: Color;\n  GradientActiveTitle: Color;\n  GradientInactiveTitle: Color;\n  GrayText: Color;\n  Hilight: Color;\n  HilightText: Color;\n  HotTrackingColor: Color;\n  InactiveBorder: Color;\n  InactiveTitle: Color;\n  InactiveTitleText: Color;\n  InfoText: Color;\n  InfoWindow: Color;\n  Menu: Color;\n  MenuBar: Color;\n  MenuHilight: Color;\n  MenuText: Color;\n  Scrollbar: Color;\n  TitleText: Color;\n  Window: Color;\n  WindowFrame: Color;\n  WindowText: Color;\n};\n"
  },
  {
    "path": "src/common/themes/vaporTeal.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'vaporTeal',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#00706f',\n  borderDarkest: '#000000',\n  borderLight: '#2fcecd',\n  borderLightest: '#58ffff',\n  canvas: '#98DFEA',\n  canvasText: '#000000',\n  canvasTextDisabled: '#00706f',\n  canvasTextDisabledShadow: '#58ffff',\n  canvasTextInvert: '#000000',\n  checkmark: '#000000',\n  checkmarkDisabled: '#00706f',\n  desktopBackground: '#008080',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#FCF6BD',\n  headerBackground: '#246A73',\n  headerNotActiveBackground: '#2fcecd',\n  headerNotActiveText: '#246A73',\n  headerText: '#58ffff',\n  hoverBackground: '#FF99C8',\n  material: '#01a8a8',\n  materialDark: '#246A73',\n  materialText: '#000000',\n  materialTextDisabled: '#00706f',\n  materialTextDisabledShadow: '#58ffff',\n  materialTextInvert: '#000000',\n  progress: '#FF99C8',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/vermillion.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'vermillion',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#7f2120',\n  borderDarkest: '#130405',\n  borderLight: '#d25051',\n  borderLightest: '#e59697',\n  canvas: '#EFE9F4',\n  canvasText: '#130405',\n  canvasTextDisabled: '#7f2120',\n  canvasTextDisabledShadow: '#e59697',\n  canvasTextInvert: '#EFE9F4',\n  checkmark: '#130405',\n  checkmarkDisabled: '#7f2120',\n  desktopBackground: '#b22930',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#000103',\n  headerNotActiveBackground: '#7f2120',\n  headerNotActiveText: '#EFE9F4',\n  headerText: '#EFE9F4',\n  hoverBackground: '#000103',\n  material: '#cf4545',\n  materialDark: '#7f2120',\n  materialText: '#130405',\n  materialTextDisabled: '#7f2120',\n  materialTextDisabledShadow: '#e59697',\n  materialTextInvert: '#EFE9F4',\n  progress: '#000103',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/violetDark.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'violetDark',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  borderDark: '#3c1f3e',\n  borderDarkest: '#18051a',\n  borderLight: '#945b9b',\n  borderLightest: '#c47bcc',\n  canvas: '#c47bcc',\n  canvasText: '#18051a',\n  canvasTextDisabled: '#000000',\n  canvasTextDisabledShadow: '#000000',\n  canvasTextInvert: '#c57ece',\n  checkmark: '#000000',\n  checkmarkDisabled: '#3c1f3e',\n  desktopBackground: '#3b1940',\n  flatDark: '#3c1f3e',\n  flatLight: '#945b9b',\n  focusSecondary: '#fefe03',\n  headerBackground: '#1034a6',\n  headerNotActiveBackground: '#210e23',\n  headerNotActiveText: '#652a6d',\n  headerText: '#010601',\n  hoverBackground: '#512155',\n  material: '#652a6d',\n  materialDark: '#210e23',\n  materialText: '#c57ece',\n  materialTextDisabled: '#3c1f3e',\n  materialTextDisabledShadow: '#c47bcc',\n  materialTextInvert: '#c47bcc',\n  progress: '#000080',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/vistaesqueMidnight.ts",
    "content": "/* \"Vista-esque Midnight\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Vista-esque-Midnight-557498234\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'vistaesqueMidnight',\n  anchor: 'rgb(64, 64, 192)',\n  anchorVisited: 'rgb(64, 64, 192)',\n  borderDark: 'rgb(21, 21, 21)',\n  borderDarkest: 'rgb(0, 0, 0)',\n  borderLight: 'rgb(32, 32, 32)',\n  borderLightest: 'rgb(128, 128, 128)',\n  canvas: 'rgb(0, 0, 0)',\n  canvasText: 'rgb(255, 255, 255)',\n  canvasTextDisabled: 'rgb(21, 21, 21)',\n  canvasTextDisabledShadow: 'rgb(128, 128, 128)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(255, 255, 255)',\n  checkmarkDisabled: 'rgb(32, 32, 32)',\n  desktopBackground: 'rgb(31, 60, 89)',\n  flatDark: 'rgb(21, 21, 21)',\n  flatLight: 'rgb(32, 32, 32)',\n  focusSecondary: 'rgb(128, 128, 128)',\n  headerBackground:\n    'linear-gradient(to right, rgb(81, 132, 188), rgb(100, 168, 60))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(22, 46, 101), rgb(18, 91, 30))',\n  headerNotActiveText: 'rgb(192, 192, 192)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(49, 106, 197)',\n  material: 'rgb(32, 32, 32)',\n  materialDark: 'rgb(22, 46, 101)',\n  materialText: 'rgb(255, 255, 255)',\n  materialTextDisabled: 'rgb(21, 21, 21)',\n  materialTextDisabledShadow: 'rgb(128, 128, 128)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(49, 106, 197)',\n  tooltip: 'rgb(0, 0, 30)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/water.ts",
    "content": "import { Theme } from './types';\n\nexport default {\n  name: 'water',\n\n  anchor: '#72b3b4',\n  anchorVisited: '#440381',\n  borderDark: '#888c8f',\n  borderDarkest: '#050608',\n  borderLight: '#dfe0e3',\n  borderLightest: '#ffffff',\n  canvas: '#ffffff',\n  canvasText: '#050608',\n  canvasTextDisabled: '#888c8f',\n  canvasTextDisabledShadow: '#ffffff',\n  canvasTextInvert: '#ffffff',\n  checkmark: '#050608',\n  checkmarkDisabled: '#888c8f',\n  desktopBackground: '#3a6ea5',\n  flatDark: '#9e9e9e',\n  flatLight: '#d8d8d8',\n  focusSecondary: '#fefe03',\n  headerBackground: '#72b3b4',\n  headerNotActiveBackground: '#9a9e9c',\n  headerNotActiveText: '#ced0cf',\n  headerText: '#ffffff',\n  hoverBackground: '#72b3b4',\n  material: '#ced0cf',\n  materialDark: '#9a9e9c',\n  materialText: '#050608',\n  materialTextDisabled: '#888c8f',\n  materialTextDisabledShadow: '#ffffff',\n  materialTextInvert: '#ffffff',\n  progress: '#72b3b4',\n  tooltip: '#fefbcc'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/white.ts",
    "content": "/* \"White\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/White-870495714\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'white',\n  anchor: 'rgb(0, 0, 128)',\n  anchorVisited: 'rgb(0, 0, 128)',\n  borderDark: 'rgb(0, 0, 0)',\n  borderDarkest: 'rgb(255, 255, 255)',\n  borderLight: 'rgb(0, 0, 0)',\n  borderLightest: 'rgb(255, 255, 255)',\n  canvas: 'rgb(255, 255, 255)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(0, 0, 0)',\n  canvasTextDisabledShadow: 'rgb(255, 255, 255)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(128, 128, 128)',\n  desktopBackground: 'rgb(0, 128, 128)',\n  flatDark: 'rgb(0, 0, 0)',\n  flatLight: 'rgb(0, 0, 0)',\n  focusSecondary: 'rgb(255, 255, 255)',\n  headerBackground: 'linear-gradient(to right, rgb(0, 0, 128), rgb(0, 0, 128))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(128, 128, 128), rgb(128, 128, 128))',\n  headerNotActiveText: 'rgb(192, 192, 192)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(0, 0, 128)',\n  material: 'rgb(255, 255, 255)',\n  materialDark: 'rgb(128, 128, 128)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(0, 0, 0)',\n  materialTextDisabledShadow: 'rgb(255, 255, 255)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(0, 0, 128)',\n  tooltip: 'rgb(255, 255, 128)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/windows1.ts",
    "content": "/* \"Windows 1\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Windows-1-573195578\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'windows1',\n  anchor: 'rgb(0, 0, 85)',\n  anchorVisited: 'rgb(0, 0, 85)',\n  borderDark: 'rgb(0, 0, 0)',\n  borderDarkest: 'rgb(255, 255, 255)',\n  borderLight: 'rgb(0, 0, 0)',\n  borderLightest: 'rgb(255, 255, 255)',\n  canvas: 'rgb(255, 255, 255)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(0, 0, 0)',\n  canvasTextDisabledShadow: 'rgb(255, 255, 255)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(85, 85, 85)',\n  desktopBackground: 'rgb(85, 254, 120)',\n  flatDark: 'rgb(0, 0, 0)',\n  flatLight: 'rgb(0, 0, 0)',\n  focusSecondary: 'rgb(255, 255, 255)',\n  headerBackground: 'linear-gradient(to right, rgb(0, 0, 0), rgb(85, 85, 255))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(0, 0, 0), rgb(109, 109, 199))',\n  headerNotActiveText: 'rgb(255, 255, 255)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(0, 0, 0)',\n  material: 'rgb(255, 255, 255)',\n  materialDark: 'rgb(0, 0, 0)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(0, 0, 0)',\n  materialTextDisabledShadow: 'rgb(255, 255, 255)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(0, 0, 0)',\n  tooltip: 'rgb(255, 255, 85)'\n} as Theme;\n"
  },
  {
    "path": "src/common/themes/wmii.ts",
    "content": "/* \"WMII\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/wmii-683233676\n */\n\nimport { Theme } from './types';\n\nexport default {\n  name: 'wmii',\n  anchor: 'rgb(129, 101, 79)',\n  anchorVisited: 'rgb(129, 101, 79)',\n  borderDark: 'rgb(145, 148, 75)',\n  borderDarkest: 'rgb(64, 64, 64)',\n  borderLight: 'rgb(193, 196, 139)',\n  borderLightest: 'rgb(224, 225, 198)',\n  canvas: 'rgb(255, 255, 255)',\n  canvasText: 'rgb(0, 0, 0)',\n  canvasTextDisabled: 'rgb(145, 148, 75)',\n  canvasTextDisabledShadow: 'rgb(224, 225, 198)',\n  canvasTextInvert: 'rgb(255, 255, 255)',\n  checkmark: 'rgb(0, 0, 0)',\n  checkmarkDisabled: 'rgb(145, 148, 75)',\n  desktopBackground: 'rgb(51, 51, 51)',\n  flatDark: 'rgb(145, 148, 75)',\n  flatLight: 'rgb(193, 196, 139)',\n  focusSecondary: 'rgb(224, 225, 198)',\n  headerBackground:\n    'linear-gradient(to right, rgb(129, 101, 79), rgb(129, 101, 79))',\n  headerNotActiveBackground:\n    'linear-gradient(to right, rgb(145, 148, 75), rgb(145, 148, 75))',\n  headerNotActiveText: 'rgb(193, 196, 139)',\n  headerText: 'rgb(255, 255, 255)',\n  hoverBackground: 'rgb(0, 0, 0)',\n  material: 'rgb(193, 196, 139)',\n  materialDark: 'rgb(145, 148, 75)',\n  materialText: 'rgb(0, 0, 0)',\n  materialTextDisabled: 'rgb(145, 148, 75)',\n  materialTextDisabledShadow: 'rgb(224, 225, 198)',\n  materialTextInvert: 'rgb(255, 255, 255)',\n  progress: 'rgb(0, 0, 0)',\n  tooltip: 'rgb(255, 255, 225)'\n} as Theme;\n"
  },
  {
    "path": "src/common/utils/events.spec.tsx",
    "content": "import { EventType, fireEvent, render, screen } from '@testing-library/react';\nimport React, { forwardRef } from 'react';\nimport {\n  isReactFocusEvent,\n  isReactKeyboardEvent,\n  isReactMouseEvent\n} from './events';\n\ntype FireEventMap = Record<string, EventType>;\n\nconst focusEventTypesMap: FireEventMap = { blur: 'blur', focus: 'focus' };\n\nconst keyboardEventTypesMap: FireEventMap = {\n  keydown: 'keyDown',\n  keypress: 'keyPress',\n  keyup: 'keyUp'\n};\n\nconst mouseEventTypesMap: FireEventMap = {\n  click: 'click',\n  contextmenu: 'contextMenu',\n  doubleclick: 'doubleClick',\n  drag: 'drag',\n  dragend: 'dragEnd',\n  dragenter: 'dragEnter',\n  dragexit: 'dragExit',\n  dragleave: 'dragLeave',\n  dragover: 'dragOver',\n  dragstart: 'dragStart',\n  drop: 'drop',\n  mousedown: 'mouseDown',\n  mouseenter: 'mouseEnter',\n  mouseleave: 'mouseLeave',\n  mousemove: 'mouseMove',\n  mouseout: 'mouseOut',\n  mouseover: 'mouseOver',\n  mouseup: 'mouseUp'\n};\n\nconst OnEverything = forwardRef<\n  HTMLInputElement,\n  {\n    callback: (event: React.SyntheticEvent<HTMLInputElement>) => void;\n  }\n>(({ callback }, ref) => {\n  return (\n    <input\n      onBlur={callback}\n      onClick={callback}\n      onContextMenu={callback}\n      onDoubleClick={callback}\n      onDrag={callback}\n      onDragEnd={callback}\n      onDragEnter={callback}\n      onDragExit={callback}\n      onDragLeave={callback}\n      onDragOver={callback}\n      onDragStart={callback}\n      onDrop={callback}\n      onFocus={callback}\n      onKeyDown={callback}\n      onKeyPress={callback}\n      onKeyUp={callback}\n      onMouseDown={callback}\n      onMouseEnter={callback}\n      onMouseLeave={callback}\n      onMouseMove={callback}\n      onMouseOut={callback}\n      onMouseOver={callback}\n      onMouseUp={callback}\n      ref={ref}\n    />\n  );\n});\n\nconst renderEventTester = async () => {\n  const callback = jest.fn<void, [React.SyntheticEvent<HTMLInputElement>]>();\n  render(<OnEverything callback={callback} />);\n  const input = await screen.findByRole('textbox');\n\n  return {\n    callback,\n    emit: (fireEventFunctionName: keyof typeof fireEvent, init?: EventInit) => {\n      fireEvent[fireEventFunctionName](input, init);\n      const callbackCall =\n        callback.mock.calls[callback.mock.calls.length - 1]?.[0];\n      return callbackCall;\n    }\n  };\n};\n\ndescribe(isReactFocusEvent, () => {\n  let emit: (\n    fireEventFunctionName: keyof typeof fireEvent,\n    init?: EventInit\n  ) => React.SyntheticEvent<HTMLInputElement, Event>;\n  beforeAll(async () => {\n    ({ emit } = await renderEventTester());\n  });\n\n  it.each(Object.keys(focusEventTypesMap))(\n    'returns correct results for %s',\n    focusEventType => {\n      expect(isReactFocusEvent(new Event(focusEventType))).toBeFalsy();\n      const event = emit(focusEventTypesMap[focusEventType]);\n      expect(isReactFocusEvent(event)).toBeTruthy();\n    }\n  );\n});\n\ndescribe(isReactKeyboardEvent, () => {\n  let emit: (\n    fireEventFunctionName: keyof typeof fireEvent,\n    init?: EventInit\n  ) => React.SyntheticEvent<HTMLInputElement, Event>;\n  beforeAll(async () => {\n    ({ emit } = await renderEventTester());\n  });\n\n  it.each(Object.keys(keyboardEventTypesMap))(\n    'returns correct results for %s',\n    focusEventType => {\n      expect(isReactKeyboardEvent(new Event(focusEventType))).toBeFalsy();\n      const event = emit(keyboardEventTypesMap[focusEventType]);\n      expect(isReactKeyboardEvent(event)).toBeTruthy();\n    }\n  );\n});\n\ndescribe(isReactMouseEvent, () => {\n  let emit: (\n    fireEventFunctionName: keyof typeof fireEvent,\n    init?: EventInit\n  ) => React.SyntheticEvent<HTMLInputElement, Event>;\n  beforeAll(async () => {\n    ({ emit } = await renderEventTester());\n  });\n\n  it.each(Object.keys(mouseEventTypesMap))(\n    'returns correct results for %s',\n    focusEventType => {\n      expect(isReactMouseEvent(new Event(focusEventType))).toBeFalsy();\n      const event = emit(mouseEventTypesMap[focusEventType]);\n      expect(isReactMouseEvent(event)).toBeTruthy();\n    }\n  );\n});\n"
  },
  {
    "path": "src/common/utils/events.ts",
    "content": "import React from 'react';\n\nexport const focusEventTypes = ['blur', 'focus'];\n\nexport const keyboardEventTypes = ['keydown', 'keypress', 'keyup'];\n\nexport const mouseEventTypes = [\n  'click',\n  'contextmenu',\n  'doubleclick',\n  'drag',\n  'dragend',\n  'dragenter',\n  'dragexit',\n  'dragleave',\n  'dragover',\n  'dragstart',\n  'drop',\n  'mousedown',\n  'mouseenter',\n  'mouseleave',\n  'mousemove',\n  'mouseout',\n  'mouseover',\n  'mouseup'\n];\n\nexport function isReactFocusEvent<T>(\n  event: React.SyntheticEvent<T> | Event\n): event is React.FocusEvent<T> {\n  return 'nativeEvent' in event && focusEventTypes.includes(event.type);\n}\n\nexport function isReactKeyboardEvent<T>(\n  event: React.SyntheticEvent<T> | Event\n): event is React.KeyboardEvent<T> {\n  return 'nativeEvent' in event && keyboardEventTypes.includes(event.type);\n}\n\nexport function isReactMouseEvent<T>(\n  event: React.SyntheticEvent<T> | Event\n): event is React.MouseEvent<T> {\n  return 'nativeEvent' in event && mouseEventTypes.includes(event.type);\n}\n"
  },
  {
    "path": "src/common/utils/index.spec.ts",
    "content": "import {\n  clamp,\n  mapFromWindowsTheme,\n  getDecimalPrecision,\n  roundValueToStep\n} from './index';\n\ndescribe('clamp', () => {\n  it('should return passed value if its between min and max', () => {\n    expect(clamp(4, 3, 5)).toBe(4);\n  });\n\n  it('should return min if value is smaller than min', () => {\n    expect(clamp(2, 3, 5)).toBe(3);\n  });\n\n  it('should return max if value is bigger than max', () => {\n    expect(clamp(6, 3, 5)).toBe(5);\n  });\n\n  it('should accept null as min', () => {\n    expect(clamp(6, null, 5)).toBe(5);\n  });\n\n  it('should accept null as max', () => {\n    expect(clamp(1, 2, null)).toBe(2);\n  });\n});\n\ndescribe('mapFromWindowsTheme', () => {\n  it('should map corresponding properties directly if gradients are disabled', () => {\n    const theme = {\n      ButtonAlternateFace: '#000000',\n      ButtonDkShadow: '#000001',\n      ButtonFace: '#000002',\n      ButtonHilight: '#000003',\n      ButtonLight: '#000004',\n      ButtonShadow: '#000005',\n      ButtonText: '#000006',\n      ActiveBorder: '#000007',\n      AppWorkspace: '#000008',\n      Background: '#000009',\n      InactiveBorder: '#00000a',\n      Scrollbar: '#00000b',\n      Window: '#00000c',\n      WindowFrame: '#00000d',\n      WindowText: '#00000e',\n      ActiveTitle: '#00000f',\n      GradientActiveTitle: '#000010',\n      GradientInactiveTitle: '#000011',\n      InactiveTitle: '#000012',\n      InactiveTitleText: '#000013',\n      TitleText: '#000014',\n      Menu: '#000015',\n      MenuBar: '#000016',\n      MenuHilight: '#000017',\n      MenuText: '#000018',\n      GrayText: '#000019',\n      Hilight: '#00001a',\n      HilightText: '#00001b',\n      HotTrackingColor: '#00001c',\n      InfoText: '#00001d',\n      InfoWindow: '#00001e'\n    };\n    const expectedTheme = {\n      name: 'theme',\n      anchor: '#00001c',\n      anchorVisited: '#00001c',\n      borderDark: '#000005',\n      borderDarkest: '#000001',\n      borderLight: '#000004',\n      borderLightest: '#000003',\n      canvas: '#00000c',\n      canvasText: '#00000e',\n      canvasTextDisabled: '#000005',\n      canvasTextDisabledShadow: '#000003',\n      canvasTextInvert: '#00001b',\n      checkmark: '#00000e',\n      checkmarkDisabled: '#000019',\n      desktopBackground: '#000009',\n      flatDark: '#000005',\n      flatLight: '#000004',\n      focusSecondary: '#000003',\n      headerBackground: '#00000f',\n      headerNotActiveBackground: '#000012',\n      headerNotActiveText: '#000013',\n      headerText: '#000014',\n      hoverBackground: '#00001a',\n      material: '#000002',\n      materialDark: '#000012',\n      materialText: '#000006',\n      materialTextDisabled: '#000005',\n      materialTextDisabledShadow: '#000003',\n      materialTextInvert: '#00001b',\n      progress: '#00001a',\n      tooltip: '#00001e'\n    };\n\n    expect(mapFromWindowsTheme('theme', theme, false)).toEqual(expectedTheme);\n  });\n\n  it('should map corresponding properties with gradients if gradients are enabled', () => {\n    const theme = {\n      ButtonAlternateFace: '#000000',\n      ButtonDkShadow: '#000001',\n      ButtonFace: '#000002',\n      ButtonHilight: '#000003',\n      ButtonLight: '#000004',\n      ButtonShadow: '#000005',\n      ButtonText: '#000006',\n      ActiveBorder: '#000007',\n      AppWorkspace: '#000008',\n      Background: '#000009',\n      InactiveBorder: '#00000a',\n      Scrollbar: '#00000b',\n      Window: '#00000c',\n      WindowFrame: '#00000d',\n      WindowText: '#00000e',\n      ActiveTitle: '#00000f',\n      GradientActiveTitle: '#000010',\n      GradientInactiveTitle: '#000011',\n      InactiveTitle: '#000012',\n      InactiveTitleText: '#000013',\n      TitleText: '#000014',\n      Menu: '#000015',\n      MenuBar: '#000016',\n      MenuHilight: '#000017',\n      MenuText: '#000018',\n      GrayText: '#000019',\n      Hilight: '#00001a',\n      HilightText: '#00001b',\n      HotTrackingColor: '#00001c',\n      InfoText: '#00001d',\n      InfoWindow: '#00001e'\n    };\n    const expectedTheme = {\n      name: 'theme',\n      anchor: '#00001c',\n      anchorVisited: '#00001c',\n      borderDark: '#000005',\n      borderDarkest: '#000001',\n      borderLight: '#000004',\n      borderLightest: '#000003',\n      canvas: '#00000c',\n      canvasText: '#00000e',\n      canvasTextDisabled: '#000005',\n      canvasTextDisabledShadow: '#000003',\n      canvasTextInvert: '#00001b',\n      checkmark: '#00000e',\n      checkmarkDisabled: '#000019',\n      desktopBackground: '#000009',\n      flatDark: '#000005',\n      flatLight: '#000004',\n      focusSecondary: '#000003',\n      headerBackground: 'linear-gradient(to right, #00000f, #000010)',\n      headerNotActiveBackground: 'linear-gradient(to right, #000012, #000011)',\n      headerNotActiveText: '#000013',\n      headerText: '#000014',\n      hoverBackground: '#00001a',\n      material: '#000002',\n      materialDark: '#000012',\n      materialText: '#000006',\n      materialTextDisabled: '#000005',\n      materialTextDisabledShadow: '#000003',\n      materialTextInvert: '#00001b',\n      progress: '#00001a',\n      tooltip: '#00001e'\n    };\n\n    expect(mapFromWindowsTheme('theme', theme, true)).toEqual(expectedTheme);\n  });\n});\n\ndescribe('getDecimalPrecision', () => {\n  it('should return 0 when passed a round number', () => {\n    expect(getDecimalPrecision(4)).toBe(0);\n  });\n\n  it('should return correct decimal precision', () => {\n    expect(getDecimalPrecision(1.233)).toBe(3);\n  });\n});\n\ndescribe('roundValueToStep', () => {\n  it('should be able to round down', () => {\n    expect(roundValueToStep(4, 3, 0)).toBe(3);\n  });\n\n  it('should be able to round up', () => {\n    expect(roundValueToStep(5, 3, 0)).toBe(6);\n  });\n});\n"
  },
  {
    "path": "src/common/utils/index.ts",
    "content": "import { WindowsTheme } from '../../types';\n\nexport const noOp = () => {};\n\nexport function clamp(value: number, min: number | null, max: number | null) {\n  if (max !== null && value > max) {\n    return max;\n  }\n  if (min !== null && value < min) {\n    return min;\n  }\n  return value;\n}\n\nfunction linearGradient(left: string, right: string) {\n  return `linear-gradient(to right, ${left}, ${right})`;\n}\n\nexport function mapFromWindowsTheme(\n  name: string,\n  windowsTheme: WindowsTheme,\n  useGradients: boolean\n) {\n  const {\n    ButtonDkShadow,\n    ButtonFace,\n    ButtonHilight,\n    ButtonLight,\n    ButtonShadow,\n    ButtonText,\n    Background,\n    Window,\n    WindowText,\n    ActiveTitle,\n    GradientActiveTitle,\n    GradientInactiveTitle,\n    InactiveTitle,\n    InactiveTitleText,\n    TitleText,\n    GrayText,\n    Hilight,\n    HilightText,\n    HotTrackingColor,\n    InfoWindow\n  } = windowsTheme;\n\n  return {\n    name,\n\n    anchor: HotTrackingColor,\n    anchorVisited: HotTrackingColor,\n    borderDark: ButtonShadow,\n    borderDarkest: ButtonDkShadow,\n    borderLight: ButtonLight,\n    borderLightest: ButtonHilight,\n    canvas: Window,\n    canvasText: WindowText,\n    canvasTextDisabled: ButtonShadow,\n    canvasTextDisabledShadow: ButtonHilight,\n    canvasTextInvert: HilightText,\n    checkmark: WindowText,\n    checkmarkDisabled: GrayText,\n    desktopBackground: Background,\n    flatDark: ButtonShadow,\n    flatLight: ButtonLight,\n    focusSecondary: ButtonHilight, // should be Hilight inverted\n    headerBackground: useGradients\n      ? linearGradient(ActiveTitle, GradientActiveTitle)\n      : ActiveTitle,\n    headerNotActiveBackground: useGradients\n      ? linearGradient(InactiveTitle, GradientInactiveTitle)\n      : InactiveTitle,\n    headerNotActiveText: InactiveTitleText,\n    headerText: TitleText,\n    hoverBackground: Hilight,\n    material: ButtonFace,\n    materialDark: InactiveTitle,\n    materialText: ButtonText,\n    materialTextDisabled: ButtonShadow,\n    materialTextDisabledShadow: ButtonHilight,\n    materialTextInvert: HilightText,\n    progress: Hilight,\n    tooltip: InfoWindow\n  };\n}\n\n// helper functions below are from Material UI (https://github.com/mui-org/material-ui)\nexport function getDecimalPrecision(num: number) {\n  if (Math.abs(num) < 1) {\n    const parts = num.toExponential().split('e-');\n    const matissaDecimalPart = parts[0].split('.')[1];\n    return (\n      (matissaDecimalPart ? matissaDecimalPart.length : 0) +\n      parseInt(parts[1], 10)\n    );\n  }\n\n  const decimalPart = num.toString().split('.')[1];\n  return decimalPart ? decimalPart.length : 0;\n}\n\nexport function roundValueToStep(value: number, step: number, min: number) {\n  const nearest = Math.round((value - min) / step) * step + min;\n  return Number(nearest.toFixed(getDecimalPrecision(step)));\n}\n\nexport function getSize(value: string | number) {\n  return typeof value === 'number' ? `${value}px` : value;\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "/* common */\nexport { default as styleReset } from './common/styleReset';\nexport { createScrollbars } from './common/index';\n\n/* components */\nexport * from './Anchor/Anchor';\nexport * from './AppBar/AppBar';\nexport * from './Avatar/Avatar';\nexport * from './Button/Button';\nexport * from './Checkbox/Checkbox';\nexport * from './ColorInput/ColorInput';\nexport * from './Counter/Counter';\nexport * from './DatePicker/DatePicker';\nexport * from './Frame/Frame';\nexport * from './GroupBox/GroupBox';\nexport * from './Handle/Handle';\nexport * from './Hourglass/Hourglass';\nexport * from './MenuList/MenuList';\nexport * from './Monitor/Monitor';\nexport * from './NumberInput/NumberInput';\nexport * from './ProgressBar/ProgressBar';\nexport * from './Radio/Radio';\nexport * from './ScrollView/ScrollView';\nexport * from './Select/Select';\nexport * from './Separator/Separator';\nexport * from './Slider/Slider';\nexport * from './Table/Table';\nexport * from './Tabs/Tabs';\nexport * from './TextInput/TextInput';\nexport * from './Toolbar/Toolbar';\nexport * from './Tooltip/Tooltip';\nexport * from './TreeView/TreeView';\nexport * from './Window/Window';\n\n/* deprecated components */\nexport * from './legacy/Bar';\nexport * from './legacy/Cutout';\nexport * from './legacy/Desktop';\nexport * from './legacy/Divider';\nexport * from './legacy/Fieldset';\nexport * from './legacy/List';\nexport * from './legacy/ListItem';\nexport * from './legacy/NumberField';\nexport * from './legacy/Panel';\nexport * from './legacy/Progress';\nexport * from './legacy/TextField';\nexport * from './legacy/Tree';\n"
  },
  {
    "path": "src/legacy/Bar.tsx",
    "content": "import { Handle, HandleProps } from '../Handle/Handle';\n\n/** @deprecated Use `HandleProps` */\nexport type BarProps = HandleProps;\n\n/** @deprecated Use `Handle` */\nexport const Bar = Handle;\n"
  },
  {
    "path": "src/legacy/Cutout.tsx",
    "content": "import { ScrollView, ScrollViewProps } from '../ScrollView/ScrollView';\n\n/** @deprecated Use `ScrollViewProps` */\nexport type CutoutProps = ScrollViewProps;\n\n/** @deprecated Use `ScrollView` */\nexport const Cutout = ScrollView;\n"
  },
  {
    "path": "src/legacy/Desktop.tsx",
    "content": "import { Monitor, MonitorProps } from '../Monitor/Monitor';\n\n/** @deprecated Use `MonitorProps` */\nexport type DesktopProps = MonitorProps;\n\n/** @deprecated Use `Monitor` */\nexport const Desktop = Monitor;\n"
  },
  {
    "path": "src/legacy/Divider.tsx",
    "content": "import { Separator, SeparatorProps } from '../Separator/Separator';\n\n/** @deprecated Use `SeparatorProps` */\nexport type DividerProps = SeparatorProps;\n\n/** @deprecated Use `Separator` */\nexport const Divider = Separator;\n"
  },
  {
    "path": "src/legacy/Fieldset.tsx",
    "content": "import { GroupBox, GroupBoxProps } from '../GroupBox/GroupBox';\n\n/** @deprecated Use `GroupBoxProps` */\nexport type FieldsetProps = GroupBoxProps;\n\n/** @deprecated Use `GroupBox` */\nexport const Fieldset = GroupBox;\n"
  },
  {
    "path": "src/legacy/List.tsx",
    "content": "import { MenuList, MenuListProps } from '../MenuList/MenuList';\n\n/** @deprecated Use `MenuListProps` */\nexport type ListProps = MenuListProps;\n\n/** @deprecated Use `MenuList` */\nexport const List = MenuList;\n"
  },
  {
    "path": "src/legacy/ListItem.tsx",
    "content": "import {\n  MenuListItem,\n  MenuListItemProps,\n  StyledMenuListItem\n} from '../MenuList/MenuList';\n\n/** @deprecated Use `MenuListItemProps` */\nexport type ListItemProps = MenuListItemProps;\n\n/** @deprecated Use `MenuListItem` */\nexport const ListItem = MenuListItem;\n\n/** @deprecated Use `StyledMenuListItem` */\nexport const StyledListItem = StyledMenuListItem;\n"
  },
  {
    "path": "src/legacy/NumberField.tsx",
    "content": "import { NumberInput, NumberInputProps } from '../NumberInput/NumberInput';\n\n/** @deprecated Use `NumberInputProps` */\nexport type NumberFieldProps = NumberInputProps;\n\n/** @deprecated Use `NumberInput` */\nexport const NumberField = NumberInput;\n"
  },
  {
    "path": "src/legacy/Panel.tsx",
    "content": "import { Frame, FrameProps } from '../Frame/Frame';\n\n/** @deprecated Use `FrameProps` */\nexport type PanelProps = FrameProps;\n\n/** @deprecated Use `Frame` */\nexport const Panel = Frame;\n"
  },
  {
    "path": "src/legacy/Progress.tsx",
    "content": "import { ProgressBar, ProgressBarProps } from '../ProgressBar/ProgressBar';\n\n/** @deprecated Use `ProgressBarProps` */\nexport type ProgressProps = ProgressBarProps;\n\n/** @deprecated Use `ProgressBar` */\nexport const Progress = ProgressBar;\n"
  },
  {
    "path": "src/legacy/TextField.tsx",
    "content": "import { TextInput, TextInputProps } from '../TextInput/TextInput';\n\n/** @deprecated Use `TextInputProps` */\nexport type TextFieldProps = TextInputProps;\n\n/** @deprecated Use `TextInput` */\nexport const TextField = TextInput;\n"
  },
  {
    "path": "src/legacy/Tree.tsx",
    "content": "import { TreeView, TreeViewProps } from '../TreeView/TreeView';\n\n/** @deprecated Use `TreeViewProps` */\nexport type TreeProps<T> = TreeViewProps<T>;\n\n/** @deprecated Use `TreeView` */\nexport const Tree = TreeView;\n"
  },
  {
    "path": "src/types.ts",
    "content": "import { ComponentType } from 'react';\n\nimport { Color, Theme, WindowsTheme } from './common/themes/types';\n\nexport type Sizes = 'sm' | 'md' | 'lg';\n\nexport type Orientation = 'horizontal' | 'vertical';\n\nexport type Direction = 'up' | 'down' | 'left' | 'right';\n\nexport type DimensionValue = undefined | number | string;\n\nexport type CommonStyledProps = {\n  /**\n   * \"as\" polymorphic prop allows to render a different HTML element or React component\n   * @see {@link https://styled-components.com/docs/api#as-polymorphic-prop}\n   */\n  as?: string | ComponentType<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type HTMLDataAttributes = Record<`data-${string}`, any>;\n\nexport type CommonThemeProps = {\n  'data-testid'?: string;\n  $disabled?: boolean;\n  shadow?: boolean;\n};\n\nexport { Color, Theme, WindowsTheme };\n"
  },
  {
    "path": "test/setup-test.ts",
    "content": "import '@testing-library/jest-dom';\nimport 'jest-styled-components';\n"
  },
  {
    "path": "test/utils.tsx",
    "content": "import { render } from '@testing-library/react';\nimport React from 'react';\nimport { ThemeProvider } from 'styled-components';\n\nimport themes from '../src/common/themes';\n\nexport const theme = themes.original;\n\nexport const renderWithTheme = (component: React.ReactNode) =>\n  render(<ThemeProvider theme={theme}>{component}</ThemeProvider>);\n\nexport class Touch {\n  #identifier: number;\n\n  #clientX = 0;\n\n  #clientY = 0;\n\n  #pageX = 0;\n\n  #pageY = 0;\n\n  constructor({\n    identifier,\n    clientX = 0,\n    clientY = 0,\n    pageX = 0,\n    pageY = 0\n  }: {\n    identifier: number;\n    clientX?: number;\n    clientY?: number;\n    pageX?: number;\n    pageY?: number;\n  }) {\n    this.#identifier = identifier;\n    this.#clientX = clientX;\n    this.#clientY = clientY;\n    this.#pageX = pageX;\n    this.#pageY = pageY;\n  }\n\n  get identifier() {\n    return this.#identifier;\n  }\n\n  get pageX() {\n    return this.#pageX;\n  }\n\n  get pageY() {\n    return this.#pageY;\n  }\n\n  get clientX() {\n    return this.#clientX;\n  }\n\n  get clientY() {\n    return this.#clientY;\n  }\n}\n"
  },
  {
    "path": "tsconfig.build.index.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"emitDeclarationOnly\": true,\n    \"outDir\": \"./dist\",\n    \"rootDir\": \"./src\",\n    \"sourceMap\": false\n  },\n  \"include\": [\n    \"types/global.d.ts\",\n    \"types/themes.d.ts\",\n    \"src/**/*.ts\",\n    \"src/*/*.tsx\"\n  ],\n  \"exclude\": [\n    \"**/*.spec.ts\",\n    \"**/*.spec.tsx\",\n    \"**/*.stories.ts\",\n    \"**/*.stories.tsx\",\n  ]\n}\n"
  },
  {
    "path": "tsconfig.build.themes.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"emitDeclarationOnly\": true,\n    \"outDir\": \"./dist/themes\",\n    \"rootDir\": \"./src/common/themes\",\n    \"sourceMap\": false\n  },\n  \"include\": [\n    \"src/common/themes/*.ts\",\n    \"src/common/themes/*.tsx\"\n  ]\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"alwaysStrict\": true,\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"esModuleInterop\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"importHelpers\": true,\n    \"jsx\": \"react\",\n    \"lib\": [\n      \"ESNext\",\n      \"DOM\"\n    ],\n    \"module\": \"Node16\",\n    \"moduleResolution\": \"Node\",\n    \"noEmitOnError\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noImplicitAny\": true,\n    \"noImplicitReturns\": true,\n    \"noImplicitThis\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"outDir\": \"./lib\",\n    \"paths\": {\n      \"react95\": [\n        \"./src\"\n      ]\n    },\n    \"resolveJsonModule\": true,\n    \"rootDir\": \"./\",\n    \"skipLibCheck\": true,\n    \"sourceMap\": true,\n    \"strict\": true,\n    \"strictFunctionTypes\": true,\n    \"strictNullChecks\": true,\n    \"strictPropertyInitialization\": true,\n    \"target\": \"ES2018\"\n  },\n  \"include\": [\n    \"**/*.ts\",\n    \"**/*.tsx\"\n  ],\n  \"exclude\": [\n    \"coverage\",\n    \"dist\",\n    \"node_modules\",\n    \"storybook\"\n  ]\n}\n"
  },
  {
    "path": "types/globals.d.ts",
    "content": "declare module '*.png' {\n  const value: string;\n  export = value;\n}\n\ndeclare module '*.woff2' {\n  const value: string;\n  export = value;\n}\n"
  },
  {
    "path": "types/themes.d.ts",
    "content": "// import original module declarations\nimport { Theme } from '../src/types';\nimport 'styled-components';\n\n// and extend them!\ndeclare module 'styled-components' {\n  export interface DefaultTheme extends Theme {}\n}\n"
  }
]