Full Code of react95-io/React95 for AI

master 871d5335daeb cached
246 files
466.6 KB
136.4k tokens
199 symbols
1 requests
Download .txt
Showing preview only (518K chars total). Download the full file or copy to clipboard to get everything.
Repository: react95-io/React95
Branch: master
Commit: 871d5335daeb
Files: 246
Total size: 466.6 KB

Directory structure:
gitextract_rz85rgj9/

├── .babelrc
├── .codesandbox/
│   └── ci.json
├── .editorconfig
├── .eslintrc.js
├── .firebaserc
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── ci.yml
│       └── release.yml
├── .gitignore
├── .prettierrc
├── .storybook/
│   ├── decorators/
│   │   └── withGlobalStyle.tsx
│   ├── main.ts
│   ├── manager.css
│   ├── manager.ts
│   ├── preview.ts
│   ├── theme-picker/
│   │   ├── ThemeButton.tsx
│   │   ├── ThemeList.tsx
│   │   ├── ThemeProvider.tsx
│   │   ├── constants.ts
│   │   └── register.ts
│   └── theme.js
├── LICENSE
├── README.md
├── docs/
│   ├── Contributing.stories.mdx
│   ├── Getting-Started.stories.mdx
│   ├── Submit-your-Project.stories.mdx
│   └── Welcome.stories.mdx
├── firebase.json
├── jest.config.js
├── package.json
├── rollup.config.js
├── src/
│   ├── Anchor/
│   │   ├── Anchor.spec.tsx
│   │   ├── Anchor.stories.tsx
│   │   └── Anchor.tsx
│   ├── AppBar/
│   │   ├── AppBar.spec.tsx
│   │   ├── AppBar.stories.tsx
│   │   └── AppBar.tsx
│   ├── Avatar/
│   │   ├── Avatar.spec.tsx
│   │   ├── Avatar.stories.tsx
│   │   └── Avatar.tsx
│   ├── Button/
│   │   ├── Button.spec.tsx
│   │   ├── Button.stories.tsx
│   │   └── Button.tsx
│   ├── Checkbox/
│   │   ├── Checkbox.spec.tsx
│   │   ├── Checkbox.stories.tsx
│   │   └── Checkbox.tsx
│   ├── ColorInput/
│   │   ├── ColorInput.spec.tsx
│   │   ├── ColorInput.stories.tsx
│   │   └── ColorInput.tsx
│   ├── Counter/
│   │   ├── Counter.spec.tsx
│   │   ├── Counter.stories.tsx
│   │   ├── Counter.tsx
│   │   └── Digit.tsx
│   ├── DatePicker/
│   │   ├── DatePicker.stories.tsx
│   │   └── DatePicker.tsx
│   ├── Frame/
│   │   ├── Frame.spec.tsx
│   │   ├── Frame.stories.tsx
│   │   └── Frame.tsx
│   ├── GroupBox/
│   │   ├── GroupBox.spec.tsx
│   │   ├── GroupBox.stories.tsx
│   │   └── GroupBox.tsx
│   ├── Handle/
│   │   ├── Handle.spec.tsx
│   │   ├── Handle.stories.tsx
│   │   └── Handle.tsx
│   ├── Hourglass/
│   │   ├── Hourglass.spec.tsx
│   │   ├── Hourglass.stories.tsx
│   │   ├── Hourglass.tsx
│   │   └── base64hourglass.tsx
│   ├── MenuList/
│   │   ├── MenuList.spec.tsx
│   │   ├── MenuList.stories.tsx
│   │   ├── MenuList.tsx
│   │   ├── MenuListItem.spec.tsx
│   │   └── MenuListItem.tsx
│   ├── Monitor/
│   │   ├── Monitor.spec.tsx
│   │   ├── Monitor.stories.tsx
│   │   └── Monitor.tsx
│   ├── NumberInput/
│   │   ├── NumberInput.spec.tsx
│   │   ├── NumberInput.stories.tsx
│   │   └── NumberInput.tsx
│   ├── ProgressBar/
│   │   ├── ProgressBar.spec.tsx
│   │   ├── ProgressBar.stories.tsx
│   │   └── ProgressBar.tsx
│   ├── Radio/
│   │   ├── Radio.spec.tsx
│   │   ├── Radio.stories.tsx
│   │   └── Radio.tsx
│   ├── ScrollView/
│   │   ├── ScrollView.spec.tsx
│   │   ├── ScrollView.stories.tsx
│   │   └── ScrollView.tsx
│   ├── Select/
│   │   ├── Select.spec.tsx
│   │   ├── Select.stories.data.ts
│   │   ├── Select.stories.tsx
│   │   ├── Select.styles.tsx
│   │   ├── Select.tsx
│   │   ├── Select.types.ts
│   │   ├── SelectNative.spec.tsx
│   │   ├── SelectNative.tsx
│   │   ├── useSelectCommon.tsx
│   │   └── useSelectState.ts
│   ├── Separator/
│   │   ├── Separator.spec.tsx
│   │   ├── Separator.stories.tsx
│   │   └── Separator.tsx
│   ├── Slider/
│   │   ├── Slider.spec.tsx
│   │   ├── Slider.stories.tsx
│   │   └── Slider.tsx
│   ├── Table/
│   │   ├── Table.spec.tsx
│   │   ├── Table.stories.tsx
│   │   ├── Table.tsx
│   │   ├── TableBody.spec.tsx
│   │   ├── TableBody.tsx
│   │   ├── TableDataCell.spec.tsx
│   │   ├── TableDataCell.tsx
│   │   ├── TableHead.spec.tsx
│   │   ├── TableHead.tsx
│   │   ├── TableHeadCell.spec.tsx
│   │   ├── TableHeadCell.tsx
│   │   ├── TableRow.spec.tsx
│   │   └── TableRow.tsx
│   ├── Tabs/
│   │   ├── Tab.spec.tsx
│   │   ├── Tab.tsx
│   │   ├── TabBody.spec.tsx
│   │   ├── TabBody.tsx
│   │   ├── Tabs.spec.tsx
│   │   ├── Tabs.stories.tsx
│   │   └── Tabs.tsx
│   ├── TextInput/
│   │   ├── TextInput.spec.tsx
│   │   ├── TextInput.stories.tsx
│   │   └── TextInput.tsx
│   ├── Toolbar/
│   │   ├── Toolbar.spec.tsx
│   │   └── Toolbar.tsx
│   ├── Tooltip/
│   │   ├── Tooltip.spec.tsx
│   │   ├── Tooltip.stories.tsx
│   │   └── Tooltip.tsx
│   ├── TreeView/
│   │   ├── TreeView.spec.tsx
│   │   ├── TreeView.stories.tsx
│   │   └── TreeView.tsx
│   ├── Window/
│   │   ├── Window.spec.tsx
│   │   ├── Window.stories.tsx
│   │   ├── Window.tsx
│   │   ├── WindowContent.spec.tsx
│   │   ├── WindowContent.tsx
│   │   ├── WindowHeader.spec.tsx
│   │   └── WindowHeader.tsx
│   ├── assets/
│   │   ├── fonts/
│   │   │   └── src/
│   │   │       ├── ms-sans-serif/
│   │   │       │   ├── license.txt
│   │   │       │   └── readme.txt
│   │   │       └── ms-sans-serif-bold/
│   │   │           ├── license.txt
│   │   │           └── readme.txt
│   │   └── images/
│   │       └── logo.psd
│   ├── common/
│   │   ├── SwitchBase.ts
│   │   ├── constants.ts
│   │   ├── hooks/
│   │   │   ├── useControlledOrUncontrolled.ts
│   │   │   ├── useEventCallback.ts
│   │   │   ├── useForkRef.spec.tsx
│   │   │   ├── useForkRef.ts
│   │   │   ├── useId.spec.ts
│   │   │   ├── useId.ts
│   │   │   └── useIsFocusVisible.ts
│   │   ├── index.ts
│   │   ├── styleReset.ts
│   │   ├── system.ts
│   │   ├── themes/
│   │   │   ├── aiee.ts
│   │   │   ├── ash.ts
│   │   │   ├── azureOrange.ts
│   │   │   ├── bee.ts
│   │   │   ├── blackAndWhite.ts
│   │   │   ├── blue.ts
│   │   │   ├── brick.ts
│   │   │   ├── candy.ts
│   │   │   ├── cherry.ts
│   │   │   ├── coldGray.ts
│   │   │   ├── counterStrike.ts
│   │   │   ├── darkTeal.ts
│   │   │   ├── denim.ts
│   │   │   ├── eggplant.ts
│   │   │   ├── fxDev.ts
│   │   │   ├── highContrast.ts
│   │   │   ├── honey.ts
│   │   │   ├── hotChocolate.ts
│   │   │   ├── hotdogStand.ts
│   │   │   ├── index.ts
│   │   │   ├── lilac.ts
│   │   │   ├── lilacRoseDark.ts
│   │   │   ├── maple.ts
│   │   │   ├── marine.ts
│   │   │   ├── matrix.ts
│   │   │   ├── millenium.ts
│   │   │   ├── modernDark.ts
│   │   │   ├── molecule.ts
│   │   │   ├── monochrome.ts
│   │   │   ├── ninjaTurtles.ts
│   │   │   ├── olive.ts
│   │   │   ├── original.ts
│   │   │   ├── pamelaAnderson.ts
│   │   │   ├── peggysPastels.ts
│   │   │   ├── plum.ts
│   │   │   ├── polarized.ts
│   │   │   ├── powerShell.ts
│   │   │   ├── rainyDay.ts
│   │   │   ├── raspberry.ts
│   │   │   ├── redWine.ts
│   │   │   ├── rose.ts
│   │   │   ├── seawater.ts
│   │   │   ├── shelbiTeal.ts
│   │   │   ├── slate.ts
│   │   │   ├── solarizedDark.ts
│   │   │   ├── solarizedLight.ts
│   │   │   ├── spruce.ts
│   │   │   ├── stormClouds.ts
│   │   │   ├── theSixtiesUSA.ts
│   │   │   ├── tokyoDark.ts
│   │   │   ├── toner.ts
│   │   │   ├── tooSexy.ts
│   │   │   ├── travel.ts
│   │   │   ├── types.ts
│   │   │   ├── vaporTeal.ts
│   │   │   ├── vermillion.ts
│   │   │   ├── violetDark.ts
│   │   │   ├── vistaesqueMidnight.ts
│   │   │   ├── water.ts
│   │   │   ├── white.ts
│   │   │   ├── windows1.ts
│   │   │   └── wmii.ts
│   │   └── utils/
│   │       ├── events.spec.tsx
│   │       ├── events.ts
│   │       ├── index.spec.ts
│   │       └── index.ts
│   ├── index.ts
│   ├── legacy/
│   │   ├── Bar.tsx
│   │   ├── Cutout.tsx
│   │   ├── Desktop.tsx
│   │   ├── Divider.tsx
│   │   ├── Fieldset.tsx
│   │   ├── List.tsx
│   │   ├── ListItem.tsx
│   │   ├── NumberField.tsx
│   │   ├── Panel.tsx
│   │   ├── Progress.tsx
│   │   ├── TextField.tsx
│   │   └── Tree.tsx
│   └── types.ts
├── test/
│   ├── setup-test.ts
│   └── utils.tsx
├── tsconfig.build.index.json
├── tsconfig.build.themes.json
├── tsconfig.json
└── types/
    ├── globals.d.ts
    └── themes.d.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "sourceType": "unambiguous",
  "presets": [
    [
      "@babel/preset-env",
      {
        "shippedProposals": true,
        "loose": true
      }
    ],
    "@babel/preset-typescript"
  ],
  "plugins": [
    "@babel/plugin-transform-shorthand-properties",
    "@babel/plugin-transform-block-scoping",
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ],
    [
      "@babel/plugin-proposal-class-properties",
      {
        "loose": true
      }
    ],
    [
      "@babel/plugin-proposal-private-property-in-object",
      {
        "loose": true
      }
    ],
    [
      "@babel/plugin-proposal-private-methods",
      {
        "loose": true
      }
    ],
    "@babel/plugin-proposal-export-default-from",
    "@babel/plugin-syntax-dynamic-import",
    [
      "@babel/plugin-proposal-object-rest-spread",
      {
        "loose": true,
        "useBuiltIns": true
      }
    ],
    "@babel/plugin-transform-classes",
    "@babel/plugin-transform-arrow-functions",
    "@babel/plugin-transform-parameters",
    "@babel/plugin-transform-destructuring",
    "@babel/plugin-transform-spread",
    "@babel/plugin-transform-for-of",
    "babel-plugin-macros",
    "@babel/plugin-proposal-optional-chaining",
    "@babel/plugin-proposal-nullish-coalescing-operator"
  ]
}


================================================
FILE: .codesandbox/ci.json
================================================
{
  "buildCommand": "build:prod",
  "node": "16",
  "sandboxes": [
    "react95-template-xkfj0"
  ]
}


================================================
FILE: .editorconfig
================================================
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = yes
insert_final_newline = yes


================================================
FILE: .eslintrc.js
================================================
module.exports = {
  extends: [
    'plugin:@typescript-eslint/recommended',
    'airbnb',
    'plugin:prettier/recommended',
    'plugin:react-hooks/recommended'
  ],
  parser: '@typescript-eslint/parser',
  plugins: ['react', 'prettier'],
  env: {
    browser: true,
    es6: true,
    jest: true
  },
  rules: {
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-empty-function': 'off',
    '@typescript-eslint/no-empty-interface': 'off',
    '@typescript-eslint/no-use-before-define': 'off',
    '@typescript-eslint/no-unused-vars': [
      'error',
      {
        argsIgnorePattern: '^_\\d*$'
      }
    ],
    'import/extensions': ['error', { js: 'never', ts: 'never', tsx: 'never' }],
    'import/no-unresolved': [
      'error',
      // TODO: Remove ../../test/utils when TypeScript migration is complete
      { ignore: ['react95', '../../test/utils'] }
    ],
    'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
    'import/prefer-default-export': 'off',
    'jsx-a11y/label-has-associated-control': ['error', { assert: 'either' }],
    'jsx-a11y/label-has-for': 'off',
    'no-nested-ternary': 'off',
    'prettier/prettier': 'error',
    'react/forbid-prop-types': 'off',
    'react/jsx-filename-extension': [
      'warn',
      { extensions: ['.js', '.jsx', '.tsx'] }
    ],
    'react/jsx-props-no-spreading': 'off',
    'react/no-array-index-key': 'off',
    'react/prop-types': 'off',
    'react/require-default-props': 'off',
    'react/static-property-placement': ['error', 'static public field']
  },
  overrides: [
    {
      files: ['*.spec.@(js|jsx|ts|tsx)', '*.stories.@(js|jsx|ts|tsx)'],
      rules: {
        'no-console': 'off'
      }
    },
    {
      files: ['*.@(ts|tsx)'],
      rules: {
        // This is handled by @typescript-eslint/no-unused-vars
        'no-undef': 'off'
      }
    }
  ],
  settings: {
    'import/parsers': {
      '@typescript-eslint/parser': ['.ts', '.tsx']
    },
    'import/resolver': {
      typescript: {}
    }
  }
};


================================================
FILE: .firebaserc
================================================
{
  "projects": {
    "default": "react95-storybook"
  }
}


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: arturbien
patreon: arturbien
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: https://www.paypal.me/react95


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  pull_request:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Git Checkout
        uses: actions/checkout@v2

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Cache packages
        uses: actions/cache@v3
        with:
          key: node_modules-v4-${{ hashFiles('yarn.lock') }}
          path: |-
            node_modules
            */node_modules
          restore-keys: 'node_modules-v4-'

      - name: Yarn install
        run: yarn install --ignore-optional --frozen-lockfile

      - name: Lint
        run: yarn run lint

  type-check:
    runs-on: ubuntu-latest
    steps:
      - name: Git Checkout
        uses: actions/checkout@v2

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Cache packages
        uses: actions/cache@v3
        with:
          key: node_modules-v4-${{ hashFiles('yarn.lock') }}
          path: |-
            node_modules
            */node_modules
          restore-keys: 'node_modules-v4-'

      - name: Yarn install
        run: yarn install --ignore-optional --frozen-lockfile

      - name: Type Check
        run: yarn run typescript

  test:
    runs-on: ubuntu-latest
    steps:
      - name: Git Checkout
        uses: actions/checkout@v2

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Cache packages
        uses: actions/cache@v3
        with:
          key: node_modules-v4-${{ hashFiles('yarn.lock') }}
          path: |-
            node_modules
            */node_modules
          restore-keys: 'node_modules-v4-'

      - name: Yarn install
        run: yarn install --ignore-optional --frozen-lockfile

      - name: Test
        run: yarn run test:ci

  build-library:
    runs-on: ubuntu-latest
    steps:
      - name: Git Checkout
        uses: actions/checkout@v2

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Cache packages
        uses: actions/cache@v3
        with:
          key: node_modules-v4-${{ hashFiles('yarn.lock') }}
          path: |-
            node_modules
            */node_modules
          restore-keys: 'node_modules-v4-'

      - name: Yarn install
        run: yarn install --ignore-optional --frozen-lockfile

      - name: Build library
        run: yarn run build

  build-storybook:
    runs-on: ubuntu-latest
    steps:
      - name: Git Checkout
        uses: actions/checkout@v2

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Cache packages
        uses: actions/cache@v3
        with:
          key: node_modules-v4-${{ hashFiles('yarn.lock') }}
          path: |-
            node_modules
            */node_modules
          restore-keys: 'node_modules-v4-'

      - name: Yarn install
        run: yarn install --ignore-optional --frozen-lockfile

      - name: Build Storybook
        run: yarn run build:storybook


================================================
FILE: .github/workflows/release.yml
================================================
name: Release

on:
  push:
    branches:
      - master
      - next
      - beta
      - alpha
      - '*.x' # maintenance releases

jobs:
  release-library:
    runs-on: ubuntu-latest
    steps:
      - name: Git Checkout
        uses: actions/checkout@v2

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Cache packages
        uses: actions/cache@v3
        with:
          key: node_modules-v4-${{ hashFiles('yarn.lock') }}
          path: |-
            node_modules
            */node_modules
          restore-keys: 'node_modules-v4-'

      - name: Yarn install
        run: yarn install --ignore-optional --frozen-lockfile

      - name: Build library
        run: yarn run build

      - name: Deploy library
        run: yarn run semantic-release || true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

  release-storybook:
    needs:
      - release-library
    if: github.ref == 'refs/heads/master'
    runs-on: ubuntu-latest
    steps:
      - name: Git Checkout
        uses: actions/checkout@v2

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Cache packages
        uses: actions/cache@v3
        with:
          key: node_modules-v4-${{ hashFiles('yarn.lock') }}
          path: |-
            node_modules
            */node_modules
          restore-keys: 'node_modules-v4-'

      - name: Yarn install
        run: yarn install --ignore-optional --frozen-lockfile

      - name: Build Storybook
        run: yarn run build:storybook

      - name: Deploy Storybook
        run: ./node_modules/.bin/firebase deploy --token=$FIREBASE_TOKEN
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}


================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# library build
/cjs
/esm
/themes
/images
/fonts
/dist

# storybook
/storybook
/.firebase

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

# JetBrains IDEs
.idea/*


================================================
FILE: .prettierrc
================================================
{
  "arrowParens": "avoid",
  "bracketSpacing": true,
  "htmlWhitespaceSensitivity": "css",
  "insertPragma": false,
  "jsxBracketSameLine": false,
  "jsxSingleQuote": true,
  "printWidth": 80,
  "proseWrap": "preserve",
  "quoteProps": "as-needed",
  "requirePragma": false,
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "none",
  "useTabs": false
}


================================================
FILE: .storybook/decorators/withGlobalStyle.tsx
================================================
import { DecoratorFn } from '@storybook/react';
import React from 'react';
import { createGlobalStyle } from 'styled-components';

import ms_sans_serif from '../../src/assets/fonts/dist/ms_sans_serif.woff2';
import ms_sans_serif_bold from '../../src/assets/fonts/dist/ms_sans_serif_bold.woff2';
import styleReset from '../../src/common/styleReset';

const GlobalStyle = createGlobalStyle`
  ${styleReset}
  @font-face {
    font-family: 'ms_sans_serif';
    src: url('${ms_sans_serif}') format('woff2');
    font-weight: 400;
    font-style: normal
  }
  @font-face {
    font-family: 'ms_sans_serif';
    src: url('${ms_sans_serif_bold}') format("woff2");
    font-weight: bold;
    font-style: normal
  }
  html, body, #root {
   height: 100%;
  }
  #root > * {
    height: 100%;
    box-sizing: border-box;
  }
  body {
    font-family: 'ms_sans_serif', 'sans-serif';
  }
`;

export const withGlobalStyle: DecoratorFn = story => (
  <>
    <GlobalStyle />
    {story()}
  </>
);


================================================
FILE: .storybook/main.ts
================================================
import type { StorybookConfig } from '@storybook/react/types';
import type { PropItem } from 'react-docgen-typescript';

const path = require('path');

const storybookConfig: StorybookConfig = {
  stories: ['../@(docs|src)/**/*.stories.@(tsx|mdx)'],
  addons: [
    {
      name: '@storybook/addon-docs',
      options: {
        sourceLoaderOptions: {
          injectStoryParameters: false
        }
      }
    },
    '@storybook/addon-storysource',
    './theme-picker/register.ts'
  ],
  core: {
    builder: 'webpack5'
  },
  features: {
    babelModeV7: true,
    storyStoreV7: true,
    modernInlineRender: true,
    postcss: false
  },
  typescript: {
    check: false,
    checkOptions: {},
    reactDocgen: 'react-docgen-typescript',
    reactDocgenTypescriptOptions: {
      shouldExtractLiteralValuesFromEnum: true,
      propFilter: (prop: PropItem) =>
        prop.parent ? !/node_modules/.test(prop.parent.fileName) : true
    }
  },
  webpackFinal: config => {
    config.resolve = {
      ...config.resolve,
      alias: {
        ...config.resolve?.alias,
        react95: path.resolve(__dirname, '../src/index')
      }
    };

    return config;
  }
};

module.exports = storybookConfig;


================================================
FILE: .storybook/manager.css
================================================
/* Remove from the sidebar menu stories that contains "unstable" */
a[data-item-id$='-unstable'].sidebar-item,
a[data-item-id*='-unstable-'].sidebar-item,
button[data-item-id$='-unstable'].sidebar-item,
button[data-item-id$='-unstable-'].sidebar-item {
  display: none !important;
}


================================================
FILE: .storybook/manager.ts
================================================
import './manager.css';

import { addons } from '@storybook/addons';
import theme from './theme';

addons.setConfig({
  theme
});


================================================
FILE: .storybook/preview.ts
================================================
import { DecoratorFn, Parameters } from '@storybook/react';
import { withGlobalStyle } from './decorators/withGlobalStyle';
import { withThemesProvider } from './theme-picker/ThemeProvider';

export const decorators: DecoratorFn[] = [withGlobalStyle, withThemesProvider];

export const parameters: Parameters = {
  layout: 'fullscreen',
  options: {
    storySort: {
      order: [
        'Docs',
        [
          'Welcome to React95',
          'Getting Started',
          'Contributing',
          'Submit your Project'
        ],
        'Controls',
        'Environment',
        'Layout',
        'Typography',
        'Other'
      ]
    }
  }
};


================================================
FILE: .storybook/theme-picker/ThemeButton.tsx
================================================
import React, { useCallback } from 'react';
import { ThemeProvider } from 'styled-components';
import { Button } from '../../src/Button/Button';
import { Theme } from '../../src/types';

export function ThemeButton({
  active,
  onChoose,
  theme
}: {
  active: boolean;
  onChoose: (themeName: string) => void;
  theme: Theme;
}) {
  const handleClick = useCallback(() => {
    onChoose(theme.name);
  }, []);

  return (
    <ThemeProvider theme={theme}>
      <Button active={active} onClick={handleClick}>
        {theme.name}
      </Button>
    </ThemeProvider>
  );
}


================================================
FILE: .storybook/theme-picker/ThemeList.tsx
================================================
import { useAddonState } from '@storybook/api';
import React, { useCallback } from 'react';
import styled from 'styled-components';

import themes from '../../src/common/themes';
import { Theme } from '../../src/types';
import { THEMES_ID } from './constants';
import { ThemeButton } from './ThemeButton';

const {
  original,
  rainyDay,
  vaporTeal,
  theSixtiesUSA,
  olive,
  tokyoDark,
  rose,
  plum,
  matrix,
  travel,
  ...otherThemes
} = themes;

const themeList = [
  original,
  rainyDay,
  vaporTeal,
  theSixtiesUSA,
  olive,
  tokyoDark,
  rose,
  plum,
  matrix,
  travel,
  ...Object.values(otherThemes)
];

type ThemesProps = {
  active?: boolean;
};

const Wrapper = styled.div<{ theme: Theme }>`
  display: grid;
  padding: 1em;
  gap: 1em;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  grid-template-rows: repeat(auto-fill, 40px);
  background-color: ${({ theme }) => theme.material};
`;

export function ThemeList({ active }: ThemesProps) {
  const [themeName, setThemeName] = useAddonState(THEMES_ID, 'original');

  const handleChoose = useCallback(
    (newThemeName: string) => {
      setThemeName(newThemeName);
    },
    [setThemeName]
  );

  if (!active) {
    return <></>;
  }

  return (
    <Wrapper key={THEMES_ID} theme={themes.original}>
      {themeList.map(theme => (
        <ThemeButton
          active={themeName === theme.name}
          key={theme.name}
          onChoose={handleChoose}
          theme={theme}
        />
      ))}
    </Wrapper>
  );
}


================================================
FILE: .storybook/theme-picker/ThemeProvider.tsx
================================================
import { useAddonState } from '@storybook/client-api';
import { DecoratorFn } from '@storybook/react';
import React from 'react';
import { ThemeProvider } from 'styled-components';

import themes from '../../src/common/themes/index';
import { THEMES_ID } from './constants';

export const withThemesProvider: DecoratorFn = story => {
  const [themeName] = useAddonState(THEMES_ID, 'original');

  return (
    <ThemeProvider theme={themes[themeName] ?? themes.original}>
      {story()}
    </ThemeProvider>
  );
};


================================================
FILE: .storybook/theme-picker/constants.ts
================================================
export const THEMES_ID = 'storybook/themes';


================================================
FILE: .storybook/theme-picker/register.ts
================================================
import addons, { makeDecorator, types } from '@storybook/addons';
import { THEMES_ID } from './constants';
import { ThemeList } from './ThemeList';

addons.register(THEMES_ID, () => {
  addons.addPanel(`${THEMES_ID}/panel`, {
    title: 'Themes',
    type: types.PANEL,
    render: ThemeList
  });
});

export default makeDecorator({
  name: 'withThemesProvider',
  parameterName: 'theme',
  wrapper: (getStory, context) => getStory(context)
});


================================================
FILE: .storybook/theme.js
================================================
import { create } from '@storybook/theming';

import brandImage from './logo.png';

export default create({
  base: 'light',
  brandTitle: 'React95',
  brandUrl: 'https://react95.io',
  brandImage,
  brandTarget: '_self',

  // UI
  appBg: '#dfdfdf',
  appContentBg: '#ffffff',
  appBorderColor: '#848584',
  appBorderRadius: 0,

  // Typography
  fontBase: '"ms_sans_serif", sans-serif',
  fontCode: 'monospace',

  // Text colors
  textColor: '#0a0a0a',
  textInverseColor: 'rgba(255,255,255,0.9)',

  // Toolbar default and active colors
  barTextColor: '#c6c6c6',
  barSelectedColor: '#fefefe',
  barBg: '#060084',

  // Form colors
  inputBg: '#ffffff',
  inputBorder: '#dfdfdf',
  inputTextColor: '#0a0a0a',
  inputBorderRadius: 0
});


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Artur Bień

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<h1 align="center">React95</h1>

<p align="center">
  <a href="https://www.npmjs.com/package/react95"><img src="https://flat.badgen.net/npm/dt/react95" alt="NPM" /></a>
  <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>
  <a href="https://www.npmjs.com/package/react95"><img src="https://flat.badgen.net/npm/v/react95" alt="React95 version" /></a>
  <a href="https://www.npmjs.com/package/react95"><img src="https://flat.badgen.net/npm/license/react95" alt="React95 license" /></a>
  <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>
</p>
<h3 align="center">
  <a href="https://storybook.react95.io/?path=/story/window--default">Components</a> -
  <a href="https://coins95.web.app/">Demo app</a> -
  <a href="https://github.com/react95-io/react95-native">React Native</a> -
  <a href="https://join.slack.com/t/react95/shared_invite/enQtOTA1NzEyNjAyNTc4LWYxZjU3NWRiMWJlMGJiMjhkNzE2MDA3ZmZjZDc1YmY0ODdlZjMwZDA1NWJiYWExYmY1NTJmNmE4OWVjNWFhMTE">Slack</a> -
  <a href="https://www.paypal.me/react95">PayPal donation 💰</a>
</h3>
<p align="center">
  <b>Refreshed</b> Windows95 UI components for your modern React apps. <br /> Built with styled-components 💅</p>

![hero](https://user-images.githubusercontent.com/28541613/81947711-28b05580-9601-11ea-964a-c3a6de998496.png)

### Support

- [Become a backer or sponsor on Patreon](https://www.patreon.com/arturbien)
- [One-time donation via PayPal](https://www.paypal.me/react95)

## Getting Started

First, install component library and styled-components in your project directory:

```sh
# yarn
$ yarn add react95 styled-components

# npm
$ npm install react95 styled-components
```

Apply style reset, wrap your app with ThemeProvider with theme of your choice... and you are ready to go! 🚀

```jsx
import React from 'react';
import { createGlobalStyle, ThemeProvider } from 'styled-components';

import { MenuList, MenuListItem, Separator, styleReset } from 'react95';
// pick a theme of your choice
import original from 'react95/dist/themes/original';
// original Windows95 font (optionally)
import ms_sans_serif from 'react95/dist/fonts/ms_sans_serif.woff2';
import ms_sans_serif_bold from 'react95/dist/fonts/ms_sans_serif_bold.woff2';

const GlobalStyles = createGlobalStyle`
  ${styleReset}
  @font-face {
    font-family: 'ms_sans_serif';
    src: url('${ms_sans_serif}') format('woff2');
    font-weight: 400;
    font-style: normal
  }
  @font-face {
    font-family: 'ms_sans_serif';
    src: url('${ms_sans_serif_bold}') format('woff2');
    font-weight: bold;
    font-style: normal
  }
  body {
    font-family: 'ms_sans_serif';
  }
`;

const App = () => (
  <div>
    <GlobalStyles />
    <ThemeProvider theme={original}>
      <MenuList>
        <MenuListItem>🎤 Sing</MenuListItem>
        <MenuListItem>💃🏻 Dance</MenuListItem>
        <Separator />
        <MenuListItem disabled>😴 Sleep</MenuListItem>
      </MenuList>
    </ThemeProvider>
  </div>
);

export default App;
```

### Submit your project

Apps built with React95 will be featured on the official React95 [website](https://react95.io) 🤟🏻

### Contributing

Any 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.

If 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 ♥️


================================================
FILE: docs/Contributing.stories.mdx
================================================
import { Meta } from '@storybook/addon-docs';

<Meta title='Docs/Contributing' />

# Contributing

Any 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.

If you want to help with the project, feel free to [open pull requests][1],
[submit issues or component proposals][2] and join our [Slack channels][3]!
Let's bring this UI back to life ♥️

[1]: https://github.com/arturbien/react95/pulls
[2]: https://github.com/arturbien/React95/issues
[3]: https://join.slack.com/t/react95/shared_invite/enQtOTA1NzEyNjAyNTc4LWYxZjU3NWRiMWJlMGJiMjhkNzE2MDA3ZmZjZDc1YmY0ODdlZjMwZDA1NWJiYWExYmY1NTJmNmE4OWVjNWFhMTE


================================================
FILE: docs/Getting-Started.stories.mdx
================================================
import { Meta } from '@storybook/addon-docs';

<Meta title='Docs/Getting Started' />

# Installation

React95 is available as an [npm package](https://www.npmjs.com/package/react95).

## npm

To install and save your `package.json` dependencies, run:

```sh
# yarn
yarn add react95 styled-components

# npm
npm install -S react95 styled-components
```

In order to have `react95` working properly, you'll also need
[styled-components 💅](https://github.com/styled-components/styled-components),
this way you can use custom themes and get the best of the library 🙂

## Usage

Apply style reset, wrap your app content with ThemeProvider with theme of your
choice... and you are ready to go! 🚀

```jsx
import React from 'react';
import { MenuList, MenuListItem, Separator, styleReset } from 'react95';
import { createGlobalStyle, ThemeProvider } from 'styled-components';

/* Pick a theme of your choice */
import original from 'react95/dist/themes/original';

/* Original Windows95 font (optional) */
import ms_sans_serif from 'react95/dist/fonts/ms_sans_serif.woff2';
import ms_sans_serif_bold from 'react95/dist/fonts/ms_sans_serif_bold.woff2';

const GlobalStyles = createGlobalStyle`
  ${styleReset}
  @font-face {
    font-family: 'ms_sans_serif';
    src: url('${ms_sans_serif}') format('woff2');
    font-weight: 400;
    font-style: normal
  }
  @font-face {
    font-family: 'ms_sans_serif';
    src: url('${ms_sans_serif_bold}') format('woff2');
    font-weight: bold;
    font-style: normal
  }
  body, input, select, textarea {
    font-family: 'ms_sans_serif';
  }
`;

const App = () => (
  <div>
    <GlobalStyles />
    <ThemeProvider theme={original}>
      <MenuList>
        <MenuListItem>🎤 Sing</MenuListItem>
        <MenuListItem>💃🏻 Dance</MenuListItem>
        <Separator />
        <MenuListItem disabled>😴 Sleep</MenuListItem>
      </MenuList>
    </ThemeProvider>
  </div>
);

export default App;
```


================================================
FILE: docs/Submit-your-Project.stories.mdx
================================================
import { Meta } from '@storybook/addon-docs';

<Meta title='Docs/Submit your Project' />

# Submit your Project

Apps built with React95 will be featured on the official React95 [website](https://react95.io) 🤟🏻

In order to submit your project, just drop a comment on [this issue](https://github.com/arturbien/React95/issues/25)!


================================================
FILE: docs/Welcome.stories.mdx
================================================
import { Meta } from '@storybook/addon-docs';

<Meta title='Docs/Welcome to React95' />

# Welcome to React95

<a href='https://www.npmjs.com/package/react95'>
  <img src='https://flat.badgen.net/npm/dt/react95' alt='NPM' />
</a>
&nbsp;
<a href='https://www.npmjs.com/package/react95'>
  <img src='https://flat.badgen.net/npm/v/react95' alt='React95 version' />
</a>
&nbsp;
<a href='https://www.npmjs.com/package/react95'>
  <img
    src='https://flat.badgen.net/npm/license/react95'
    alt='React95 license'
  />
</a>

<h3>
  <a href='?path=/story/controls-button--default'>Components</a>
  &nbsp;-&nbsp;
  <a href='https://coins95.web.app/'>Demo app</a>
  &nbsp;-&nbsp;
  <a href='https://react95.io/'>Website</a>
  &nbsp;-&nbsp;
  <a href='https://join.slack.com/t/react95/shared_invite/enQtOTA1NzEyNjAyNTc4LWYxZjU3NWRiMWJlMGJiMjhkNzE2MDA3ZmZjZDc1YmY0ODdlZjMwZDA1NWJiYWExYmY1NTJmNmE4OWVjNWFhMTE'>
    Slack
  </a>
  &nbsp;-&nbsp;
  <a href='https://www.paypal.me/react95'>PayPal donation 💰</a>
</h3>

**Refreshed** Windows 95 UI components for your modern React apps. Built with
[styled-components](https://github.com/styled-components/styled-components) 💅.

![hero](https://user-images.githubusercontent.com/28541613/81947711-28b05580-9601-11ea-964a-c3a6de998496.png)

### Getting Started

Check out our [getting started](?path=/story/docs-getting-started--page) docs!

### Motivation

Create 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.

### Support

- [Become a backer or sponsor on Patreon](https://www.patreon.com/arturbien)
- [One-time donation via PayPal](https://www.paypal.me/react95)


================================================
FILE: firebase.json
================================================
{
  "hosting": {
    "public": "storybook",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}


================================================
FILE: jest.config.js
================================================
module.exports = {
  globals: {
    'ts-jest': {
      diagnostics: false,
      isolatedModules: true
    }
  },
  coverageReporters: ['text', 'html'],
  preset: 'ts-jest/presets/default-esm',
  setupFilesAfterEnv: ['<rootDir>/test/setup-test.ts'],
  testEnvironment: 'jsdom'
};


================================================
FILE: package.json
================================================
{
  "name": "react95",
  "version": "0.0.0-development",
  "description": "Refreshed Windows95 UI components for modern web apps - React95",
  "keywords": [
    "react",
    "styled-components",
    "windows95",
    "components",
    "vaporwave"
  ],
  "author": "Artur Bień <artur.bien+react95@gmail.com> (https://www.linkedin.com/in/arturbien/)",
  "funding": [
    {
      "type": "paypal",
      "url": "https://www.paypal.me/react95"
    },
    {
      "type": "patreon",
      "url": "https://www.patreon.com/arturbien"
    }
  ],
  "license": "MIT",
  "repository": "git@github.com:arturbien/React95.git",
  "homepage": "https://react95.io",
  "main": "./dist/index.js",
  "module": "./dist/index.mjs",
  "types": "./dist/index.d.ts",
  "files": [
    "/dist"
  ],
  "publishConfig": {
    "access": "public"
  },
  "scripts": {
    "start": "cross-env STORYBOOK_DISPLAY_WARNING=true DISPLAY_WARNING=true start-storybook -p 9009 --no-open",
    "build:storybook": "cross-env STORYBOOK_DISPLAY_WARNING=true DISPLAY_WARNING=true build-storybook -o ./storybook",
    "build": "rm -rf dist && yarn run build:prod",
    "build:dev": "cross-env NODE_ENV=development rollup -c",
    "build:prod": "cross-env NODE_ENV=production rollup -c",
    "test": "jest ./src",
    "test:ci": "jest ./src --maxWorkers=2",
    "test:watch": "jest ./src --watch",
    "test:coverage": "jest ./src --coverage",
    "typescript": "tsc --noEmit",
    "lint": "eslint --ext .js,.ts,.tsx src",
    "lint:fix": "yarn run lint --fix",
    "semantic-release": "semantic-release",
    "cz": "git-cz"
  },
  "peerDependencies": {
    "react": ">= 16.8.0",
    "react-dom": ">= 16.8.0",
    "styled-components": ">= 5.3.3"
  },
  "devDependencies": {
    "@babel/core": "^7.18.9",
    "@babel/plugin-proposal-class-properties": "^7.18.6",
    "@babel/plugin-proposal-decorators": "^7.18.10",
    "@babel/plugin-proposal-export-default-from": "^7.18.10",
    "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
    "@babel/plugin-proposal-object-rest-spread": "^7.18.9",
    "@babel/plugin-proposal-optional-chaining": "^7.18.9",
    "@babel/plugin-proposal-private-methods": "^7.18.6",
    "@babel/plugin-proposal-private-property-in-object": "^7.18.6",
    "@babel/plugin-syntax-dynamic-import": "^7.8.3",
    "@babel/plugin-transform-arrow-functions": "^7.18.6",
    "@babel/plugin-transform-block-scoping": "^7.18.9",
    "@babel/plugin-transform-classes": "^7.18.9",
    "@babel/plugin-transform-destructuring": "^7.18.9",
    "@babel/plugin-transform-for-of": "^7.18.8",
    "@babel/plugin-transform-parameters": "^7.18.8",
    "@babel/plugin-transform-shorthand-properties": "^7.18.6",
    "@babel/plugin-transform-spread": "^7.18.9",
    "@babel/preset-env": "^7.18.10",
    "@babel/preset-typescript": "^7.18.6",
    "@rollup/plugin-typescript": "^8.3.4",
    "@storybook/addon-docs": "6.5.10",
    "@storybook/addon-storysource": "6.5.10",
    "@storybook/builder-webpack5": "^6.5.10",
    "@storybook/manager-webpack5": "^6.5.10",
    "@storybook/react": "6.5.10",
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^12.1.5",
    "@testing-library/react-hooks": "^8.0.1",
    "@types/jest": "^28.1.6",
    "@types/react": "^18.0.15",
    "@types/react-dom": "^18.0.6",
    "@types/styled-components": "^5.1.25",
    "@typescript-eslint/eslint-plugin": "^5.32.0",
    "@typescript-eslint/parser": "^5.32.0",
    "babel-plugin-macros": "^3.1.0",
    "babel-plugin-polyfill-corejs3": "^0.5.3",
    "commitizen": "^4.2.5",
    "cross-env": "^7.0.3",
    "cz-conventional-changelog": "^3.3.0",
    "esbuild": "^0.14.53",
    "eslint": "^8.21.0",
    "eslint-config-airbnb": "^19.0.4",
    "eslint-config-prettier": "^8.5.0",
    "eslint-import-resolver-typescript": "^3.4.0",
    "eslint-plugin-import": "^2.26.0",
    "eslint-plugin-jsx-a11y": "^6.6.1",
    "eslint-plugin-prettier": "^4.2.1",
    "eslint-plugin-react": "^7.30.1",
    "eslint-plugin-react-hooks": "^4.6.0",
    "firebase-tools": "^11.4.2",
    "husky": "^8.0.1",
    "jest": "^28.1.3",
    "jest-environment-jsdom": "^28.1.3",
    "jest-styled-components": "^7.0.8",
    "lint-staged": "^13.0.3",
    "prettier": "^2.7.1",
    "react": "^17.0.2",
    "react-docgen-typescript": "^2.2.2",
    "react-dom": "^17.0.2",
    "rimraf": "^3.0.2",
    "rollup": "^2.77.2",
    "rollup-plugin-copy": "^3.4.0",
    "rollup-plugin-esbuild": "^4.9.1",
    "rollup-plugin-replace": "^2.2.0",
    "semantic-release": "^19.0.3",
    "styled-components": "^5.3.5",
    "ts-jest": "^28.0.7",
    "typescript": "^4.7.4",
    "webpack": "5"
  },
  "dependencies": {},
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.js": [
      "eslint --fix",
      "prettier --write",
      "git add"
    ]
  },
  "config": {
    "commitizen": {
      "path": "./node_modules/cz-conventional-changelog"
    }
  }
}


================================================
FILE: rollup.config.js
================================================
import typescript from '@rollup/plugin-typescript';
import copy from 'rollup-plugin-copy';
import esbuild from 'rollup-plugin-esbuild';
import replace from 'rollup-plugin-replace';

const NODE_ENV = process.env.NODE_ENV || 'development';

const baseBundle = {
  external: id => !/^[./]/.test(id),
  plugins: [
    replace({
      'process.env.NODE_ENV': JSON.stringify(NODE_ENV)
    }),
    esbuild()
  ]
};

export default [
  {
    ...baseBundle,
    input: ['./src/index.ts', './src/types.ts'],
    output: [
      {
        dir: 'dist',
        entryFileNames: '[name].js',
        exports: 'auto',
        format: 'cjs',
        preserveModules: true,
        preserveModulesRoot: 'src'
      },
      {
        dir: 'dist',
        entryFileNames: '[name].mjs',
        exports: 'auto',
        format: 'es',
        preserveModules: true,
        preserveModulesRoot: 'src'
      }
    ],
    plugins: [
      ...baseBundle.plugins,
      typescript({
        tsconfig: './tsconfig.build.index.json',
        declaration: true,
        declarationDir: 'dist'
      })
    ]
  },
  {
    ...baseBundle,
    input: './src/common/themes/index.ts',
    output: {
      dir: 'dist/themes',
      exports: 'default',
      format: 'cjs',
      preserveModules: true,
      preserveModulesRoot: 'src/common/themes'
    },
    plugins: [
      ...baseBundle.plugins,
      copy({
        targets: [
          { src: './src/assets/fonts/dist/*', dest: './dist/fonts' },
          { src: './src/assets/images/*', dest: './dist/images' }
        ]
      }),
      typescript({
        tsconfig: './tsconfig.build.themes.json',
        declaration: true,
        declarationDir: 'dist/themes'
      })
    ]
  }
];


================================================
FILE: src/Anchor/Anchor.spec.tsx
================================================
import React from 'react';

import { render } from '@testing-library/react';

import { Anchor } from './Anchor';

const defaultProps = {
  children: '',
  href: ''
};

describe('<Anchor />', () => {
  it('should render href', () => {
    const { container } = render(
      <Anchor {...defaultProps} href='http://yoda.com' />
    );
    const anchorEl = container.firstChild;

    expect(anchorEl).toHaveAttribute('href', 'http://yoda.com');
  });

  it('should render children', () => {
    const { container } = render(
      <Anchor {...defaultProps}>You shall pass</Anchor>
    );
    const anchorEl = container.firstChild;

    expect(anchorEl).toHaveTextContent('You shall pass');
  });

  it('should render custom style', () => {
    const { container } = render(
      <Anchor {...defaultProps} style={{ color: 'papayawhip' }} />
    );
    const anchorEl = container.firstChild;

    expect(anchorEl).toHaveAttribute('style', 'color: papayawhip;');
  });

  it('should render custom props', () => {
    const customProps = { target: '_blank' };
    const { container } = render(<Anchor {...defaultProps} {...customProps} />);
    const anchorEl = container.firstChild;

    expect(anchorEl).toHaveAttribute('target', '_blank');
  });
});


================================================
FILE: src/Anchor/Anchor.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React from 'react';
import { Anchor } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.material};
`;

export default {
  title: 'Typography/Anchor',
  component: Anchor,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof Anchor>;

export function Default() {
  return (
    <h1>
      Everybody likes{' '}
      <Anchor href='https://expensive.toys' target='_blank'>
        https://expensive.toys
      </Anchor>
    </h1>
  );
}

Default.story = {
  name: 'default'
};


================================================
FILE: src/Anchor/Anchor.tsx
================================================
import React, { forwardRef } from 'react';
import styled from 'styled-components';

import { CommonStyledProps } from '../types';

type AnchorProps = {
  children: React.ReactNode;
  underline?: boolean;
} & React.AnchorHTMLAttributes<HTMLAnchorElement> &
  CommonStyledProps;

const StyledAnchor = styled.a<{ underline: boolean }>`
  color: ${({ theme }) => theme.anchor};
  font-size: inherit;
  text-decoration: ${({ underline }) => (underline ? 'underline' : 'none')};
  &:visited {
    color: ${({ theme }) => theme.anchorVisited};
  }
`;

const Anchor = forwardRef<HTMLAnchorElement, AnchorProps>(
  ({ children, underline = true, ...otherProps }: AnchorProps, ref) => {
    return (
      <StyledAnchor ref={ref} underline={underline} {...otherProps}>
        {children}
      </StyledAnchor>
    );
  }
);

Anchor.displayName = 'Anchor';

export { Anchor, AnchorProps };


================================================
FILE: src/AppBar/AppBar.spec.tsx
================================================
import { render } from '@testing-library/react';
import React from 'react';

import { AppBar } from './AppBar';

const defaultProps = { children: '' };

describe('<AppBar />', () => {
  it('should render header', () => {
    const { container } = render(<AppBar {...defaultProps} />);
    const headerEl = container.firstElementChild;

    expect(headerEl && headerEl.tagName).toBe('HEADER');
  });

  it('should render children', () => {
    const { container } = render(<AppBar>A nice app bar</AppBar>);
    const headerEl = container.firstElementChild;

    expect(headerEl).toHaveTextContent('A nice app bar');
  });

  it('should render fixed prop properly', () => {
    const { container, rerender } = render(<AppBar {...defaultProps} fixed />);
    const headerEl = container.firstElementChild;

    expect(headerEl).toHaveStyleRule('position', 'fixed');

    rerender(<AppBar {...defaultProps} fixed={false} />);

    expect(headerEl).toHaveStyleRule('position', 'absolute');
  });

  it('should render position prop properly', () => {
    const { container } = render(
      <AppBar {...defaultProps} position='sticky' />
    );
    const headerEl = container.firstElementChild;

    expect(headerEl).toHaveStyleRule('position', 'sticky');
  });

  it('should custom style', () => {
    const { container } = render(
      <AppBar {...defaultProps} style={{ backgroundColor: 'papayawhip' }} />
    );
    const headerEl = container.firstElementChild;

    expect(headerEl).toHaveAttribute('style', 'background-color: papayawhip;');
  });

  it('should render custom props', () => {
    const customProps = { title: 'cool-header' };
    const { container } = render(<AppBar {...defaultProps} {...customProps} />);
    const headerEl = container.firstElementChild;

    expect(headerEl).toHaveAttribute('title', 'cool-header');
  });
});


================================================
FILE: src/AppBar/AppBar.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React, { useState } from 'react';
import {
  AppBar,
  Button,
  MenuList,
  MenuListItem,
  Separator,
  TextInput,
  Toolbar
} from 'react95';
import styled from 'styled-components';
import logoIMG from '../assets/images/logo.png';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.desktopBackground};
`;

export default {
  title: 'Environment/AppBar',
  component: AppBar,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof AppBar>;

export function Default() {
  const [open, setOpen] = useState(false);

  return (
    <AppBar>
      <Toolbar style={{ justifyContent: 'space-between' }}>
        <div style={{ position: 'relative', display: 'inline-block' }}>
          <Button
            onClick={() => setOpen(!open)}
            active={open}
            style={{ fontWeight: 'bold' }}
          >
            <img
              src={logoIMG}
              alt='react95 logo'
              style={{ height: '20px', marginRight: 4 }}
            />
            Start
          </Button>
          {open && (
            <MenuList
              style={{
                position: 'absolute',
                left: '0',
                top: '100%'
              }}
              onClick={() => setOpen(false)}
            >
              <MenuListItem>
                <span role='img' aria-label='👨‍💻'>
                  👨‍💻
                </span>
                Profile
              </MenuListItem>
              <MenuListItem>
                <span role='img' aria-label='📁'>
                  📁
                </span>
                My account
              </MenuListItem>
              <Separator />
              <MenuListItem disabled>
                <span role='img' aria-label='🔙'>
                  🔙
                </span>
                Logout
              </MenuListItem>
            </MenuList>
          )}
        </div>

        <TextInput placeholder='Search...' width={150} />
      </Toolbar>
    </AppBar>
  );
}

Default.story = {
  name: 'default'
};


================================================
FILE: src/AppBar/AppBar.tsx
================================================
import React, { forwardRef } from 'react';
import styled, { CSSProperties } from 'styled-components';

import { createBorderStyles, createBoxStyles } from '../common';
import { CommonStyledProps } from '../types';

type AppBarProps = {
  children: React.ReactNode;
  /** @deprecated Use `position` instead */
  fixed?: boolean;
  position?: CSSProperties['position'];
} & React.HTMLAttributes<HTMLElement> &
  CommonStyledProps;

const StyledAppBar = styled.header<AppBarProps>`
  ${createBorderStyles()};
  ${createBoxStyles()};

  position: ${props => props.position ?? (props.fixed ? 'fixed' : 'absolute')};
  top: 0;
  right: 0;
  left: auto;
  display: flex;
  flex-direction: column;
  width: 100%;
`;

const AppBar = forwardRef<HTMLElement, AppBarProps>(
  ({ children, fixed = true, position = 'fixed', ...otherProps }, ref) => {
    return (
      <StyledAppBar
        fixed={fixed}
        position={fixed !== false ? position : undefined}
        ref={ref}
        {...otherProps}
      >
        {children}
      </StyledAppBar>
    );
  }
);

AppBar.displayName = 'AppBar';

export { AppBar, AppBarProps };


================================================
FILE: src/Avatar/Avatar.spec.tsx
================================================
import { render } from '@testing-library/react';
import React from 'react';

import { renderWithTheme, theme } from '../../test/utils';

import { Avatar } from './Avatar';

describe('<Avatar />', () => {
  it('should render component', () => {
    const { container } = render(<Avatar />);

    expect(container).toBeInTheDocument();
  });

  it('should render children', () => {
    const { container } = render(<Avatar>Avatar children</Avatar>);
    const avatarEl = container.firstElementChild;

    expect(avatarEl && avatarEl.innerHTML).toBe('Avatar children');
  });

  it('should handle border properly', () => {
    const { container, rerender } = renderWithTheme(
      <Avatar noBorder={false} />
    );
    const avatarEl = container.firstElementChild;

    expect(avatarEl).toHaveStyleRule(
      'border-top',
      `2px solid ${theme.borderDark}`
    );

    rerender(<Avatar noBorder />);

    expect(avatarEl).not.toHaveStyleRule('border-top', '');
  });

  it('should handle square properly', () => {
    const { container, rerender } = render(<Avatar square />);
    const avatarEl = container.firstElementChild;

    expect(avatarEl).toHaveStyleRule('border-radius', '0');

    rerender(<Avatar square={false} />);

    expect(avatarEl).toHaveStyleRule('border-radius', '50%');
  });

  it('should render with source', async () => {
    const catGif = 'https://cdn2.thecatapi.com/images/1ac.gif';
    const { findByAltText } = render(<Avatar src={catGif} alt='cat avatar' />);
    const imageEl = (await findByAltText('cat avatar')) as HTMLImageElement;

    expect(imageEl && imageEl.src).toBe(catGif);
  });

  it('should render source with priority over children', async () => {
    const catGif = 'https://cdn2.thecatapi.com/images/1ac.gif';
    const { queryByText } = render(
      <Avatar src={catGif} alt='cat avatar'>
        Cats are cool
      </Avatar>
    );
    const content = await queryByText(/cats are cool/i);

    expect(content).toBeNull();
  });

  describe('prop: size', () => {
    it('should set proper size', () => {
      const { container } = renderWithTheme(<Avatar size='85%' />);
      const avatarEl = container.firstElementChild;

      expect(avatarEl).toHaveStyleRule('width', '85%');
      expect(avatarEl).toHaveStyleRule('height', '85%');
    });

    it('when passed a number, sets size in px', () => {
      const { container } = renderWithTheme(<Avatar size={25} />);
      const avatarEl = container.firstElementChild;

      expect(avatarEl).toHaveStyleRule('width', '25px');
      expect(avatarEl).toHaveStyleRule('height', '25px');
    });
  });
});


================================================
FILE: src/Avatar/Avatar.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React from 'react';
import { Avatar } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.material};
  & > div > * {
    margin-right: 1rem;
  }
`;

export default {
  title: 'Other/Avatar',
  component: Avatar,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof Avatar>;

export function Default() {
  return (
    <div style={{ display: 'inline-flex' }}>
      <Avatar size={50} src='https://placekitten.com/100/100' />
      <Avatar noBorder size={50} src='https://placedog.net/100/100' />
      <Avatar size={50} style={{ background: 'palevioletred' }}>
        AK
      </Avatar>
      <Avatar square size={50}>
        <span role='img' aria-label='🚀'>
          🚀
        </span>
      </Avatar>
    </div>
  );
}

Default.story = {
  name: 'default'
};


================================================
FILE: src/Avatar/Avatar.tsx
================================================
import React, { forwardRef } from 'react';
import styled from 'styled-components';
import { getSize } from '../common/utils';
import { CommonStyledProps } from '../types';

type AvatarProps = {
  alt?: string;
  children?: React.ReactNode;
  noBorder?: boolean;
  size?: string | number;
  square?: boolean;
  src?: string;
} & React.HTMLAttributes<HTMLDivElement> &
  CommonStyledProps;

const StyledAvatar = styled.div<
  Pick<AvatarProps, 'noBorder' | 'square' | 'src'> & { size?: string }
>`
  display: inline-block;
  box-sizing: border-box;
  object-fit: contain;
  ${({ size }) =>
    `
    height: ${size};
    width: ${size};
    `}
  border-radius: ${({ square }) => (square ? 0 : '50%')};
  overflow: hidden;
  ${({ noBorder, theme }) =>
    !noBorder &&
    `
    border-top: 2px solid ${theme.borderDark};
    border-left: 2px solid ${theme.borderDark};
    border-bottom: 2px solid ${theme.borderLightest};
    border-right: 2px solid ${theme.borderLightest};
    background: ${theme.material};
  `}
  ${({ src }) =>
    !src &&
    `
    display: flex;
    align-items: center;
    justify-content: space-around;
    font-weight: bold;
    font-size: 1rem;
  `}
`;

const StyledAvatarImg = styled.img`
  display: block;
  object-fit: contain;
  width: 100%;
  height: 100%;
`;

const Avatar = forwardRef<HTMLDivElement, AvatarProps>(
  (
    {
      alt = '',
      children,
      noBorder = false,
      size = 35,
      square = false,
      src,
      ...otherProps
    },
    ref
  ) => {
    return (
      <StyledAvatar
        noBorder={noBorder}
        ref={ref}
        size={getSize(size)}
        square={square}
        src={src}
        {...otherProps}
      >
        {src ? <StyledAvatarImg src={src} alt={alt} /> : children}
      </StyledAvatar>
    );
  }
);

Avatar.displayName = 'Avatar';

export { Avatar, AvatarProps };


================================================
FILE: src/Button/Button.spec.tsx
================================================
import { fireEvent, render } from '@testing-library/react';
import React from 'react';

import { renderWithTheme, theme } from '../../test/utils';
import { blockSizes } from '../common/system';

import { Button } from './Button';

const defaultProps = {
  children: 'click me'
};

describe('<Button />', () => {
  it('should render button', () => {
    const { getByRole } = render(<Button {...defaultProps} />);
    const button = getByRole('button');

    expect(button).toBeInTheDocument();
    expect(button.tagName).toBe('BUTTON');
  });

  it('should handle different types', () => {
    const { getByRole } = render(<Button {...defaultProps} type='submit' />);
    const button = getByRole('button');

    expect(button).toHaveAttribute('type', 'submit');
  });

  it('should handle click properly', () => {
    const onButtonClick = jest.fn();
    const { getByRole } = render(
      <Button {...defaultProps} onClick={onButtonClick} />
    );
    const button = getByRole('button');

    fireEvent.click(button);

    expect(onButtonClick).toHaveBeenCalled();
  });

  it('should handle disabled for all variants', () => {
    const { getByRole, rerender } = renderWithTheme(
      <Button {...defaultProps} disabled />
    );
    const button = getByRole('button');
    const disabledTextShadow = `1px 1px ${theme.materialTextDisabledShadow}`;

    expect(button).toHaveStyleRule('color', theme.materialTextDisabled);
    expect(button).toHaveStyleRule('text-shadow', disabledTextShadow);

    rerender(<Button {...defaultProps} variant='menu' />);
    expect(button).toHaveStyleRule('color', theme.materialTextDisabled);
    expect(button).toHaveStyleRule('text-shadow', disabledTextShadow);

    rerender(<Button {...defaultProps} variant='flat' />);
    expect(button).toHaveStyleRule('color', theme.materialTextDisabled);
    expect(button).toHaveStyleRule('text-shadow', disabledTextShadow);

    rerender(<Button {...defaultProps} variant='thin' />);
    expect(button).toHaveStyleRule('color', theme.materialTextDisabled);
    expect(button).toHaveStyleRule('text-shadow', disabledTextShadow);
  });

  it('should handle fullWidth prop', () => {
    const { getByRole, rerender } = render(
      <Button {...defaultProps} fullWidth />
    );
    const button = getByRole('button');

    expect(button).toHaveStyleRule('width', '100%');

    rerender(<Button {...defaultProps} fullWidth={false} />);

    expect(button).toHaveStyleRule('width', 'auto');
  });

  it('should handle button sizes properly', () => {
    const { getByRole, rerender } = render(
      <Button {...defaultProps} size='sm' />
    );
    const button = getByRole('button');

    expect(button).toHaveStyleRule('height', blockSizes.sm);

    rerender(<Button {...defaultProps} size='lg' />);

    expect(button).toHaveStyleRule('height', blockSizes.lg);
  });

  it('should handle square prop', () => {
    const { getByRole } = render(<Button {...defaultProps} square size='md' />);
    const button = getByRole('button');

    expect(button).toHaveStyleRule('padding', '0');
    expect(button).toHaveStyleRule('width', blockSizes.md);
  });

  it('should render children', () => {
    const { getByRole } = render(<Button {...defaultProps} />);
    const button = getByRole('button');

    expect(button.innerHTML).toBe('click me');
  });

  describe('prop: disabled', () => {
    it('should render disabled', () => {
      const { getByRole } = render(<Button {...defaultProps} disabled />);
      const button = getByRole('button');

      expect(button).toHaveAttribute('disabled');
    });
    it('should not fire click when disabled', () => {
      const onButtonClick = jest.fn();
      const { getByRole } = render(<Button {...defaultProps} disabled />);
      const button = getByRole('button');

      fireEvent.click(button);

      expect(onButtonClick).not.toHaveBeenCalled();
    });
  });
});


================================================
FILE: src/Button/Button.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React, { useState } from 'react';
import {
  Button,
  MenuList,
  MenuListItem,
  ScrollView,
  Separator,
  Toolbar,
  Window,
  WindowContent,
  WindowHeader
} from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.material};
  #default-buttons button {
    margin-bottom: 1rem;
    margin-right: 1rem;
  }

  #cutout {
    background: ${({ theme }) => theme.canvas};
    padding: 1rem;
    width: 300px;
  }
`;

export default {
  title: 'Controls/Button',
  component: Button,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof Button>;

export function Default() {
  return (
    <div id='default-buttons'>
      <Button>Default</Button>
      <br />
      <Button primary>Primary</Button>
      <br />
      <Button disabled>Disabled</Button>
      <br />
      <Button active>Active</Button>
      <br />
      <Button square>
        <span role='img' aria-label='recycle'>
          ♻︎
        </span>
      </Button>
      <br />
      <Button fullWidth>Full width</Button>
      <br />
      <Button size='sm'>Size small</Button>
      <Button size='lg'>Size large</Button>
    </div>
  );
}

Default.story = {
  name: 'default'
};

export function Raised() {
  return (
    <div id='default-buttons'>
      <Button variant='raised'>Default</Button>
      <br />
      <Button variant='raised' primary>
        Primary
      </Button>
      <br />
      <Button variant='raised' disabled>
        Disabled
      </Button>
      <br />
      <Button variant='raised' active>
        Active
      </Button>
      <br />
      <Button variant='raised' square>
        <span role='img' aria-label='recycle'>
          ♻︎
        </span>
      </Button>
      <br />
      <Button variant='raised' fullWidth>
        Full width
      </Button>
      <br />
      <Button variant='raised' size='sm'>
        Size small
      </Button>
      <Button variant='raised' size='lg'>
        Size large
      </Button>
    </div>
  );
}

Raised.story = {
  name: 'raised'
};

export function Flat() {
  return (
    <Window>
      <WindowContent>
        <ScrollView id='cutout'>
          <p style={{ lineHeight: 1.3 }}>
            When you want to use Buttons on a light background (like scrollable
            content), just use the flat variant:
          </p>
          <div
            style={{
              marginTop: '1.5rem'
            }}
          >
            <Toolbar>
              <Button variant='flat' primary style={{ marginRight: '0.5rem' }}>
                Primary
              </Button>
              <Button variant='flat' style={{ marginRight: '0.5rem' }}>
                Regular
              </Button>
              <Button variant='flat' disabled>
                Disabled
              </Button>
            </Toolbar>
          </div>
        </ScrollView>
      </WindowContent>
    </Window>
  );
}

Flat.story = {
  name: 'flat'
};

const imageSrc =
  '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';
export function Thin() {
  const [open, setOpen] = useState(false);

  return (
    <Window style={{ maxWidth: '250px' }}>
      <WindowHeader>
        <span role='img' aria-label='Kiwi'>
          🥝
        </span>
        Kiwi.app
      </WindowHeader>
      <Toolbar noPadding>
        <Button variant='thin' disabled>
          Upload
        </Button>
        <Button variant='thin' disabled>
          Save
        </Button>
        <div
          style={{
            position: 'relative',
            display: 'inline-block',
            alignSelf: 'left'
          }}
        >
          <Button
            variant='thin'
            onClick={() => setOpen(!open)}
            size='sm'
            active={open}
          >
            Share
          </Button>
          {open && (
            <MenuList
              style={{
                position: 'absolute',
                right: '0',
                top: '100%',
                zIndex: '9999'
              }}
              onClick={() => setOpen(false)}
            >
              <MenuListItem size='sm'>Copy link</MenuListItem>
              <Separator />
              <MenuListItem size='sm'>Facebook</MenuListItem>
              <MenuListItem size='sm'>Twitter</MenuListItem>
              <MenuListItem size='sm'>Instagram</MenuListItem>
              <Separator />
              <MenuListItem size='sm' disabled>
                MySpace
              </MenuListItem>
            </MenuList>
          )}
        </div>
      </Toolbar>
      <WindowContent style={{ padding: '0.25rem' }}>
        <ScrollView>
          <img
            style={{ width: '100%', height: '1uto', display: 'block' }}
            src={imageSrc}
            alt='kiwi'
          />
        </ScrollView>
      </WindowContent>
    </Window>
  );
}

Thin.story = {
  name: 'thin'
};


================================================
FILE: src/Button/Button.tsx
================================================
import React, { forwardRef } from 'react';
import styled, { css } from 'styled-components';
import {
  createBorderStyles,
  createBoxStyles,
  createDisabledTextStyles,
  createFlatBoxStyles,
  createHatchedBackground,
  focusOutline
} from '../common';
import { blockSizes } from '../common/system';
import { noOp } from '../common/utils';
import { CommonStyledProps, Sizes } from '../types';

type ButtonProps = {
  active?: boolean;
  children?: React.ReactNode;
  disabled?: boolean;
  fullWidth?: boolean;
  onClick?: React.ButtonHTMLAttributes<HTMLButtonElement>['onClick'];
  onTouchStart?: React.ButtonHTMLAttributes<HTMLButtonElement>['onTouchStart'];
  primary?: boolean;
  size?: Sizes;
  square?: boolean;
  type?: string;
} & (
  | {
      variant?: 'default' | 'raised' | 'flat' | 'thin';
    }
  | {
      /** @deprecated Use `thin` */
      variant?: 'menu';
    }
) &
  Omit<
    React.ButtonHTMLAttributes<HTMLButtonElement>,
    'disabled' | 'onClick' | 'onTouchStart' | 'type'
  > &
  CommonStyledProps;

type StyledButtonProps = Pick<
  ButtonProps,
  | 'active'
  | 'disabled'
  | 'fullWidth'
  | 'primary'
  | 'size'
  | 'square'
  | 'variant'
>;

const commonButtonStyles = css<StyledButtonProps>`
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: ${({ size = 'md' }) => blockSizes[size]};
  width: ${({ fullWidth, size = 'md', square }) =>
    fullWidth ? '100%' : square ? blockSizes[size] : 'auto'};
  padding: ${({ square }) => (square ? 0 : `0 10px`)};
  font-size: 1rem;
  user-select: none;
  &:active {
    padding-top: ${({ disabled }) => !disabled && '2px'};
  }
  padding-top: ${({ active, disabled }) => active && !disabled && '2px'};
  &:after {
    content: '';
    position: absolute;
    display: block;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
  }
  &:not(:disabled) {
    cursor: pointer;
  }
  font-family: inherit;
`;

export const StyledButton = styled.button<StyledButtonProps>`
  ${({ active, disabled, primary, theme, variant }) =>
    variant === 'flat'
      ? css`
          ${createFlatBoxStyles()}
          ${primary
            ? `
          border: 2px solid ${theme.checkmark};
            outline: 2px solid ${theme.flatDark};
            outline-offset: -4px;
          `
            : `
          border: 2px solid ${theme.flatDark};
            outline: 2px solid transparent;
            outline-offset: -4px;
          `}
          &:focus:after, &:active:after {
            ${!active && !disabled && focusOutline}
            outline-offset: -4px;
          }
        `
      : variant === 'menu' || variant === 'thin'
      ? css`
          ${createBoxStyles()};
          border: 2px solid transparent;
          &:hover,
          &:focus {
            ${!disabled &&
            !active &&
            createBorderStyles({ style: 'buttonThin' })}
          }
          &:active {
            ${!disabled && createBorderStyles({ style: 'buttonThinPressed' })}
          }
          ${active && createBorderStyles({ style: 'buttonThinPressed' })}
          ${disabled && createDisabledTextStyles()}
        `
      : css`
          ${createBoxStyles()};
          border: none;
          ${disabled && createDisabledTextStyles()}
          ${active
            ? createHatchedBackground({
                mainColor: theme.material,
                secondaryColor: theme.borderLightest
              })
            : ''}
          &:before {
            box-sizing: border-box;
            content: '';
            position: absolute;
            ${primary
              ? css`
                  left: 2px;
                  top: 2px;
                  width: calc(100% - 4px);
                  height: calc(100% - 4px);
                  outline: 2px solid ${theme.borderDarkest};
                `
              : css`
                  left: 0;
                  top: 0;
                  width: 100%;
                  height: 100%;
                `}

            ${active
              ? createBorderStyles({
                  style: variant === 'raised' ? 'window' : 'button',
                  invert: true
                })
              : createBorderStyles({
                  style: variant === 'raised' ? 'window' : 'button',
                  invert: false
                })}
          }
          &:active:before {
            ${!disabled &&
            createBorderStyles({
              style: variant === 'raised' ? 'window' : 'button',
              invert: true
            })}
          }
          &:focus:after,
          &:active:after {
            ${!disabled && focusOutline}
            outline-offset: -8px;
          }
          &:active:focus:after,
          &:active:after {
            top: ${active ? '0' : '1px'};
          }
        `}
  ${commonButtonStyles}
`;

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      onClick,
      disabled = false,
      children,
      type = 'button',
      fullWidth = false,
      size = 'md',
      square = false,
      active = false,
      onTouchStart = noOp,
      primary = false,
      variant = 'default',
      ...otherProps
    },
    ref
  ) => {
    return (
      <StyledButton
        active={active}
        disabled={disabled}
        $disabled={disabled}
        fullWidth={fullWidth}
        onClick={disabled ? undefined : onClick}
        onTouchStart={onTouchStart}
        primary={primary}
        ref={ref}
        size={size}
        square={square}
        type={type}
        variant={variant}
        {...otherProps}
      >
        {children}
      </StyledButton>
    );
  }
);

Button.displayName = 'Button';

export { Button, ButtonProps };


================================================
FILE: src/Checkbox/Checkbox.spec.tsx
================================================
import React from 'react';

import { renderWithTheme } from '../../test/utils';
import { Checkbox } from './Checkbox';

describe('<Checkbox />', () => {
  describe('label', () => {
    it('renders', () => {
      const labelText = 'Swag';
      const { getByLabelText } = renderWithTheme(
        <Checkbox label={labelText} />
      );
      expect(getByLabelText(labelText)).toBeInTheDocument();
    });
  });

  describe('prop: onChange', () => {
    it('should call onChange when uncontrolled', () => {
      const handleChange = jest.fn(event => event.target.checked);
      const { getByRole } = renderWithTheme(
        <Checkbox onChange={handleChange} />
      );

      getByRole('checkbox').click();

      expect(handleChange).toHaveBeenCalledTimes(1);
      // event.target.check is true
      expect(handleChange.mock.results[0].value).toBe(true);
    });

    it('should call onChange when controlled', () => {
      const checked = true;
      const handleChange = jest.fn(event => event.target.checked);

      const { getByRole } = renderWithTheme(
        <Checkbox onChange={handleChange} checked={checked} />
      );

      getByRole('checkbox').click();

      expect(handleChange).toHaveBeenCalledTimes(1);

      expect(handleChange.mock.results[0].value).toBe(!checked);
    });
  });

  describe('prop: disabled', () => {
    it('should disable checkbox', () => {
      const handleChange = jest.fn();

      const { getByRole } = renderWithTheme(
        <Checkbox disabled onChange={handleChange} />
      );
      const checkbox = getByRole('checkbox');
      expect(checkbox).toHaveAttribute('disabled');

      checkbox.click();
      expect(handleChange).not.toHaveBeenCalled();
    });
    it('should be overridden by props', () => {
      const { getByRole, rerender } = renderWithTheme(<Checkbox disabled />);
      rerender(<Checkbox disabled={false} />);
      const checkbox = getByRole('checkbox');
      expect(checkbox).not.toHaveAttribute('disabled');
    });
  });
  describe('prop: indeterminate', () => {
    it('renders indeterminate state', () => {
      const { getByRole } = renderWithTheme(<Checkbox indeterminate />);
      const checkbox = getByRole('checkbox');

      // don't set native 'indeterminate' attribute because
      // different browsers treat it differently
      // instead we're setting 'data-indeterminate' attribute
      expect(checkbox).toHaveAttribute('data-indeterminate');
      expect(checkbox).not.toHaveAttribute('indeterminate');

      expect(getByRole('presentation').firstChild).toHaveAttribute(
        'data-testid',
        'indeterminateIcon'
      );
    });
    it('replaces checked icon', () => {
      const { getByRole, rerender } = renderWithTheme(<Checkbox checked />);

      expect(getByRole('checkbox')).toHaveAttribute(
        'data-indeterminate',
        'false'
      );

      rerender(<Checkbox checked indeterminate />);

      expect(getByRole('checkbox')).toHaveAttribute(
        'data-indeterminate',
        'true'
      );
      expect(getByRole('presentation').firstChild).toHaveAttribute(
        'data-testid',
        'indeterminateIcon'
      );
    });
  });
  describe('controlled', () => {
    it('should check the checkbox', () => {
      const { getByRole, rerender } = renderWithTheme(
        <Checkbox checked={false} />
      );

      rerender(<Checkbox checked />);
      const checkbox = getByRole('checkbox') as HTMLInputElement;

      expect(checkbox.checked).toBe(true);
      expect(getByRole('checkbox')).toHaveAttribute('checked');
      expect(getByRole('presentation').firstChild).toHaveAttribute(
        'data-testid',
        'checkmarkIcon'
      );
    });

    it('should uncheck the checkbox', () => {
      const { getByRole, rerender } = renderWithTheme(<Checkbox checked />);
      rerender(<Checkbox checked={false} />);
      const checkbox = getByRole('checkbox') as HTMLInputElement;

      expect(checkbox.checked).toBe(false);
      expect(getByRole('checkbox')).not.toHaveAttribute('checked');
      expect(getByRole('presentation').firstChild).toBeNull();
      //   check if proper icon was rendered
    });
  });

  describe('uncontrolled', () => {
    it('can change checked state uncontrolled starting from defaultChecked', () => {
      const { getByRole } = renderWithTheme(<Checkbox defaultChecked />);
      const checkbox = getByRole('checkbox') as HTMLInputElement;

      expect(checkbox.checked).toBe(true);

      checkbox.click();

      expect(checkbox.checked).toBe(false);

      checkbox.click();

      expect(checkbox.checked).toBe(true);
    });
  });
});


================================================
FILE: src/Checkbox/Checkbox.stories.tsx
================================================
import React, { useState } from 'react';
import styled from 'styled-components';

import { ComponentMeta } from '@storybook/react';
import { Checkbox, GroupBox, ScrollView } from 'react95';

const Wrapper = styled.div`
  background: ${({ theme }) => theme.material};
  padding: 5rem;

  #cutout {
    background: ${({ theme }) => theme.canvas};
    padding: 1rem;
    width: 250px;
    display: flex;
    align-items: center;
    justify-content: space-around;
  }
`;

export default {
  title: 'Controls/Checkbox',
  component: Checkbox,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof Checkbox>;

export function Default() {
  const [state, setState] = useState({
    cheese: true,
    bacon: false,
    broccoli: false
  });

  const { cheese, bacon, broccoli } = state;
  const ingredientsArr = Object.values(state).map(val => (val ? 1 : 0));
  const possibleIngredients = Object.keys(state).length;
  const chosenIngredients = ingredientsArr.reduce<number>((a, b) => a + b, 0);

  const isIndeterminate = ![0, possibleIngredients].includes(chosenIngredients);

  const toggleAll = () => {
    console.log(ingredientsArr);
    if (isIndeterminate) {
      setState({
        cheese: true,
        bacon: true,
        broccoli: true
      });
    } else if (ingredientsArr[0] === 1) {
      setState({
        cheese: false,
        bacon: false,
        broccoli: false
      });
    } else {
      setState({
        cheese: true,
        bacon: true,
        broccoli: true
      });
    }
  };

  const toggleIngredient = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value as 'cheese' | 'bacon' | 'broccoli';
    setState({
      ...state,
      [value]: !state[value]
    });
  };

  return (
    <div style={{ maxWidth: '250px' }}>
      <GroupBox label='Pizza toppings'>
        <Checkbox
          name='allToppings'
          label='All'
          value='allToppings'
          indeterminate={isIndeterminate}
          checked={
            !isIndeterminate && chosenIngredients === possibleIngredients
          }
          onChange={toggleAll}
        />
        <div style={{ paddingLeft: '1.5rem' }}>
          <Checkbox
            checked={!!cheese}
            onChange={toggleIngredient}
            value='cheese'
            label='🧀 Extra cheese'
            name='ingredients'
          />
          <br />
          <Checkbox
            checked={!!bacon}
            onChange={toggleIngredient}
            value='bacon'
            label='🥓 Bacon'
            name='ingredients'
          />
          <br />
          <Checkbox
            checked={!!broccoli}
            onChange={toggleIngredient}
            value='broccoli'
            label='🥦 Broccoli'
            name='ingredients'
          />
        </div>
      </GroupBox>
      <Checkbox
        name='shipping'
        value='shipping'
        label='Free shipping'
        defaultChecked
        disabled
        style={{ marginTop: '1rem' }}
      />
    </div>
  );
}

Default.story = {
  name: 'default'
};

export function Flat() {
  const [state, setState] = useState({
    cheese: true,
    bacon: false,
    broccoli: false
  });

  const { cheese, bacon, broccoli } = state;
  const ingredientsArr = Object.values(state).map(val => (val ? 1 : 0));
  const possibleIngredients = Object.keys(state).length;
  const chosenIngredients = ingredientsArr.reduce<number>((a, b) => a + b, 0);

  const isIndeterminate = ![0, possibleIngredients].includes(chosenIngredients);

  const toggleAll = () => {
    console.log(ingredientsArr);
    if (isIndeterminate) {
      setState({
        cheese: true,
        bacon: true,
        broccoli: true
      });
    } else if (ingredientsArr[0] === 1) {
      setState({
        cheese: false,
        bacon: false,
        broccoli: false
      });
    } else {
      setState({
        cheese: true,
        bacon: true,
        broccoli: true
      });
    }
  };

  const toggleIngredient = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value as 'cheese' | 'bacon' | 'broccoli';
    setState({
      ...state,
      [value]: !state[value]
    });
  };

  return (
    <ScrollView id='cutout'>
      <div style={{ maxWidth: '250px' }}>
        <GroupBox variant='flat' label='Pizza toppings'>
          <Checkbox
            variant='flat'
            name='allToppings'
            label='All'
            value='allToppings'
            indeterminate={isIndeterminate}
            checked={
              !isIndeterminate && chosenIngredients === possibleIngredients
            }
            onChange={toggleAll}
          />
          <div style={{ paddingLeft: '1.5rem' }}>
            <Checkbox
              variant='flat'
              checked={!!cheese}
              onChange={toggleIngredient}
              value='cheese'
              label='🧀 Extra cheese'
              name='ingredients'
            />
            <br />
            <Checkbox
              variant='flat'
              checked={!!bacon}
              onChange={toggleIngredient}
              value='bacon'
              label='🥓 Bacon'
              name='ingredients'
            />
            <br />
            <Checkbox
              variant='flat'
              checked={!!broccoli}
              onChange={toggleIngredient}
              value='broccoli'
              label='🥦 Broccoli'
              name='ingredients'
            />
          </div>
        </GroupBox>
        <Checkbox
          variant='flat'
          name='shipping'
          value='shipping'
          label='Free shipping'
          defaultChecked
          disabled
          style={{ marginTop: '1rem' }}
        />
      </div>
    </ScrollView>
  );
}

Flat.story = {
  name: 'flat'
};


================================================
FILE: src/Checkbox/Checkbox.tsx
================================================
import React, { forwardRef, useCallback } from 'react';
import styled, { css } from 'styled-components';

import { createHatchedBackground } from '../common';
import useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';
import {
  LabelText,
  size,
  StyledInput,
  StyledLabel
} from '../common/SwitchBase';
import { noOp } from '../common/utils';
import { StyledScrollView } from '../ScrollView/ScrollView';
import { CommonThemeProps } from '../types';

type CheckboxProps = {
  checked?: boolean;
  className?: string;
  defaultChecked?: boolean;
  disabled?: boolean;
  indeterminate?: boolean;
  label?: number | string;
  name?: string;
  onChange?: React.ChangeEventHandler<HTMLInputElement>;
  style?: React.CSSProperties;
  value?: number | string;
  variant?: 'default' | 'flat';
} & Omit<
  React.InputHTMLAttributes<HTMLInputElement>,
  | 'checked'
  | 'className'
  | 'defaultChecked'
  | 'disabled'
  | 'label'
  | 'name'
  | 'onChange'
  | 'style'
  | 'value'
>;

type CheckmarkProps = {
  $disabled: boolean;
  variant: 'default' | 'flat';
};

const sharedCheckboxStyles = css`
  width: ${size}px;
  height: ${size}px;
  display: flex;
  align-items: center;
  justify-content: space-around;
  margin-right: 0.5rem;
`;
const StyledCheckbox = styled(StyledScrollView)<CommonThemeProps>`
  ${sharedCheckboxStyles}
  width: ${size}px;
  height: ${size}px;
  background: ${({ $disabled, theme }) =>
    $disabled ? theme.material : theme.canvas};
  &:before {
    box-shadow: none;
  }
`;
const StyledFlatCheckbox = styled.div<CommonThemeProps>`
  position: relative;
  box-sizing: border-box;
  display: inline-block;
  background: ${({ $disabled, theme }) =>
    $disabled ? theme.flatLight : theme.canvas};
  ${sharedCheckboxStyles}
  width: ${size - 4}px;
  height: ${size - 4}px;
  outline: none;
  border: 2px solid ${({ theme }) => theme.flatDark};
  background: ${({ $disabled, theme }) =>
    $disabled ? theme.flatLight : theme.canvas};
`;

const CheckmarkIcon = styled.span.attrs(() => ({
  'data-testid': 'checkmarkIcon'
}))<CheckmarkProps>`
  display: inline-block;
  position: relative;
  width: 100%;
  height: 100%;
  &:after {
    content: '';
    display: block;
    position: absolute;
    left: 50%;
    top: calc(50% - 1px);
    width: 3px;
    height: 7px;

    border: solid
      ${({ $disabled, theme }) =>
        $disabled ? theme.checkmarkDisabled : theme.checkmark};
    border-width: 0 3px 3px 0;
    transform: translate(-50%, -50%) rotate(45deg);

    border-color: ${p =>
      p.$disabled ? p.theme.checkmarkDisabled : p.theme.checkmark};
  }
`;
const IndeterminateIcon = styled.span.attrs(() => ({
  'data-testid': 'indeterminateIcon'
}))<CheckmarkProps>`
  display: inline-block;
  position: relative;

  width: 100%;
  height: 100%;

  &:after {
    content: '';
    display: block;

    width: 100%;
    height: 100%;

    ${({ $disabled, theme }) =>
      createHatchedBackground({
        mainColor: $disabled ? theme.checkmarkDisabled : theme.checkmark
      })}
    background-position: 0px 0px, 2px 2px;
  }
`;

const CheckboxComponents = {
  flat: StyledFlatCheckbox,
  default: StyledCheckbox
};

const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
  (
    {
      checked,
      className = '',
      defaultChecked = false,
      disabled = false,
      indeterminate = false,
      label = '',
      onChange = noOp,
      style = {},
      value,
      variant = 'default',
      ...otherProps
    },
    ref
  ) => {
    const [state, setState] = useControlledOrUncontrolled({
      defaultValue: defaultChecked,
      onChange,
      readOnly: otherProps.readOnly ?? disabled,
      value: checked
    });

    const handleChange = useCallback(
      (e: React.ChangeEvent<HTMLInputElement>) => {
        const newState = e.target.checked;
        setState(newState);
        onChange(e);
      },
      [onChange, setState]
    );

    const CheckboxComponent = CheckboxComponents[variant];

    let Icon = null;
    if (indeterminate) {
      Icon = IndeterminateIcon;
    } else if (state) {
      Icon = CheckmarkIcon;
    }

    return (
      <StyledLabel $disabled={disabled} className={className} style={style}>
        <StyledInput
          disabled={disabled}
          onChange={disabled ? undefined : handleChange}
          readOnly={disabled}
          type='checkbox'
          value={value}
          checked={state}
          data-indeterminate={indeterminate}
          ref={ref}
          {...otherProps}
        />
        <CheckboxComponent $disabled={disabled} role='presentation'>
          {Icon && <Icon $disabled={disabled} variant={variant} />}
        </CheckboxComponent>
        {label && <LabelText>{label}</LabelText>}
      </StyledLabel>
    );
  }
);

Checkbox.displayName = 'Checkbox';

export { Checkbox, CheckboxProps };


================================================
FILE: src/ColorInput/ColorInput.spec.tsx
================================================
import { fireEvent } from '@testing-library/react';
import React from 'react';
import { renderWithTheme } from '../../test/utils';
import { ColorInput } from './ColorInput';

function rgb2hex(str: string) {
  const rgb = str.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
  function hex(x: string) {
    return `0${parseInt(x, 10).toString(16)}`.slice(-2);
  }
  return rgb ? `#${hex(rgb[1])}${hex(rgb[2])}${hex(rgb[3])}` : '';
}

describe('<ColorInput />', () => {
  it('should call handlers', () => {
    const color = '#f0f0dd';
    const onChange = jest.fn();
    const { container } = renderWithTheme(<ColorInput onChange={onChange} />);
    const input = container.querySelector(`[type="color"]`) as HTMLInputElement;
    fireEvent.change(input, { target: { value: color } });
    expect(onChange).toBeCalledTimes(1);
  });

  it('should properly pass value to input element', () => {
    const color = '#f0f0dd';
    const { container } = renderWithTheme(<ColorInput value={color} />);
    const input = container.querySelector(`[type="color"]`) as HTMLInputElement;

    expect(input.value).toBe(color);
  });
  it('should display current color', () => {
    const color = '#f0f0dd';
    const { getByRole } = renderWithTheme(<ColorInput value={color} />);
    const colorPreview = getByRole('presentation');
    const displayedColor = window.getComputedStyle(
      colorPreview,
      null
    ).background;

    const displayedColorHex = rgb2hex(displayedColor);
    expect(displayedColorHex).toBe(color);
  });

  describe('prop: disabled', () => {
    it('should render disabled input', () => {
      const { container } = renderWithTheme(<ColorInput disabled />);
      const input = container.querySelector(`[type="color"]`);

      expect(input).toHaveAttribute('disabled');
    });

    it('should be overridden by props', () => {
      const { container, rerender } = renderWithTheme(<ColorInput disabled />);
      rerender(<ColorInput disabled={false} />);
      const input = container.querySelector(`[type="color"]`);

      expect(input).not.toHaveAttribute('disabled');
    });
  });
});


================================================
FILE: src/ColorInput/ColorInput.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React from 'react';
import styled from 'styled-components';

import { ColorInput, ScrollView } from 'react95';

const Wrapper = styled.div`
  background: ${({ theme }) => theme.material};
  padding: 5rem;
  & > span {
    margin-left: 1rem;
    margin-right: 0.5rem;
  }
  #cutout {
    background: ${({ theme }) => theme.canvas};
    display: inline-block;
  }
  .content {
    padding: 1rem;
    & > div {
      margin: 1rem 0;
    }

    & > div > span {
      margin-right: 0.5rem;
    }
  }
`;

export default {
  title: 'Controls/ColorInput',
  component: ColorInput,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof ColorInput>;

export function Default() {
  return (
    <>
      <span>enabled: </span>
      <ColorInput defaultValue='#00f' />
      <span>disabled: </span>
      <ColorInput disabled defaultValue='#00f' />
    </>
  );
}

Default.story = {
  name: 'default'
};

export function Flat() {
  return (
    <ScrollView id='cutout'>
      <div className='content'>
        <div>
          <span>enabled: </span>
          <ColorInput variant='flat' defaultValue='#00f' />
        </div>
        <div>
          <span>disabled: </span>
          <ColorInput variant='flat' disabled defaultValue='#00f' />
        </div>
      </div>
    </ScrollView>
  );
}

Flat.story = {
  name: 'flat'
};


================================================
FILE: src/ColorInput/ColorInput.tsx
================================================
import React, { forwardRef } from 'react';
import styled, { css } from 'styled-components';
import { StyledButton } from '../Button/Button';
import { focusOutline } from '../common';
import useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';
import { noOp } from '../common/utils';
import { Separator } from '../Separator/Separator';
import { CommonStyledProps } from '../types';

type ColorInputProps = {
  defaultValue?: string;
  disabled?: boolean;
  onChange?: React.ChangeEventHandler<HTMLInputElement>;
  value?: string;
  variant?: 'default' | 'flat';
} & Omit<
  React.InputHTMLAttributes<HTMLInputElement>,
  'defaultValue' | 'disabled' | 'onChange' | 'value'
> &
  CommonStyledProps;

const Trigger = styled(StyledButton)`
  padding-left: 8px;
`;

const StyledSeparator = styled(Separator)`
  height: 21px;
  position: relative;
  top: 0;
`;

export const StyledColorInput = styled.input`
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  z-index: 1;
  cursor: pointer;
  &:disabled {
    cursor: default;
  }
`;

// TODO replace with SVG icon
const ColorPreview = styled.div<{
  color: string;
  $disabled: boolean;
}>`
  box-sizing: border-box;
  height: 19px;
  display: inline-block;
  width: 35px;
  margin-right: 5px;

  background: ${({ color }) => color};

  ${({ $disabled }) =>
    $disabled
      ? css`
          border: 2px solid ${({ theme }) => theme.materialTextDisabled};
          filter: drop-shadow(
            1px 1px 0px ${({ theme }) => theme.materialTextDisabledShadow}
          );
        `
      : css`
          border: 2px solid ${({ theme }) => theme.materialText};
        `}
  ${StyledColorInput}:focus:not(:active) + &:after {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    ${focusOutline}
    outline-offset: -8px;
  }
`;

const ChevronIcon = styled.span<
  Required<Pick<ColorInputProps, 'variant'>> & {
    $disabled: boolean;
  }
>`
  width: 0px;
  height: 0px;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  display: inline-block;
  margin-left: 6px;

  ${({ $disabled }) =>
    $disabled
      ? css`
          border-top: 6px solid ${({ theme }) => theme.materialTextDisabled};
          filter: drop-shadow(
            1px 1px 0px ${({ theme }) => theme.materialTextDisabledShadow}
          );
        `
      : css`
          border-top: 6px solid ${({ theme }) => theme.materialText};
        `}
  &:after {
    content: '';
    box-sizing: border-box;
    position: absolute;
    top: ${({ variant }) => (variant === 'flat' ? '6px' : '8px')};
    right: 8px;
    width: 16px;
    height: 19px;
  }
`;

// TODO make sure all aria and role attributes are in place
const ColorInput = forwardRef<HTMLInputElement, ColorInputProps>(
  (
    {
      value,
      defaultValue,
      onChange = noOp,
      disabled = false,
      variant = 'default',
      ...otherProps
    },
    ref
  ) => {
    const [valueDerived, setValueState] = useControlledOrUncontrolled({
      defaultValue,
      onChange,
      readOnly: otherProps.readOnly ?? disabled,
      value
    });

    const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
      const color = e.target.value;
      setValueState(color);
      onChange(e);
    };

    return (
      //  we only need button styles, so we display
      // it as a div and reset type attribute
      <Trigger disabled={disabled} as='div' variant={variant} size='md'>
        <StyledColorInput
          onChange={handleChange}
          readOnly={disabled}
          disabled={disabled}
          value={valueDerived ?? '#008080'}
          type='color'
          ref={ref}
          {...otherProps}
        />
        <ColorPreview
          $disabled={disabled}
          color={valueDerived ?? '#008080'}
          role='presentation'
        />
        {variant === 'default' && <StyledSeparator orientation='vertical' />}
        <ChevronIcon $disabled={disabled} variant={variant} />
      </Trigger>
    );
  }
);

ColorInput.displayName = 'ColorInput';

export { ColorInput, ColorInputProps };


================================================
FILE: src/Counter/Counter.spec.tsx
================================================
import React from 'react';

import { renderWithTheme } from '../../test/utils';

import { Counter } from './Counter';

describe('<Counter />', () => {
  it('should render', () => {
    const { container } = renderWithTheme(<Counter />);
    const counter = container.firstElementChild;

    expect(counter).toBeInTheDocument();
  });

  it('should handle custom style', () => {
    const { container } = renderWithTheme(
      <Counter style={{ backgroundColor: 'papayawhip' }} />
    );
    const counter = container.firstElementChild;

    expect(counter).toHaveAttribute('style', 'background-color: papayawhip;');
  });

  it('should handle custom props', () => {
    const customProps: React.HTMLAttributes<HTMLDivElement> = {
      title: 'potatoe'
    };
    const { container } = renderWithTheme(<Counter {...customProps} />);
    const counter = container.firstElementChild;

    expect(counter).toHaveAttribute('title', 'potatoe');
  });

  describe('prop: minLength', () => {
    it('renders correct number of digits', () => {
      const { container } = renderWithTheme(
        <Counter value={32} minLength={7} />
      );
      const counter = container.firstElementChild;

      expect(counter && counter.childElementCount).toBe(7);
    });

    it('value length takes priority if bigger than minLength', () => {
      const { container } = renderWithTheme(
        <Counter value={1234} minLength={2} />
      );
      const counter = container.firstElementChild;

      expect(counter && counter.childElementCount).toBe(4);
    });
  });
});


================================================
FILE: src/Counter/Counter.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React, { useState } from 'react';
import { Button, Counter, Frame } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.desktopBackground};
  .counter-wrapper {
    display: flex;
    margin-top: 1rem;
  }
  .counter-wrapper button {
    margin-left: 0.5rem;
    height: 51px;
  }
  .wrapper {
    padding: 1rem;
  }
`;

export default {
  title: 'Other/Counter',
  component: Counter,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof Counter>;

export function Default() {
  const [count, setCount] = useState(13);
  const handleClick = () => setCount(count + 1);
  return (
    <Frame className='wrapper'>
      <Counter value={123456789} minLength={11} size='lg' />

      <div className='counter-wrapper'>
        <Counter value={count} minLength={3} />
        <Button onClick={handleClick}>Click!</Button>
      </div>
    </Frame>
  );
}

Default.story = {
  name: 'default'
};


================================================
FILE: src/Counter/Counter.tsx
================================================
import React, { forwardRef, useMemo } from 'react';
import styled from 'styled-components';

import { createBorderStyles } from '../common';
import { CommonStyledProps, Sizes } from '../types';
import { Digit } from './Digit';

type CounterProps = {
  minLength?: number;
  size?: Sizes | 'xl';
  value?: number;
} & React.HTMLAttributes<HTMLDivElement> &
  CommonStyledProps;

const CounterWrapper = styled.div`
  ${createBorderStyles({ style: 'status' })}
  display: inline-flex;
  background: #000000;
`;

const pixelSizes = {
  sm: 1,
  md: 2,
  lg: 3,
  xl: 4
};

const Counter = forwardRef<HTMLDivElement, CounterProps>(
  ({ value = 0, minLength = 3, size = 'md', ...otherProps }, ref) => {
    const digits = useMemo(
      () => value.toString().padStart(minLength, '0').split(''),
      [minLength, value]
    );
    return (
      <CounterWrapper ref={ref} {...otherProps}>
        {digits.map((digit, i) => (
          <Digit digit={digit} pixelSize={pixelSizes[size]} key={i} />
        ))}
      </CounterWrapper>
    );
  }
);

Counter.displayName = 'Counter';

export { Counter, CounterProps };


================================================
FILE: src/Counter/Digit.tsx
================================================
import React from 'react';
import styled, { css } from 'styled-components';

import { createHatchedBackground } from '../common';
import { CommonStyledProps } from '../types';

type DigitProps = {
  pixelSize?: number;
  digit?: number | string;
} & React.HTMLAttributes<HTMLDivElement> &
  CommonStyledProps;

const DigitWrapper = styled.div<Required<Pick<DigitProps, 'pixelSize'>>>`
  position: relative;
  --react95-digit-primary-color: #ff0102;
  --react95-digit-secondary-color: #740201;
  --react95-digit-bg-color: #000000;

  ${({ pixelSize }) => css`
    width: ${11 * pixelSize}px;
    height: ${21 * pixelSize}px;
    margin: ${pixelSize}px;

    span,
    span:before,
    span:after {
      box-sizing: border-box;
      display: inline-block;
      position: absolute;
    }
    span.active,
    span.active:before,
    span.active:after {
      background: var(--react95-digit-primary-color);
    }
    span:not(.active),
    span:not(.active):before,
    span:not(.active):after {
      ${createHatchedBackground({
        mainColor: 'var(--react95-digit-bg-color)',
        secondaryColor: 'var(--react95-digit-secondary-color)',
        pixelSize
      })}
    }

    span.horizontal,
    span.horizontal:before,
    span.horizontal:after {
      height: ${pixelSize}px;
      border-left: ${pixelSize}px solid var(--react95-digit-bg-color);
      border-right: ${pixelSize}px solid var(--react95-digit-bg-color);
    }
    span.horizontal.active,
    span.horizontal.active:before,
    span.horizontal.active:after {
      height: ${pixelSize}px;
      border-left: ${pixelSize}px solid var(--react95-digit-primary-color);
      border-right: ${pixelSize}px solid var(--react95-digit-primary-color);
    }
    span.horizontal {
      left: ${pixelSize}px;
      width: ${9 * pixelSize}px;
    }
    span.horizontal:before {
      content: '';
      width: 100%;
      top: ${pixelSize}px;
      left: ${0}px;
    }
    span.horizontal:after {
      content: '';
      width: calc(100% - ${pixelSize * 2}px);
      top: ${2 * pixelSize}px;
      left: ${pixelSize}px;
    }
    span.horizontal.top {
      top: 0;
    }
    span.horizontal.bottom {
      bottom: 0;
      transform: rotateX(180deg);
    }

    span.center,
    span.center:before,
    span.center:after {
      height: ${pixelSize}px;
      border-left: ${pixelSize}px solid var(--react95-digit-bg-color);
      border-right: ${pixelSize}px solid var(--react95-digit-bg-color);
    }
    span.center.active,
    span.center.active:before,
    span.center.active:after {
      border-left: ${pixelSize}px solid var(--react95-digit-primary-color);
      border-right: ${pixelSize}px solid var(--react95-digit-primary-color);
    }
    span.center {
      top: 50%;
      transform: translateY(-50%);
      left: ${pixelSize}px;
      width: ${9 * pixelSize}px;
    }
    span.center:before,
    span.center:after {
      content: '';
      width: 100%;
    }
    span.center:before {
      top: ${pixelSize}px;
    }
    span.center:after {
      bottom: ${pixelSize}px;
    }

    span.vertical,
    span.vertical:before,
    span.vertical:after {
      width: ${pixelSize}px;
      border-top: ${pixelSize}px solid var(--react95-digit-bg-color);
      border-bottom: ${pixelSize}px solid var(--react95-digit-bg-color);
    }
    span.vertical {
      height: ${11 * pixelSize}px;
    }
    span.vertical.left {
      left: 0;
    }
    span.vertical.right {
      right: 0;
      transform: rotateY(180deg);
    }
    span.vertical.top {
      top: 0px;
    }
    span.vertical.bottom {
      bottom: 0px;
    }
    span.vertical:before {
      content: '';
      height: 100%;
      top: ${0}px;
      left: ${pixelSize}px;
    }
    span.vertical:after {
      content: '';
      height: calc(100% - ${pixelSize * 2}px);
      top: ${pixelSize}px;
      left: ${pixelSize * 2}px;
    }
  `}
`;

const segments = [
  'horizontal top',
  'center',
  'horizontal bottom',
  'vertical top left',
  'vertical top right',
  'vertical bottom left',
  'vertical bottom right'
];

const digitActiveSegments = [
  [1, 0, 1, 1, 1, 1, 1], // 0
  [0, 0, 0, 0, 1, 0, 1], // 1
  [1, 1, 1, 0, 1, 1, 0], // 2
  [1, 1, 1, 0, 1, 0, 1], // 3
  [0, 1, 0, 1, 1, 0, 1], // 4
  [1, 1, 1, 1, 0, 0, 1], // 5
  [1, 1, 1, 1, 0, 1, 1], // 6
  [1, 0, 0, 0, 1, 0, 1], // 7
  [1, 1, 1, 1, 1, 1, 1], // 8
  [1, 1, 1, 1, 1, 0, 1] // 9
];

function Digit({ digit = 0, pixelSize = 2, ...otherProps }: DigitProps) {
  const segmentClasses = digitActiveSegments[Number(digit)].map((isActive, i) =>
    isActive ? `${segments[i]} active` : segments[i]
  );
  return (
    <DigitWrapper pixelSize={pixelSize} {...otherProps}>
      {segmentClasses.map((className, i) => (
        <span className={className} key={i} />
      ))}
    </DigitWrapper>
  );
}

export { Digit, DigitProps };


================================================
FILE: src/DatePicker/DatePicker.stories.tsx
================================================
/* eslint-disable camelcase, react/jsx-pascal-case */
import { ComponentMeta } from '@storybook/react';
import React from 'react';
import { DatePicker__UNSTABLE } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.desktopBackground};
`;

export default {
  title: 'DatePicker__UNSTABLE',
  component: DatePicker__UNSTABLE,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof DatePicker__UNSTABLE>;

export function Default() {
  return <DatePicker__UNSTABLE onAccept={date => console.log(date)} />;
}

Default.story = {
  name: 'default'
};


================================================
FILE: src/DatePicker/DatePicker.tsx
================================================
import React, { forwardRef, useCallback, useMemo, useState } from 'react';
import styled from 'styled-components';

import { Button } from '../Button/Button';
import { NumberInput } from '../NumberInput/NumberInput';
import { ScrollView } from '../ScrollView/ScrollView';
import { Select } from '../Select/Select';
import { Toolbar } from '../Toolbar/Toolbar';
import { Window, WindowContent, WindowHeader } from '../Window/Window';

type DatePickerProps = {
  className?: string;
  date?: string;
  onAccept?: (chosenDate: string) => void;
  onCancel?: React.MouseEventHandler<HTMLButtonElement>;
  shadow?: boolean;
};

const Calendar = styled(ScrollView)`
  width: 234px;
  margin: 1rem 0;
  background: ${({ theme }) => theme.canvas};
`;

const WeekDays = styled.div`
  display: flex;
  background: ${({ theme }) => theme.materialDark};
  color: #dfe0e3;
`;

const Dates = styled.div`
  display: flex;
  flex-wrap: wrap;
`;

const DateItem = styled.div`
  text-align: center;
  height: 1.5em;
  line-height: 1.5em;
  width: 14.28%;
`;

const DateItemContent = styled.span<{ active: boolean }>`
  cursor: pointer;

  background: ${({ active, theme }) =>
    active ? theme.hoverBackground : 'transparent'};
  color: ${({ active, theme }) =>
    active ? theme.canvasTextInvert : theme.canvasText};

  &:hover {
    border: 2px dashed
      ${({ theme, active }) => (active ? 'none' : theme.materialDark)};
  }
`;

const months = [
  { value: 0, label: 'January' },
  { value: 1, label: 'February' },
  { value: 2, label: 'March' },
  { value: 3, label: 'April' },
  { value: 4, label: 'May' },
  { value: 5, label: 'June' },
  { value: 6, label: 'July' },
  { value: 7, label: 'August' },
  { value: 8, label: 'September' },
  { value: 9, label: 'October' },
  { value: 10, label: 'November' },
  { value: 11, label: 'December' }
];

function daysInMonth(year: number, month: number) {
  return new Date(year, month + 1, 0).getDate();
}

function dayIndex(year: number, month: number, day: number) {
  return new Date(year, month, day).getDay();
}

function convertDateToState(stringDate: string) {
  const date = new Date(Date.parse(stringDate));
  const day = date.getUTCDate();
  const month = date.getUTCMonth();
  const year = date.getUTCFullYear();

  return { day, month, year };
}

const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
  (
    {
      className,
      date: initialDate = new Date().toISOString(),
      onAccept,
      onCancel,
      shadow = true
    },
    ref
  ) => {
    const [date, setDate] = useState(() => convertDateToState(initialDate));
    const { year, month, day } = date;

    const handleMonthSelect = useCallback(
      ({ value: monthSelected }: { value: number }) => {
        setDate(currentDate => ({ ...currentDate, month: monthSelected }));
      },
      []
    );

    const handleYearSelect = useCallback((yearSelected: number) => {
      setDate(currentDate => ({ ...currentDate, year: yearSelected }));
    }, []);

    const handleDaySelect = useCallback((daySelected: number) => {
      setDate(currentDate => ({ ...currentDate, day: daySelected }));
    }, []);

    const handleAccept = useCallback(() => {
      const chosenDate = [date.year, date.month + 1, date.day]
        .map(part => String(part).padStart(2, '0'))
        .join('-');

      onAccept?.(chosenDate);
    }, [date.day, date.month, date.year, onAccept]);

    const dayPickerItems = useMemo(() => {
      const items: React.ReactNode[] = Array.from({ length: 42 });
      const firstDayIndex = dayIndex(year, month, 1);
      let itemDay = day;

      const daysNumber = daysInMonth(year, month);
      itemDay = itemDay < daysNumber ? itemDay : daysNumber;
      items.forEach((_, i) => {
        if (i >= firstDayIndex && i < daysNumber + firstDayIndex) {
          const dayNumber = i - firstDayIndex + 1;

          items[i] = (
            <DateItem
              key={i}
              onClick={() => {
                handleDaySelect(dayNumber);
              }}
            >
              <DateItemContent active={dayNumber === itemDay}>
                {dayNumber}
              </DateItemContent>
            </DateItem>
          );
        } else {
          items[i] = <DateItem key={i} />;
        }
      });
      return items;
    }, [day, handleDaySelect, month, year]);

    return (
      <Window
        className={className}
        ref={ref}
        shadow={shadow}
        style={{ margin: 20 }}
      >
        <WindowHeader>
          <span role='img' aria-label='📆'>
            📆
          </span>
          Date
        </WindowHeader>
        <WindowContent>
          <Toolbar noPadding style={{ justifyContent: 'space-between' }}>
            <Select
              options={months}
              value={month}
              onChange={handleMonthSelect}
              width={128}
              menuMaxHeight={200}
            />
            <NumberInput value={year} onChange={handleYearSelect} width={100} />
          </Toolbar>
          <Calendar>
            <WeekDays>
              <DateItem>S</DateItem>
              <DateItem>M</DateItem>
              <DateItem>T</DateItem>
              <DateItem>W</DateItem>
              <DateItem>T</DateItem>
              <DateItem>F</DateItem>
              <DateItem>S</DateItem>
            </WeekDays>
            <Dates>{dayPickerItems}</Dates>
          </Calendar>
          <Toolbar noPadding style={{ justifyContent: 'space-between' }}>
            <Button fullWidth onClick={onCancel} disabled={!onCancel}>
              Cancel
            </Button>
            <Button
              fullWidth
              onClick={onAccept ? handleAccept : undefined}
              disabled={!onAccept}
            >
              OK
            </Button>
          </Toolbar>
        </WindowContent>
      </Window>
    );
  }
);

DatePicker.displayName = 'DatePicker';

// eslint-disable-next-line camelcase
export { DatePicker as DatePicker__UNSTABLE, DatePickerProps };


================================================
FILE: src/Frame/Frame.spec.tsx
================================================
import { render } from '@testing-library/react';
import React from 'react';

import { Frame } from './Frame';

describe('<Frame />', () => {
  it('should render frame', () => {
    const { container } = render(<Frame />);
    const frame = container.firstElementChild;

    expect(frame).toBeInTheDocument();
  });

  it('should render custom styles', () => {
    const { container } = render(
      <Frame style={{ backgroundColor: 'papayawhip' }} />
    );
    const frame = container.firstElementChild;

    expect(frame).toHaveAttribute('style', 'background-color: papayawhip;');
  });

  it('should render children', async () => {
    const { findByText } = render(
      <Frame>
        <span>Cool frame</span>
      </Frame>
    );
    const content = await findByText(/cool frame/i);

    expect(content).toBeInTheDocument();
  });

  it('should render custom props', () => {
    const customProps = { title: 'frame' };
    const { container } = render(<Frame {...customProps} />);
    const frame = container.firstElementChild;

    expect(frame).toHaveAttribute('title', 'frame');
  });
});


================================================
FILE: src/Frame/Frame.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React from 'react';
import { Frame } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.material};
  #default-buttons button {
    margin-bottom: 1rem;
    margin-right: 1rem;
  }

  #cutout {
    background: ${({ theme }) => theme.canvas};
    padding: 1rem;
    width: 300px;
  }
`;

export default {
  title: 'Layout/Frame',
  component: Frame,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof Frame>;

export function Default() {
  return (
    <Frame
      variant='outside'
      shadow
      style={{ padding: '0.5rem', lineHeight: '1.5', width: 600 }}
    >
      <p style={{ padding: '0.5rem' }}>
        This is a frame of the &apos;window&apos; variant, the default. Notice
        the subtle difference in borders. The lightest border is not on the edge
        of this frame.
      </p>
      <Frame variant='inside' style={{ margin: '1rem', padding: '1rem' }}>
        This frame of the &apos;button&apos; variant on the other hand has the
        lightest border on the edge. Use this frame inside &apos;window&apos;
        frames.
        <br />
        <Frame
          variant='field'
          style={{
            marginTop: '1rem',
            padding: '1rem',
            height: 200,
            width: 100
          }}
        >
          A field frame variant is used to display content.
        </Frame>
      </Frame>
      <Frame
        variant='well'
        style={{ marginTop: '1rem', padding: '0.1rem 0.25rem', width: '100%' }}
      >
        The &apos;status&apos; variant of a frame is often used as a status bar
        at the end of the window.
      </Frame>
    </Frame>
  );
}

Default.story = {
  name: 'default'
};


================================================
FILE: src/Frame/Frame.tsx
================================================
import React, { forwardRef } from 'react';
import styled, { css } from 'styled-components';
import { createBorderStyles, createBoxStyles } from '../common';
import { CommonStyledProps } from '../types';

type FrameProps = {
  children?: React.ReactNode;
  shadow?: boolean;
} & (
  | {
      variant?: 'window' | 'button' | 'field' | 'status';
    }
  | {
      /** @deprecated Use 'window', 'button' or 'status' */
      variant?: 'outside' | 'inside' | 'well';
    }
) &
  React.HTMLAttributes<HTMLDivElement> &
  CommonStyledProps;

const createFrameStyles = (variant: FrameProps['variant']) => {
  switch (variant) {
    case 'status':
    case 'well':
      return css`
        ${createBorderStyles({ style: 'status' })}
      `;
    case 'window':
    case 'outside':
      return css`
        ${createBorderStyles({ style: 'window' })}
      `;
    case 'field':
      return css`
        ${createBorderStyles({ style: 'field' })}
      `;
    default:
      return css`
        ${createBorderStyles()}
      `;
  }
};

const StyledFrame = styled.div<Required<Pick<FrameProps, 'variant'>>>`
  position: relative;
  font-size: 1rem;
  ${({ variant }) => createFrameStyles(variant)}
  ${({ variant }) =>
    createBoxStyles(
      variant === 'field'
        ? { background: 'canvas', color: 'canvasText' }
        : undefined
    )}
`;

const Frame = forwardRef<HTMLDivElement, FrameProps>(
  ({ children, shadow = false, variant = 'window', ...otherProps }, ref) => {
    return (
      <StyledFrame ref={ref} shadow={shadow} variant={variant} {...otherProps}>
        {children}
      </StyledFrame>
    );
  }
);

Frame.displayName = 'Frame';

export { Frame, FrameProps };


================================================
FILE: src/GroupBox/GroupBox.spec.tsx
================================================
import React from 'react';

import { renderWithTheme, theme } from '../../test/utils';

import { GroupBox } from './GroupBox';

describe('<GroupBox />', () => {
  it('renders GroupBox', () => {
    const { container } = renderWithTheme(<GroupBox />);
    const groupBox = container.firstChild as HTMLFieldSetElement;

    expect(groupBox).toBeInTheDocument();
  });
  it('renders children', () => {
    const textContent = 'Hi there!';
    const { getByText } = renderWithTheme(
      <GroupBox>
        <span>{textContent}</span>
      </GroupBox>
    );
    expect(getByText(textContent)).toBeInTheDocument();
  });

  describe('prop: label', () => {
    it('renders Label', () => {
      const labelText = 'Name:';
      const { container } = renderWithTheme(<GroupBox label={labelText} />);
      const groupBox = container.firstChild as HTMLFieldSetElement;
      const legend = groupBox.querySelector('legend');
      expect(legend?.textContent).toBe(labelText);
    });
    it('when not provided, <legend /> element is not rendered', () => {
      const { container } = renderWithTheme(<GroupBox />);
      const groupBox = container.firstChild as HTMLFieldSetElement;
      const legend = groupBox.querySelector('legend');
      expect(legend).not.toBeInTheDocument();
    });
  });
  describe('prop: disabled', () => {
    it('renders with disabled text content', () => {
      const { container } = renderWithTheme(<GroupBox disabled />);
      const groupBox = container.firstChild as HTMLFieldSetElement;

      expect(groupBox).toHaveAttribute('aria-disabled', 'true');

      expect(groupBox).toHaveStyleRule('color', theme.materialTextDisabled);
      expect(groupBox).toHaveStyleRule(
        'text-shadow',
        `1px 1px ${theme.materialTextDisabledShadow}`
      );
    });
  });
});


================================================
FILE: src/GroupBox/GroupBox.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React, { useState } from 'react';
import { Checkbox, GroupBox, ScrollView, Window, WindowContent } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.desktopBackground};
`;

export default {
  title: 'Controls/GroupBox',
  component: GroupBox,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof GroupBox>;

export function Default() {
  return (
    <Window>
      <WindowContent>
        <GroupBox label='Label here'>
          Some content here
          <span role='img' aria-label='😍'>
            😍
          </span>
        </GroupBox>
        <br />
        <GroupBox label='Disabled' disabled>
          Some content here
          <span role='img' aria-label='😍'>
            😍
          </span>
        </GroupBox>
      </WindowContent>
    </Window>
  );
}

Default.story = {
  name: 'default'
};

export function Flat() {
  return (
    <Window>
      <WindowContent>
        <ScrollView
          style={{ padding: '1rem', background: 'white', width: '300px' }}
        >
          <GroupBox variant='flat' label='Label here'>
            Some content here
            <span role='img' aria-label='😍'>
              😍
            </span>
          </GroupBox>
          <br />
          <GroupBox variant='flat' label='Disabled' disabled>
            Some content here
            <span role='img' aria-label='😍'>
              😍
            </span>
          </GroupBox>
        </ScrollView>
      </WindowContent>
    </Window>
  );
}

Flat.story = {
  name: 'flat'
};

export function ToggleExample() {
  const [state, setState] = useState(true);
  return (
    <Window>
      <WindowContent>
        <GroupBox
          disabled={state}
          label={
            <Checkbox
              style={{ margin: 0 }}
              label='Enable'
              checked={!state}
              onChange={() => setState(!state)}
            />
          }
        >
          Some content here
          <span role='img' aria-label='emoji in love'>
            😍
          </span>
        </GroupBox>
      </WindowContent>
    </Window>
  );
}

ToggleExample.story = {
  name: 'toggle example'
};


================================================
FILE: src/GroupBox/GroupBox.tsx
================================================
import React, { forwardRef } from 'react';
import styled, { css } from 'styled-components';
import { createDisabledTextStyles } from '../common';
import { CommonStyledProps } from '../types';

type GroupBoxProps = {
  label?: React.ReactNode;
  children?: React.ReactNode;
  disabled?: boolean;
  variant?: 'default' | 'flat';
} & React.FieldsetHTMLAttributes<HTMLFieldSetElement> &
  CommonStyledProps;

const StyledFieldset = styled.fieldset<
  Pick<GroupBoxProps, 'variant'> & { $disabled: boolean }
>`
  position: relative;
  border: 2px solid
    ${({ theme, variant }) =>
      variant === 'flat' ? theme.flatDark : theme.borderLightest};
  padding: 16px;
  margin-top: 8px;
  font-size: 1rem;
  color: ${({ theme }) => theme.materialText};
  ${({ variant }) =>
    variant !== 'flat' &&
    css`
      box-shadow: -1px -1px 0 1px ${({ theme }) => theme.borderDark},
        inset -1px -1px 0 1px ${({ theme }) => theme.borderDark};
    `}
  ${props => props.$disabled && createDisabledTextStyles()}
`;

const StyledLegend = styled.legend<Pick<GroupBoxProps, 'variant'>>`
  display: flex;
  position: absolute;
  top: 0;
  left: 8px;
  transform: translateY(calc(-50% - 2px));
  padding: 0 8px;

  font-size: 1rem;
  background: ${({ theme, variant }) =>
    variant === 'flat' ? theme.canvas : theme.material};
`;

const GroupBox = forwardRef<HTMLFieldSetElement, GroupBoxProps>(
  (
    { label, disabled = false, variant = 'default', children, ...otherProps },
    ref
  ) => {
    return (
      <StyledFieldset
        aria-disabled={disabled}
        $disabled={disabled}
        variant={variant}
        ref={ref}
        {...otherProps}
      >
        {label && <StyledLegend variant={variant}>{label}</StyledLegend>}
        {children}
      </StyledFieldset>
    );
  }
);

GroupBox.displayName = 'GroupBox';

export { GroupBox, GroupBoxProps };


================================================
FILE: src/Handle/Handle.spec.tsx
================================================
import React from 'react';

import { renderWithTheme } from '../../test/utils';

import { Handle } from './Handle';

describe('<Handle />', () => {
  it('should render bar', () => {
    const { container } = renderWithTheme(<Handle />);
    const barEl = container.firstChild;

    expect(barEl).toBeInTheDocument();
  });

  it('should handle custom style', () => {
    const { container } = renderWithTheme(
      <Handle style={{ backgroundColor: 'papayawhip' }} />
    );
    const barEl = container.firstChild;

    expect(barEl).toHaveAttribute('style', 'background-color: papayawhip;');
  });

  it('should handle custom props', () => {
    const customProps = { title: 'potatoe' };
    const { container } = renderWithTheme(<Handle {...customProps} />);
    const barEl = container.firstChild;

    expect(barEl).toHaveAttribute('title', 'potatoe');
  });

  describe('prop: size', () => {
    it('should set proper size', () => {
      const { container } = renderWithTheme(<Handle size='85%' />);
      const barEl = container.firstChild;

      expect(barEl).toHaveStyleRule('height', '85%');
    });

    it('when passed a number, sets size in px', () => {
      const { container } = renderWithTheme(<Handle size={25} />);
      const barEl = container.firstChild;

      expect(barEl).toHaveStyleRule('height', '25px');
    });
  });
});


================================================
FILE: src/Handle/Handle.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React from 'react';
import { AppBar, Button, Handle, Toolbar } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.desktopBackground};
`;

export default {
  title: 'Controls/Handle',
  component: Handle,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof Handle>;

export function Default() {
  return (
    <AppBar>
      <Toolbar>
        <Handle size={35} />
        <Button variant='menu'>Edit</Button>
        <Button variant='menu' disabled>
          Save
        </Button>
        <Handle size={35} />
      </Toolbar>
    </AppBar>
  );
}

Default.story = {
  name: 'default'
};


================================================
FILE: src/Handle/Handle.tsx
================================================
import React from 'react';
import styled from 'styled-components';
import { CommonStyledProps } from '../types';
import { getSize } from '../common/utils';

type HandleProps = {
  size?: string | number;
} & React.HTMLAttributes<HTMLDivElement> &
  CommonStyledProps;

// TODO: add horizontal variant
// TODO: allow user to specify number of bars (like 3 horizontal bars for drag handle)
const Handle = styled.div<HandleProps>`
  ${({ theme, size = '100%' }) => `
  display: inline-block;
  box-sizing: border-box;
  height: ${getSize(size)};
  width: 5px;
  border-top: 2px solid ${theme.borderLightest};
  border-left: 2px solid ${theme.borderLightest};
  border-bottom: 2px solid ${theme.borderDark};
  border-right: 2px solid ${theme.borderDark};
  background: ${theme.material};
`}
`;

Handle.displayName = 'Handle';

export { Handle, HandleProps };


================================================
FILE: src/Hourglass/Hourglass.spec.tsx
================================================
import { render } from '@testing-library/react';
import React from 'react';

import { Hourglass } from './Hourglass';

describe('<Hourglass />', () => {
  it('should render hourglass', () => {
    const { container } = render(<Hourglass />);
    const hourglass = container.firstElementChild;

    expect(hourglass).toBeInTheDocument();
  });

  it('should render correct size', () => {
    const { container } = render(<Hourglass size='66px' />);
    const hourglass = container.firstElementChild;

    const computedStyles = hourglass
      ? window.getComputedStyle(hourglass)
      : null;
    expect(computedStyles?.width).toBe('66px');
    expect(computedStyles?.height).toBe('66px');
  });

  it('should handle custom props', () => {
    const customProps: React.HTMLAttributes<HTMLSpanElement> = {
      title: 'hourglass'
    };
    const { container } = render(<Hourglass {...customProps} />);
    const hourglass = container.firstElementChild;

    expect(hourglass).toHaveAttribute('title', 'hourglass');
  });
});


================================================
FILE: src/Hourglass/Hourglass.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React from 'react';
import { Hourglass } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.desktopBackground};
`;

export default {
  title: 'Other/Hourglass',
  component: Hourglass,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof Hourglass>;

export function Default() {
  return <Hourglass size={32} style={{ margin: 20 }} />;
}

Default.story = {
  name: 'default'
};


================================================
FILE: src/Hourglass/Hourglass.tsx
================================================
import React, { forwardRef } from 'react';
import styled from 'styled-components';
import { getSize } from '../common/utils';
import { CommonStyledProps } from '../types';
import base64hourglass from './base64hourglass';

type HourglassProps = {
  size?: string | number;
} & React.HTMLAttributes<HTMLDivElement> &
  CommonStyledProps;

const StyledContainer = styled.div<Required<Pick<HourglassProps, 'size'>>>`
  display: inline-block;
  height: ${({ size }) => getSize(size)};
  width: ${({ size }) => getSize(size)};
`;

const StyledHourglass = styled.span`
  display: block;
  background: ${base64hourglass};
  background-size: cover;
  width: 100%;
  height: 100%;
`;

const Hourglass = forwardRef<HTMLSpanElement, HourglassProps>(
  ({ size = 30, ...otherProps }, ref) => {
    return (
      <StyledContainer size={size} ref={ref} {...otherProps}>
        <StyledHourglass />
      </StyledContainer>
    );
  }
);

Hourglass.displayName = 'Hourglass';

export { Hourglass, HourglassProps };


================================================
FILE: src/Hourglass/base64hourglass.tsx
================================================
const base64hourglass =
  "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=')";
export default base64hourglass;


================================================
FILE: src/MenuList/MenuList.spec.tsx
================================================
import React from 'react';

import { renderWithTheme } from '../../test/utils';

import { MenuList } from './MenuList';

describe('<MenuList />', () => {
  it('renders MenuList', () => {
    const { container } = renderWithTheme(<MenuList />);
    const menuList = container.firstElementChild;

    expect(menuList).toBeInTheDocument();
  });
  it('is an ul', () => {
    const { container } = renderWithTheme(<MenuList />);
    const menuList = container.firstElementChild;

    expect(menuList?.tagName).toBe('UL');
  });
  it('renders children', () => {
    const textContent = 'Hi there!';
    const { getByText } = renderWithTheme(
      <MenuList>
        <span>{textContent}</span>
      </MenuList>
    );
    expect(getByText(textContent)).toBeInTheDocument();
  });

  describe('prop: inline', () => {
    it('renders inline', () => {
      const { container } = renderWithTheme(<MenuList inline />);
      const menuList = container.firstElementChild;

      expect(menuList).toHaveStyleRule('display', 'inline-flex');
      expect(menuList).toHaveStyleRule('align-items', 'center');
    });
  });
  describe('prop: fullWidth', () => {
    it('has 100% width', () => {
      const { container } = renderWithTheme(<MenuList fullWidth />);
      const menuList = container.firstElementChild;

      expect(menuList).toHaveStyleRule('width', '100%');
    });
  });
});


================================================
FILE: src/MenuList/MenuList.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React from 'react';
import { Handle, MenuList, MenuListItem, Separator } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.desktopBackground};
  display: flex;
  align-items: center;
  & > * {
    margin-right: 1rem;
  }
`;

export default {
  title: 'Controls/MenuList',
  component: MenuList,
  subcomponents: { MenuListItem },
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof MenuList>;

export function Default() {
  return (
    <>
      <MenuList>
        <MenuListItem primary>Photos</MenuListItem>
        <MenuListItem
          as='a'
          // TODO: Come up with a more elegant way to allow props when `as` is used
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          href='https://expensive.toys'
          target='_blank'
        >
          Link
        </MenuListItem>
        <MenuListItem disabled>Other</MenuListItem>
      </MenuList>
      <MenuList inline>
        <MenuListItem square disabled>
          <span role='img' aria-label='🌿'>
            🌿
          </span>
        </MenuListItem>
        <Handle size={38} />
        <MenuListItem>Tackle</MenuListItem>
        <MenuListItem>Growl</MenuListItem>
        <MenuListItem disabled>Razor Leaf</MenuListItem>
      </MenuList>
      <MenuList>
        <MenuListItem primary size='sm'>
          View
        </MenuListItem>
        <Separator />
        <MenuListItem size='sm'>Paste</MenuListItem>
        <MenuListItem size='sm'>Paste Shortcut</MenuListItem>
        <MenuListItem size='sm'>Undo Copy</MenuListItem>
        <Separator />
        <MenuListItem size='sm'>Properties</MenuListItem>
      </MenuList>
      <MenuList>
        <MenuListItem square>
          <span role='img' aria-label='😎'>
            😎
          </span>
        </MenuListItem>
        <MenuListItem square>
          <span role='img' aria-label='🤖'>
            🤖
          </span>
        </MenuListItem>
        <MenuListItem square>
          <span role='img' aria-label='🎁'>
            🎁
          </span>
        </MenuListItem>
      </MenuList>
    </>
  );
}

Default.story = {
  name: 'default'
};


================================================
FILE: src/MenuList/MenuList.tsx
================================================
import React from 'react';

import styled from 'styled-components';
import { createBorderStyles, createBoxStyles } from '../common';
import { CommonStyledProps } from '../types';

type MenuListProps = React.HTMLAttributes<HTMLUListElement> & {
  fullWidth?: boolean;
  shadow?: boolean;
  inline?: boolean;
} & CommonStyledProps;

// TODO keyboard controls
const MenuList = styled.ul.attrs(() => ({
  role: 'menu'
}))<MenuListProps>`
  box-sizing: border-box;
  width: ${props => (props.fullWidth ? '100%' : 'auto')};
  padding: 4px;
  ${createBorderStyles({ style: 'window' })}
  ${createBoxStyles()}
  ${props =>
    props.inline &&
    `
    display: inline-flex;
    align-items: center;
  `}
  list-style: none;
  position: relative;
`;

MenuList.displayName = 'MenuList';

export * from './MenuListItem';

export { MenuList, MenuListProps };


================================================
FILE: src/MenuList/MenuListItem.spec.tsx
================================================
import React from 'react';

import { renderWithTheme, theme } from '../../test/utils';
import { blockSizes } from '../common/system';
import { MenuListItem } from './MenuListItem';

const defaultSize = 'lg';
describe('<MenuListItem />', () => {
  it('renders MenuListItem', () => {
    const { getByRole } = renderWithTheme(<MenuListItem />);
    const menuListItem = getByRole('menuitem');
    expect(menuListItem).toBeInTheDocument();
    expect(menuListItem).not.toHaveAttribute('aria-disabled');
  });
  it('renders children', () => {
    const textContent = 'Hi there!';
    const { getByText } = renderWithTheme(
      <MenuListItem>
        <span>{textContent}</span>
      </MenuListItem>
    );
    expect(getByText(textContent)).toBeInTheDocument();
  });
  it('should have a default role of menuitem', () => {
    const { getByRole } = renderWithTheme(<MenuListItem />);
    const menuListItem = getByRole('menuitem');
    expect(menuListItem).toHaveAttribute('role', 'menuitem');
  });

  it('should render with custom role', () => {
    const { getByRole } = renderWithTheme(<MenuListItem role='option' />);
    const menuListItem = getByRole('option');
    expect(menuListItem).toHaveAttribute('role', 'option');
  });

  //   it('should have a tabIndex of -1 by default', () => {
  //     const { getByRole } = renderWithTheme(<MenuListItem role='option' />);
  //     const menuListItem = getByRole('menuitem');
  //     expect(menuListItem).toHaveAttribute('tabIndex', '-1');
  //   });
  describe('prop: disabled', () => {
    it('should not trigger onClick callback', () => {
      const clickHandler = jest.fn();
      const { getByRole } = renderWithTheme(
        <MenuListItem disabled onClick={clickHandler} />
      );
      const menuListItem = getByRole('menuitem') as HTMLElement;
      menuListItem.click();
      expect(clickHandler).not.toBeCalled();
      expect(menuListItem).toHaveAttribute('aria-disabled', 'true');
    });
    it('renders with disabled styles ', () => {
      const { getByRole } = renderWithTheme(<MenuListItem disabled />);
      const menuListItem = getByRole('menuitem');
      expect(menuListItem).toHaveStyleRule('pointer-events', 'none');
      expect(menuListItem).toHaveStyleRule('color', theme.materialTextDisabled);
      expect(menuListItem).toHaveStyleRule(
        'text-shadow',
        `1px 1px ${theme.materialTextDisabledShadow}`
      );
    });
  });
  describe('prop: onClick', () => {
    it('should be called when clicked', () => {
      const clickHandler = jest.fn();
      const { getByRole } = renderWithTheme(
        <MenuListItem onClick={clickHandler} />
      );
      const menuListItem = getByRole('menuitem') as HTMLElement;
      menuListItem.click();
      expect(clickHandler).toHaveBeenCalledTimes(1);
    });
  });
  describe('prop: square', () => {
    it('should render square MenuListItem', () => {
      const { getByRole } = renderWithTheme(<MenuListItem square />);
      const menuListItem = getByRole('menuitem');

      expect(menuListItem).toHaveStyleRule('width', blockSizes[defaultSize]);
      expect(menuListItem).toHaveStyleRule('height', blockSizes[defaultSize]);
    });
  });
  describe('prop: size', () => {
    it('should define MenuListItem height', () => {
      const size = 'sm';
      const { getByRole } = renderWithTheme(<MenuListItem size={size} />);
      const menuListItem = getByRole('menuitem');

      expect(menuListItem).toHaveStyleRule('height', blockSizes[size]);
    });
  });
});


================================================
FILE: src/MenuList/MenuListItem.tsx
================================================
import React, { forwardRef } from 'react';

import styled from 'styled-components';
import { createDisabledTextStyles } from '../common';
import { blockSizes } from '../common/system';
import { CommonStyledProps, Sizes } from '../types';

type MenuListItemProps = {
  disabled?: boolean;
  square?: boolean;
  primary?: boolean;
  size?: Sizes;
} & React.HTMLAttributes<HTMLLIElement> &
  CommonStyledProps;

export const StyledMenuListItem = styled.li<{
  $disabled?: boolean;
  square?: boolean;
  primary?: boolean;
  size: Sizes;
}>`
  box-sizing: border-box;

  display: flex;
  align-items: center;
  position: relative;
  height: ${props => blockSizes[props.size]};
  width: ${props => (props.square ? blockSizes[props.size] : 'auto')};
  padding: 0 8px;
  font-size: 1rem;
  white-space: nowrap;
  justify-content: ${props =>
    props.square ? 'space-around' : 'space-between'};
  text-align: center;
  line-height: ${props => blockSizes[props.size]};
  color: ${({ theme }) => theme.materialText};
  pointer-events: ${({ $disabled }) => ($disabled ? 'none' : 'auto')};
  font-weight: ${({ primary }) => (primary ? 'bold' : 'normal')};
  &:hover {
    ${({ theme, $disabled }) =>
      !$disabled &&
      `
        color: ${theme.materialTextInvert};
        background: ${theme.hoverBackground};
      `}

    cursor: default;
  }
  ${props => props.$disabled && createDisabledTextStyles()}
`;

const MenuListItem = forwardRef<HTMLLIElement, MenuListItemProps>(
  (
    {
      size = 'lg',
      disabled,
      // tabIndex: tabIndexProp,
      square,
      children,
      onClick,
      primary,
      ...otherProps
    },
    ref
  ) => {
    // let tabIndex;
    // if (!disabled) {
    //   tabIndex = tabIndexProp !== undefined ? tabIndexProp : -1;
    // }

    return (
      <StyledMenuListItem
        $disabled={disabled}
        size={size}
        square={square}
        onClick={disabled ? undefined : onClick}
        primary={primary}
        // tabIndex={tabIndex}
        role='menuitem'
        ref={ref}
        aria-disabled={disabled}
        {...otherProps}
      >
        {children}
      </StyledMenuListItem>
    );
  }
);

MenuListItem.displayName = 'MenuListItem';

export { MenuListItem, MenuListItemProps };


================================================
FILE: src/Monitor/Monitor.spec.tsx
================================================
import React from 'react';

import { renderWithTheme } from '../../test/utils';

import { Monitor } from './Monitor';

describe('<Monitor />', () => {
  it('should render', () => {
    const { container } = renderWithTheme(<Monitor />);
    const monitorElement = container.firstElementChild;

    expect(monitorElement).toBeInTheDocument();
  });

  it('should handle custom props', () => {
    const customProps: React.HTMLAttributes<HTMLDivElement> = {
      title: 'potatoe'
    };
    const { container } = renderWithTheme(<Monitor {...customProps} />);
    const monitorElement = container.firstElementChild;

    expect(monitorElement).toHaveAttribute('title', 'potatoe');
  });

  describe('prop: backgroundStyles', () => {
    it('should forward styles to background element', () => {
      const { getByTestId } = renderWithTheme(
        <Monitor backgroundStyles={{ backgroundColor: 'papayawhip' }} />
      );
      const backgroundElement = getByTestId('background');

      expect(backgroundElement).toHaveAttribute(
        'style',
        'background-color: papayawhip;'
      );
    });
  });

  describe('prop: children', () => {
    it('children should be rendered in background element', () => {
      const { getByTestId } = renderWithTheme(<Monitor>Hi!</Monitor>);
      const backgroundElement = getByTestId('background');

      expect(backgroundElement.innerHTML).toBe('Hi!');
    });
  });
});


================================================
FILE: src/Monitor/Monitor.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React from 'react';
import { Monitor } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.desktopBackground};
`;

export default {
  title: 'Other/Monitor',
  component: Monitor,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof Monitor>;

export function Default() {
  return <Monitor backgroundStyles={{ background: 'blue' }} />;
}

Default.story = {
  name: 'default'
};


================================================
FILE: src/Monitor/Monitor.tsx
================================================
import React, { forwardRef } from 'react';
import styled from 'styled-components';

import { StyledScrollView } from '../ScrollView/ScrollView';

type MonitorProps = {
  backgroundStyles?: React.CSSProperties;
  children?: React.ReactNode;
};

const Wrapper = styled.div`
  position: relative;
  display: inline-block;
  padding-bottom: 26px;
`;

const Inner = styled.div`
  position: relative;
`;

const MonitorBody = styled.div`
  position: relative;
  z-index: 1;
  box-sizing: border-box;
  width: 195px;
  height: 155px;
  padding: 12px;
  background: ${({ theme }) => theme.material};
  border-top: 4px solid ${({ theme }) => theme.borderLightest};
  border-left: 4px solid ${({ theme }) => theme.borderLightest};
  border-bottom: 4px solid ${({ theme }) => theme.borderDark};
  border-right: 4px solid ${({ theme }) => theme.borderDark};

  outline: 1px dotted ${({ theme }) => theme.material};
  outline-offset: -3px;
  &:before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    outline: 1px dotted ${({ theme }) => theme.material};
  }
  box-shadow: 1px 1px 0 1px ${({ theme }) => theme.borderDarkest};

  &:after {
    content: '';
    display: inline-block;
    position: absolute;
    bottom: 4px;
    right: 12px;
    width: 10px;
    border-top: 2px solid #4d9046;
    border-bottom: 2px solid #07ff00;
  }
`;

const Background = styled(StyledScrollView).attrs(() => ({
  'data-testid': 'background'
}))`
  width: 100%;
  height: 100%;
`;

const Stand = styled.div`
  box-sizing: border-box;
  position: absolute;
  top: calc(100% + 2px);
  left: 50%;
  transform: translateX(-50%);
  height: 10px;
  width: 50%;
  background: ${({ theme }) => theme.material};
  border-left: 2px solid ${({ theme }) => theme.borderLightest};
  border-bottom: 2px solid ${({ theme }) => theme.borderDarkest};
  border-right: 2px solid ${({ theme }) => theme.borderDarkest};
  box-shadow: inset 0px 0px 0px 2px ${({ theme }) => theme.borderDark};

  &:before {
    content: '';
    position: absolute;
    top: calc(100% + 2px);
    left: 50%;
    transform: translateX(-50%);
    width: 80%;
    height: 8px;
    background: ${({ theme }) => theme.material};
    border-left: 2px solid ${({ theme }) => theme.borderLightest};
    border-right: 2px solid ${({ theme }) => theme.borderDarkest};
    box-shadow: inset 0px 0px 0px 2px ${({ theme }) => theme.borderDark};
  }
  &:after {
    content: '';
    position: absolute;
    top: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    width: 150%;
    height: 4px;
    background: ${({ theme }) => theme.material};
    border: 2px solid ${({ theme }) => theme.borderDark};
    border-bottom: none;
    box-shadow: inset 1px 1px 0px 1px ${({ theme }) => theme.borderLightest},
      1px 1px 0 1px ${({ theme }) => theme.borderDarkest};
  }
`;

const Monitor = forwardRef<HTMLDivElement, MonitorProps>(
  ({ backgroundStyles, children, ...otherProps }, ref) => {
    return (
      <Wrapper ref={ref} {...otherProps}>
        <Inner>
          <MonitorBody>
            <Background style={backgroundStyles}>{children}</Background>
          </MonitorBody>
          <Stand />
        </Inner>
      </Wrapper>
    );
  }
);

Monitor.displayName = 'Monitor';

export { Monitor, MonitorProps };


================================================
FILE: src/NumberInput/NumberInput.spec.tsx
================================================
import { fireEvent } from '@testing-library/react';
import React from 'react';

import { renderWithTheme } from '../../test/utils';
import { NumberInput } from './NumberInput';

// TODO: should we pass number or string to callbacks?
describe('<NumberInput />', () => {
  it('should call onChange on spin buttons click', () => {
    const handleChange = jest.fn();

    const { getByTestId } = renderWithTheme(
      <NumberInput onChange={handleChange} defaultValue={2} />
    );
    const spinButton = getByTestId('increment');
    spinButton.click();
    expect(handleChange).toHaveBeenCalledTimes(1);
    expect(handleChange).toHaveBeenCalledWith(3);
  });

  it('should call onChange on blur after keyboard input', () => {
    const handleChange = jest.fn();
    const { container } = renderWithTheme(
      <NumberInput onChange={handleChange} defaultValue={0} />
    );
    const input = container.querySelector('input') as HTMLInputElement;
    input.focus();
    fireEvent.change(input, { target: { value: '777' } });

    expect(handleChange).toHaveBeenCalledTimes(0);
    input.blur();

    expect(handleChange).toHaveBeenCalledTimes(1);
    expect(handleChange).toHaveBeenCalledWith(777);
  });

  // TODO: this test passes even tho it fails in real-life
  it('should not call onChange on blur, when clicked element is one of the spin buttons', () => {
    const handleChange = jest.fn();

    const { getByTestId, container } = renderWithTheme(
      <NumberInput onChange={handleChange} value={0} />
    );
    const input = container.querySelector('input') as HTMLInputElement;
    const incrementButton = getByTestId('increment');

    input.focus();
    fireEvent.keyDown(document.activeElement as HTMLInputElement, {
      key: '2'
    });
    incrementButton.click();
    expect(handleChange).toHaveBeenCalledTimes(1);
  });

  it('should give correct result after user changes input value and then clicks increment button', () => {
    const handleChange = jest.fn();
    const { container, getByTestId } = renderWithTheme(
      <NumberInput onChange={handleChange} defaultValue={0} />
    );
    const input = container.querySelector('input') as HTMLInputElement;
    const incrementButton = getByTestId('increment');

    fireEvent.change(input, { target: { value: '2' } });
    incrementButton.click();

    expect(handleChange).toHaveBeenCalledWith(3);
  });

  it('should reach max value', () => {
    const { getByTestId, container } = renderWithTheme(
      <NumberInput defaultValue={90} min={0} max={100} step={10} />
    );
    const input = container.querySelector('input') as HTMLInputElement;
    const incrementButton = getByTestId('increment');
    incrementButton.click();

    expect(input.value).toBe('100');
  });

  it('should reach min value', () => {
    const { getByTestId, container } = renderWithTheme(
      <NumberInput defaultValue={10} min={0} max={100} step={10} />
    );
    const input = container.querySelector('input') as HTMLInputElement;
    const decrementButton = getByTestId('decrement');
    decrementButton.click();

    expect(input.value).toBe('0');
  });

  describe('prop: step', () => {
    it('should be 1 by default', () => {
      const { getByTestId, container } = renderWithTheme(
        <NumberInput defaultValue={0} />
      );
      const input = container.querySelector('input') as HTMLInputElement;
      const incrementButton = getByTestId('increment');
      incrementButton.click();

      expect(input.value).toBe('1');
    });

    it('should change value by specified step', () => {
      const { getByTestId, container } = renderWithTheme(
        <NumberInput defaultValue={10} step={3} />
      );
      const input = container.querySelector('input') as HTMLInputElement;
      const decrementButton = getByTestId('decrement');
      decrementButton.click();

      expect(input.value).toBe('7');
    });

    it('should handle decimal step', () => {
      const { getByTestId, container } = renderWithTheme(
        <NumberInput defaultValue={10} step={0.3} />
      );
      const input = container.querySelector('input') as HTMLInputElement;
      const decrementButton = getByTestId('decrement');
      decrementButton.click();

      expect(input.value).toBe('9.7');
    });
  });

  describe('prop: disabled', () => {
    it('should render disabled', () => {
      const { getByTestId, container } = renderWithTheme(
        <NumberInput defaultValue={10} disabled />
      );
      const input = container.querySelector('input') as HTMLInputElement;
      const incrementButton = getByTestId('increment');
      const decrementButton = getByTestId('decrement');

      expect(input).toHaveAttribute('disabled');
      expect(incrementButton).toHaveAttribute('disabled');
      expect(decrementButton).toHaveAttribute('disabled');
    });

    it('should not react to button clicks', () => {
      const { getByTestId, container } = renderWithTheme(
        <NumberInput defaultValue={10} disabled />
      );
      const input = container.querySelector('input') as HTMLInputElement;
      const incrementButton = getByTestId('increment');
      const decrementButton = getByTestId('decrement');

      incrementButton.click();
      expect(input.value).toBe('10');

      decrementButton.click();
      expect(input.value).toBe('10');
    });
  });

  describe('prop: width', () => {
    it('should render component of specified width', () => {
      const { container } = renderWithTheme(
        <NumberInput defaultValue={10} disabled width={93} />
      );
      expect(
        getComputedStyle(container.firstElementChild as HTMLInputElement).width
      ).toBe('93px');
    });

    it('should handle %', () => {
      const { container } = renderWithTheme(
        <NumberInput defaultValue={10} disabled width='93%' />
      );
      expect(
        getComputedStyle(container.firstElementChild as HTMLInputElement).width
      ).toBe('93%');
    });
  });
});


================================================
FILE: src/NumberInput/NumberInput.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React from 'react';
import { ScrollView, NumberInput } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  background: ${({ theme }) => theme.material};
  padding: 5rem;
  & > * {
    margin-bottom: 1rem;
  }

  #cutout {
    background: ${({ theme }) => theme.canvas};
    padding: 2rem;
    width: 300px;
    & > div > * {
      margin-bottom: 1rem;
    }
  }
`;

export default {
  title: 'Controls/NumberInput',
  component: NumberInput,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof NumberInput>;

export function Default() {
  return (
    <>
      <NumberInput defaultValue={3} step={1.5} min={1.5} max={9} width={130} />
      <br />
      <NumberInput defaultValue={1995} width={130} />
      <br />
      <NumberInput disabled defaultValue={2020} width={130} />
    </>
  );
}

Default.story = {
  name: 'default'
};

export function Flat() {
  return (
    <ScrollView id='cutout'>
      <p>
        When you want to use NumberInput on a light background (like scrollable
        content), just use the flat variant:
      </p>
      <NumberInput
        variant='flat'
        defaultValue={1.5}
        min={0}
        max={9}
        width='130px'
      />
      <br />
      <NumberInput variant='flat' defaultValue={1995} width='130px' />
      <br />
      <NumberInput variant='flat' disabled defaultValue={2020} width='130px' />
    </ScrollView>
  );
}

Flat.story = {
  name: 'flat'
};


================================================
FILE: src/NumberInput/NumberInput.tsx
================================================
import React, { forwardRef, useCallback } from 'react';
import styled, { css } from 'styled-components';

import { Button } from '../Button/Button';
import useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';
import { blockSizes } from '../common/system';
import { clamp, getSize } from '../common/utils';
import { TextInput } from '../TextInput/TextInput';
import { CommonStyledProps } from '../types';

type NumberInputProps = {
  className?: string;
  defaultValue?: number;
  disabled?: boolean;
  max?: number;
  min?: number;
  readOnly?: boolean;
  step?: number;
  onChange?: (value: number) => void;
  style?: React.CSSProperties;
  value?: number;
  variant?: 'default' | 'flat';
  width?: string | number;
} & CommonStyledProps;

const StyledNumberInputWrapper = styled.div`
  display: inline-flex;
  align-items: center;
`;

const StyledButton = styled(Button)`
  width: 30px;
  padding: 0;
  flex-shrink: 0;

  ${({ variant }) =>
    variant === 'flat'
      ? css`
          height: calc(50% - 1px);
        `
      : css`
          height: 50%;
        `}
`;

const StyledButtonWrapper = styled.div<Pick<NumberInputProps, 'variant'>>`
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  justify-content: space-between;

  ${({ variant }) =>
    variant === 'flat'
      ? css`
          height: calc(${blockSizes.md} - 4px);
        `
      : css`
          height: ${blockSizes.md};
          margin-left: 2px;
        `}
`;

const StyledButtonIcon = styled.span<{ invert?: boolean }>`
  width: 0px;
  height: 0px;
  display: inline-block;
  ${({ invert }) =>
    invert
      ? css`
          border-left: 4px solid transparent;
          border-right: 4px solid transparent;
          border-bottom: 4px solid ${({ theme }) => theme.materialText};
        `
      : css`
          border-left: 4px solid transparent;
          border-right: 4px solid transparent;
          border-top: 4px solid ${({ theme }) => theme.materialText};
        `}
  ${StyledButton}:disabled & {
    filter: drop-shadow(
      1px 1px 0px ${({ theme }) => theme.materialTextDisabledShadow}
    );
    ${({ invert }) =>
      invert
        ? css`
            border-bottom-color: ${({ theme }) => theme.materialTextDisabled};
          `
        : css`
            border-top-color: ${({ theme }) => theme.materialTextDisabled};
          `}
  }
`;
const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
  (
    {
      className,
      defaultValue,
      disabled = false,
      max,
      min,
      onChange,
      readOnly,
      step = 1,
      style,
      value,
      variant = 'default',
      width,
      ...otherProps
    },
    ref
  ) => {
    const [valueDerived, setValueState] = useControlledOrUncontrolled({
      defaultValue,
      onChange,
      readOnly,
      value
    });

    const handleInputChange = useCallback(
      (e: React.ChangeEvent<HTMLInputElement>) => {
        const newValue = parseFloat(e.target.value);
        setValueState(newValue);
      },
      [setValueState]
    );

    const handleClick = useCallback(
      (delta: number) => {
        const newValue = clamp(
          parseFloat(((valueDerived ?? 0) + delta).toFixed(2)),
          min ?? null,
          max ?? null
        );

        setValueState(newValue);

        onChange?.(newValue);
      },
      [max, min, onChange, setValueState, valueDerived]
    );

    const onBlur = useCallback(() => {
      if (valueDerived !== undefined) {
        onChange?.(valueDerived);
      }
    }, [onChange, valueDerived]);

    const stepUp = useCallback(() => {
      handleClick(step);
    }, [handleClick, step]);

    const stepDown = useCallback(() => {
      handleClick(-step);
    }, [handleClick, step]);

    const buttonVariant = variant === 'flat' ? 'flat' : 'raised';
    return (
      <StyledNumberInputWrapper
        className={className}
        style={{
          ...style,
          width: width !== undefined ? getSize(width) : 'auto'
        }}
        {...otherProps}
      >
        <TextInput
          value={valueDerived}
          variant={variant}
          onChange={handleInputChange}
          disabled={disabled}
          type='number'
          readOnly={readOnly}
          ref={ref}
          fullWidth
          onBlur={onBlur}
        />
        <StyledButtonWrapper variant={variant}>
          <StyledButton
            data-testid='increment'
            variant={buttonVariant}
            disabled={disabled || readOnly}
            onClick={stepUp}
          >
            <StyledButtonIcon invert />
          </StyledButton>
          <StyledButton
            data-testid='decrement'
            variant={buttonVariant}
            disabled={disabled || readOnly}
            onClick={stepDown}
          >
            <StyledButtonIcon />
          </StyledButton>
        </StyledButtonWrapper>
      </StyledNumberInputWrapper>
    );
  }
);

NumberInput.displayName = 'NumberInput';

export { NumberInput, NumberInputProps };


================================================
FILE: src/ProgressBar/ProgressBar.spec.tsx
================================================
import React from 'react';

import { renderWithTheme } from '../../test/utils';
import { ProgressBar } from './ProgressBar';

describe('<ProgressBar />', () => {
  it('renders ProgressBar', () => {
    const value = 32;
    const { getByRole } = renderWithTheme(<ProgressBar value={value} />);

    const progressBar = getByRole('progressbar');

    expect(progressBar).toBeInTheDocument();
    expect(progressBar).toHaveAttribute('aria-valuenow', value.toString());
  });

  describe('prop: variant', () => {
    describe('variant: "default"', () => {
      it('displays current percentage value', () => {
        const value = 32;
        const { queryByTestId } = renderWithTheme(
          <ProgressBar value={value} />
        );

        expect(queryByTestId('defaultProgress1')?.textContent).toBe(
          `${value}%`
        );
        expect(queryByTestId('defaultProgress2')?.textContent).toBe(
          `${value}%`
        );

        expect(queryByTestId('defaultProgress2')).toHaveStyleRule(
          'clip-path',
          `polygon( 0 0, ${value}% 0, ${value}% 100%, 0 100% )`
        );

        expect(queryByTestId('indeterminateProgress')).not.toBeInTheDocument();
      });
    });

    describe('variant: "tile"', () => {
      it('Renders "tile" progress', () => {
        const { queryByTestId } = renderWithTheme(
          <ProgressBar variant='tile' />
        );
        expect(queryByTestId('defaultProgress1')).not.toBeInTheDocument();
        expect(queryByTestId('defaultProgress2')).not.toBeInTheDocument();
        expect(queryByTestId('tileProgress')).toBeInTheDocument();
      });

      // it('Renders correct number of tiles', () => {
      //   const value = 34;
      //   const { queryByTestId } = renderWithTheme(
      //     <Progress variant='tile' value={value} />
      //   );
      //   const tileProgress = queryByTestId('tileProgress');
      //   const tileProgressWidth = tileProgress.getBoundingClientRect().width;
      //   const tile = tileProgress.firstChild;
      //   const tileWidth = tile.getBoundingClientRect().width;

      //   const targetTileNumber = Math.floor(
      //     ((value / 100) * tileProgressWidth) / tileWidth
      //   );
      //   expect(tileProgress.childElementCount).toBe(targetTileNumber);
      // });
    });
  });

  describe('prop: hideValue', () => {
    it('renders progress bars, but does not show value', () => {
      const value = 32;
      const { queryByTestId } = renderWithTheme(
        <ProgressBar hideValue value={value} />
      );
      expect(queryByTestId('defaultProgress1')).toBeInTheDocument();
      expect(queryByTestId('defaultProgress2')).toBeInTheDocument();
      expect(queryByTestId('defaultProgress1')).toBeEmptyDOMElement();
      expect(queryByTestId('defaultProgress2')).toBeEmptyDOMElement();
    });
  });
});


================================================
FILE: src/ProgressBar/ProgressBar.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React, { useEffect, useState } from 'react';
import { ProgressBar } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  background: ${({ theme }) => theme.material};
  padding: 5rem;
`;

export default {
  title: 'Controls/ProgressBar',
  component: ProgressBar,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof ProgressBar>;

export function Default() {
  const [percent, setPercent] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setPercent(previousPercent => {
        if (previousPercent === 100) {
          return 0;
        }
        const diff = Math.random() * 10;
        return Math.min(previousPercent + diff, 100);
      });
    }, 500);
    return () => {
      clearInterval(timer);
    };
  }, []);

  return <ProgressBar value={Math.floor(percent)} />;
}

Default.story = {
  name: 'default'
};

export function Tile() {
  const [percent, setPercent] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setPercent(previousPercent => {
        if (previousPercent === 100) {
          return 0;
        }
        const diff = Math.random() * 10;
        return Math.min(previousPercent + diff, 100);
      });
    }, 500);
    return () => {
      clearInterval(timer);
    };
  }, []);

  return <ProgressBar variant='tile' value={Math.floor(percent)} />;
}

Tile.story = {
  name: 'tile'
};

export function HideValue() {
  const [percent, setPercent] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setPercent(previousPercent => {
        if (previousPercent === 100) {
          return 0;
        }
        const diff = Math.random() * 10;
        return Math.min(previousPercent + diff, 100);
      });
    }, 500);
    return () => {
      clearInterval(timer);
    };
  }, []);

  return <ProgressBar hideValue value={Math.floor(percent)} />;
}

HideValue.story = {
  name: 'hide value'
};


================================================
FILE: src/ProgressBar/ProgressBar.tsx
================================================
import React, {
  forwardRef,
  useCallback,
  useEffect,
  useRef,
  useState
} from 'react';
import styled, { css } from 'styled-components';

import { blockSizes } from '../common/system';
import { StyledScrollView } from '../ScrollView/ScrollView';
import { CommonStyledProps } from '../types';

type ProgressBarProps = {
  hideValue?: boolean;
  shadow?: boolean;
  value?: number;
  variant?: 'default' | 'tile';
} & React.HTMLAttributes<HTMLDivElement> &
  CommonStyledProps;

const Wrapper = styled.div<Required<Pick<ProgressBarProps, 'variant'>>>`
  display: inline-block;
  height: ${blockSizes.md};
  width: 100%;
`;

const ProgressCutout = styled(StyledScrollView)<
  Required<Pick<ProgressBarProps, 'variant'>>
>`
  width: 100%;
  height: 100%;
  position: relative;
  text-align: center;
  padding: 0;
  overflow: hidden;
  &:before {
    z-index: 1;
  }
`;
const commonBarStyles = css`
  width: calc(100% - 4px);
  height: calc(100% - 4px);

  display: flex;
  align-items: center;
  justify-content: space-around;
`;
const WhiteBar = styled.div`
  position: relative;
  top: 4px;
  ${commonBarStyles}
  background: ${({ theme }) => theme.canvas};
  color: #000;
  margin-left: 2px;
  margin-top: -2px;
  color: ${({ theme }) => theme.materialText};
`;

const BlueBar = styled.div<Pick<ProgressBarProps, 'value'>>`
  position: absolute;
  top: 2px;
  left: 2px;
  ${commonBarStyles}
  color: ${({ theme }) => theme.materialTextInvert};
  background: ${({ theme }) => theme.progress};
  clip-path: polygon(
    0 0,
    ${({ value = 0 }) => value}% 0,
    ${({ value = 0 }) => value}% 100%,
    0 100%
  );
  transition: 0.4s linear clip-path;
`;

const TilesWrapper = styled.div`
  width: calc(100% - 6px);
  height: calc(100% - 8px);
  position: absolute;
  left: 3px;
  top: 4px;
  box-sizing: border-box;
  display: inline-flex;
`;
const tileWidth = 17;
const Tile = styled.span`
  display: inline-block;
  width: ${tileWidth}px;
  box-sizing: border-box;
  height: 100%;
  background: ${({ theme }) => theme.progress};
  border-color: ${({ theme }) => theme.material};
  border-width: 0px 1px;
  border-style: solid;
`;

const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>(
  (
    {
      hideValue = false,
      shadow = true,
      value,
      variant = 'default',
      ...otherProps
    },
    ref
  ) => {
    const displayValue = hideValue ? null : `${value}%`;

    const tilesWrapperRef = useRef<HTMLDivElement | null>(null);
    const [tiles, setTiles] = useState([]);

    // TODO debounce this function
    const updateTilesNumber = useCallback(() => {
      if (!tilesWrapperRef.current || value === undefined) {
        return;
      }
      const progressWidth =
        tilesWrapperRef.current.getBoundingClientRect().width;
      const newTilesNumber = Math.round(
        ((value / 100) * progressWidth) / tileWidth
      );
      setTiles(Array.from({ length: newTilesNumber }));
    }, [value]);

    useEffect(() => {
      updateTilesNumber();

      window.addEventListener('resize', updateTilesNumber);
      return () => window.removeEventListener('resize', updateTilesNumber);
    }, [updateTilesNumber]);

    return (
      <Wrapper
        aria-valuenow={value !== undefined ? Math.round(value) : undefined}
        ref={ref}
        role='progressbar'
        variant={variant}
        {...otherProps}
      >
        <ProgressCutout variant={variant} shadow={shadow}>
          {variant === 'default' ? (
            <>
              <WhiteBar data-testid='defaultProgress1'>{displayValue}</WhiteBar>
              <BlueBar data-testid='defaultProgress2' value={value}>
                {displayValue}
              </BlueBar>
            </>
          ) : (
            <TilesWrapper ref={tilesWrapperRef} data-testid='tileProgress'>
              {tiles.map((_, index) => (
                <Tile key={index} />
              ))}
            </TilesWrapper>
          )}
        </ProgressCutout>
      </Wrapper>
    );
  }
);

ProgressBar.displayName = 'ProgressBar';

export { ProgressBar, ProgressBarProps };


================================================
FILE: src/Radio/Radio.spec.tsx
================================================
import React from 'react';

import { renderWithTheme } from '../../test/utils';
import { Radio } from './Radio';

describe('<Radio />', () => {
  describe('label', () => {
    it('renders', () => {
      const labelText = 'Swag';
      const { getByLabelText } = renderWithTheme(<Radio label={labelText} />);
      expect(getByLabelText(labelText)).toBeInTheDocument();
    });
  });

  describe('prop: onChange', () => {
    it('should be called when Radio is clicked', () => {
      const handleChange = jest.fn(event => event.target.checked);

      const { getByRole } = renderWithTheme(
        <Radio onChange={handleChange} value='swag' />
      );

      getByRole('radio').click();

      expect(handleChange).toHaveBeenCalledTimes(1);
    });
  });

  describe('prop: disabled', () => {
    it('should disable radio', () => {
      const handleChange = jest.fn();

      const { getByRole } = renderWithTheme(<Radio disabled />);
      const checkbox = getByRole('radio');
      expect(checkbox).toHaveAttribute('disabled');

      checkbox.click();
      expect(handleChange).not.toHaveBeenCalled();
    });
    it('should be overridden by props', () => {
      const { getByRole, rerender } = renderWithTheme(<Radio disabled />);
      rerender(<Radio disabled={false} />);
      const checkbox = getByRole('radio');
      expect(checkbox).not.toHaveAttribute('disabled');
    });
  });

  describe('controlled', () => {
    it('should check the radio', () => {
      const { getByRole, rerender } = renderWithTheme(
        <Radio checked={false} readOnly />
      );

      rerender(<Radio checked readOnly />);
      const checkbox = getByRole('radio') as HTMLInputElement;

      expect(checkbox.checked).toBe(true);
      expect(getByRole('radio')).toHaveAttribute('checked');
      expect(getByRole('presentation').firstChild).toHaveAttribute(
        'data-testid',
        'checkmarkIcon'
      );
    });

    it('should uncheck the checkbox', () => {
      const { getByRole, rerender } = renderWithTheme(
        <Radio checked readOnly />
      );
      rerender(<Radio checked={false} readOnly />);
      const checkbox = getByRole('radio') as HTMLInputElement;

      expect(checkbox.checked).toBe(false);
      expect(getByRole('radio')).not.toHaveAttribute('checked');
      expect(getByRole('presentation').firstChild).toBeNull();
    });
  });
});


================================================
FILE: src/Radio/Radio.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React, { useState } from 'react';
import { GroupBox, Radio, ScrollView, Window, WindowContent } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.desktopBackground};
  #cutout {
    background: ${({ theme }) => theme.canvas};
    color: ${({ theme }) => theme.materialText};
    padding: 1rem;
    width: 300px;
    & p {
      margin-bottom: 2rem;
    }
  }
`;

export default {
  title: 'Controls/Radio',
  component: Radio,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof Radio>;

export function Default() {
  const [state, setState] = useState('Pear');
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) =>
    setState(e.target.value);

  return (
    <Window>
      <WindowContent>
        <GroupBox label='Fruits'>
          <Radio
            checked={state === 'Pear'}
            onChange={handleChange}
            value='Pear'
            label='🍐 Pear'
            name='fruits'
          />
          <br />
          <Radio
            checked={state === 'Orange'}
            onChange={handleChange}
            value='Orange'
            label='🍊 Orange'
            name='fruits'
          />
          <br />
          <Radio
            checked={state === 'Kiwi'}
            onChange={handleChange}
            value='Kiwi'
            label='🥝 Kiwi'
            name='fruits'
          />
          <br />
          <Radio
            checked={state === 'Grape'}
            onChange={handleChange}
            value='Grape'
            label='🍇 Grape'
            name='fruits'
            disabled
          />
        </GroupBox>
      </WindowContent>
    </Window>
  );
}

Default.story = {
  name: 'default'
};

export function Flat() {
  const [state, setState] = useState('Pear');
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) =>
    setState(e.target.value);

  return (
    <Window>
      <WindowContent>
        <ScrollView id='cutout'>
          <p>
            When you want to use radio buttons on a light background (like
            scrollable content), just use the flat variant:
          </p>

          <GroupBox variant='flat' label='Fruits'>
            <Radio
              variant='flat'
              checked={state === 'Pear'}
              onChange={handleChange}
              value='Pear'
              label='🍐 Pear'
              name='fruits'
            />
            <br />
            <Radio
              variant='flat'
              checked={state === 'Orange'}
              onChange={handleChange}
              value='Orange'
              label='🍊 Orange'
              name='fruits'
            />
            <br />
            <Radio
              variant='flat'
              checked={state === 'Kiwi'}
              onChange={handleChange}
              value='Kiwi'
              label='🥝 Kiwi'
              name='fruits'
            />
            <br />
            <Radio
              variant='flat'
              checked={state === 'Grape'}
              onChange={handleChange}
              value='Grape'
              label='🍇 Grape'
              name='fruits'
              disabled
            />
          </GroupBox>
        </ScrollView>
      </WindowContent>
    </Window>
  );
}

Flat.story = {
  name: 'flat'
};


================================================
FILE: src/Radio/Radio.tsx
================================================
import React, { forwardRef } from 'react';
import styled, { css, CSSProperties } from 'styled-components';

import { createFlatBoxStyles } from '../common';
import {
  LabelText,
  size,
  StyledInput,
  StyledLabel
} from '../common/SwitchBase';
import { StyledScrollView } from '../ScrollView/ScrollView';
import { CommonStyledProps } from '../types';

type RadioVariant = 'default' | 'flat';

type RadioProps = {
  checked?: boolean;
  className?: string;
  disabled?: boolean;
  label?: string | number;
  name?: string;
  onChange?: React.ChangeEventHandler<HTMLInputElement>;
  style?: CSSProperties;
  value?: string | number | boolean;
  variant?: RadioVariant;
} & Omit<
  React.InputHTMLAttributes<HTMLInputElement>,
  'checked' | 'className' | 'disabled' | 'name' | 'onChange' | 'style' | 'value'
> &
  CommonStyledProps;

const sharedCheckboxStyles = css`
  width: ${size}px;
  height: ${size}px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: space-around;
  margin-right: 0.5rem;
`;

type StyledCheckboxProps = {
  $disabled: boolean;
};

const StyledCheckbox = styled(StyledScrollView)<StyledCheckboxProps>`
  ${sharedCheckboxStyles}
  background: ${({ $disabled, theme }) =>
    $disabled ? theme.material : theme.canvas};

  &:before {
    content: '';
    position: absolute;
    left: 0px;
    top: 0px;
    width: calc(100% - 4px);
    height: calc(100% - 4px);
    border-radius: 50%;
    box-shadow: none;
  }
`;
const StyledFlatCheckbox = styled.div<StyledCheckboxProps>`
  ${createFlatBoxStyles()}
  ${sharedCheckboxStyles}
  outline: none;
  background: ${({ $disabled, theme }) =>
    $disabled ? theme.flatLight : theme.canvas};
  &:before {
    content: '';
    display: inline-block;
    position: absolute;
    top: 0;
    left: 0;
    width: calc(100% - 4px);
    height: calc(100% - 4px);
    border: 2px solid ${({ theme }) => theme.flatDark};
    border-radius: 50%;
  }
`;

type IconProps = {
  'data-testid': 'checkmarkIcon';
  $disabled: boolean;
  variant: RadioVariant;
};

const Icon = styled.span.attrs(() => ({
  'data-testid': 'checkmarkIcon'
}))<IconProps>`
  position: absolute;
  content: '';
  display: inline-block;
  top: 50%;
  left: 50%;
  width: 6px;
  height: 6px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: ${p =>
    p.$disabled ? p.theme.checkmarkDisabled : p.theme.checkmark};
`;

const CheckboxComponents = {
  flat: StyledFlatCheckbox,
  default: StyledCheckbox
};

const Radio = forwardRef<HTMLInputElement, RadioProps>(
  (
    {
      checked,
      className = '',
      disabled = false,
      label = '',
      onChange,
      style = {},
      variant = 'default',
      ...otherProps
    },
    ref
  ) => {
    const CheckboxComponent = CheckboxComponents[variant];

    return (
      <StyledLabel $disabled={disabled} className={className} style={style}>
        <CheckboxComponent $disabled={disabled} role='presentation'>
          {checked && <Icon $disabled={disabled} variant={variant} />}
        </CheckboxComponent>
        <StyledInput
          disabled={disabled}
          onChange={disabled ? undefined : onChange}
          readOnly={disabled}
          type='radio'
          checked={checked}
          ref={ref}
          {...otherProps}
        />
        {label && <LabelText>{label}</LabelText>}
      </StyledLabel>
    );
  }
);

Radio.displayName = 'Radio';

export { Radio, RadioProps };


================================================
FILE: src/ScrollView/ScrollView.spec.tsx
================================================
import { render } from '@testing-library/react';
import React from 'react';

import { ScrollView } from './ScrollView';

describe('<ScrollView />', () => {
  it('should render scrollview', () => {
    const { container } = render(<ScrollView />);
    const scrollView = container.firstElementChild;

    expect(scrollView).toBeInTheDocument();
  });

  it('should render custom styles', () => {
    const { container } = render(
      <ScrollView style={{ backgroundColor: 'papayawhip' }} />
    );
    const scrollView = container.firstElementChild;

    expect(scrollView).toHaveAttribute(
      'style',
      'background-color: papayawhip;'
    );
  });

  it('should render children', async () => {
    const { findByText } = render(
      <ScrollView>
        <span>Cool ScrollView</span>
      </ScrollView>
    );
    const content = await findByText(/cool scrollview/i);

    expect(content).toBeInTheDocument();
  });

  it('should render custom props', () => {
    const customProps = { title: 'scrollview' };
    const { container } = render(<ScrollView {...customProps} />);
    const scrollView = container.firstElementChild;

    expect(scrollView).toHaveAttribute('title', 'scrollview');
  });
});


================================================
FILE: src/ScrollView/ScrollView.stories.tsx
================================================
import { ComponentMeta } from '@storybook/react';
import React from 'react';
import { ScrollView, Window, WindowContent } from 'react95';
import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 5rem;
  background: ${({ theme }) => theme.desktopBackground};
`;

export default {
  title: 'Layout/ScrollView',
  component: ScrollView,
  decorators: [story => <Wrapper>{story()}</Wrapper>]
} as ComponentMeta<typeof ScrollView>;

export function Default() {
  return (
    <Window>
      <WindowContent>
        <ScrollView style={{ width: '300px', height: '200px' }}>
          <div>
            <p style={{ width: 400 }}>
              React95 is the best UI library ever created
            </p>
            <p>React95 is the best UI library ever created</p>
            <p>React95 is the best UI library ever created</p>
            <p>React95 is the best UI library ever created</p>
            <p>React95 is the best UI library ever created</p>
            <p>React95 is the best UI library ever created</p>
            <p>React95 is the best UI library ever created</p>
            <p>React95 is the best UI library ever created</p>
            <p>React95 is the best UI library ever created</p>
          </div>
        </ScrollView>
      </WindowContent>
    </Window>
  );
}

Default.story = {
  name: 'default'
};


================================================
FILE: src/ScrollView/ScrollView.tsx
================================================
import React, { forwardRef } from 'react';
import styled from 'styled-components';
import { insetShadow, createScrollbars } from '../common';
import { CommonStyledProps } from '../types';

type ScrollViewProps = {
  children?: React.ReactNode;
  shadow?: boolean;
} & React.HTMLAttributes<HTMLDivElement> &
  CommonStyledProps;

export const StyledScrollView = styled.div<Pick<ScrollViewProps, 'shadow'>>`
  position: relative;
  box-sizing: border-box;
  padding: 2px;
  font-size: 1rem;
  border-style: solid;
  border-width: 2px;
  border-left-color: ${({ theme }) => theme.borderDark};
  border-top-color: ${({ theme }) => theme.borderDark};
  border-right-color: ${({ theme }) => theme.borderLightest};
  border-bottom-color: ${({ theme }) => theme.borderLightest};
  line-height: 1.5;
  &:before {
    position: absolute;
    left: 0;
    top: 0;
    content: '';
    width: calc(100% - 4px);
    height: calc(100% - 4px);

    border-style: solid;
    border-width: 2px;
    border-left-color: ${({ theme }) => theme.borderDarkest};
    border-top-color: ${({ theme }) => theme.borderDarkest};
    border-right-color: ${({ theme }) => theme.borderLight};
    border-bottom-color: ${({ theme }) => theme.borderLight};

    pointer-events: none;
    ${props => props.shadow && `box-shadow:${insetShadow};`}
  }
`;

const Content = styled.div`
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  padding: 4px;
  overflow: auto;
  ${createScrollbars()}
`;

const ScrollView = forwardRef<HTMLDivElement, ScrollViewProps>(
  ({ children, shadow = true, ...otherProps }, ref) => {
    return (
      <StyledScrollView ref={ref} shadow={shadow} {...otherProps}>
        <Content>{children}</Content>
      </StyledScrollView>
    );
  }
);

ScrollView.displayName = 'ScrollView';

export { ScrollView, ScrollViewProps };


================================================
FILE: src/Select/Select.spec.tsx
================================================
import { fireEvent, screen, waitFor } from '@testing-library/react';
import React from 'react';
import { renderWithTheme } from '../../test/utils';
import { noOp } from '../common/utils';
import { Select } from './Select';
import { SelectOption, SelectRef } from './Select.types';

const options: SelectOption<number>[] = [
  { label: 'ten', value: 10 },
  { label: 'twenty', value: 20 },
  { label: 'thirty', value: 30 }
];

describe('<Select />', () => {
  it('should be able to mount the component', () => {
    const { container } = renderWithTheme(
      <Select defaultValue={10} options={options} />
    );

    const input = container.querySelector('input') as HTMLInputElement;
    expect(input.value).toBe('10');
  });

  it('renders dropdown button with icon', () => {
    renderWithTheme(<Select defaultValue={10} options={options} />);

    const button = screen.getByTestId('select-button');
    expect(button).toBeInTheDocument();
    // we render styled.button, but as='div'
    // because it's used only for aesthetic purposes
    expect(button.tagName).not.toBe('BUTTON');
    expect(button.firstChild).toHaveAttribute('data-testid', 'select-icon');
  });

  it('the trigger is in tab order', () => {
    renderWithTheme(<Select defaultValue={10} options={options} />);
    expect(screen.getByRole('button')).toHaveProperty('tabIndex', 1);
  });

  it('should accept null child', () => {
    renderWithTheme(<Select defaultValue={10} options={[...options, null]} />);
  });

  it('should have an input with [type="hidden"] and string value by default', () => {
    const { container } = renderWithTheme(
      <Select defaultValue={10} options={options} />
    );
    const input = container.querySelector('input');
    expect(input).toHaveAttribute('type', 'hidden');
    expect(input).toHaveAttribute('value', '10');
  });

  it('passes through the blur event when menu is closed', () => {
    const handleBlur = jest.fn();
    renderWithTheme(
      <Select
        onBlur={handleBlur}
        options={[
          { label: 'ten', value: '10' },
          { label: 'none', value: '' }
        ]}
      />
    );

    const trigger = screen.getByRole('button');
    fireEvent.focus(trigger);
    fireEvent.blur(trigger);
    expect(handleBlur).toHaveBeenCalledTimes(1);
  });

  it('should ignore onBlur when the menu opens', () => {
    // mousedown calls focus while click opens moving the focus to an item
    // this means the trigger is blurred immediately
    const handleBlur = jest.fn();
    renderWithTheme(
      <Select
        onBlur={handleBlur}
        defaultValue=''
        onMouseDown={event => {
          // simulating certain platforms that focus on mousedown
          if (event.defaultPrevented === false) {
            event.currentTarget.focus();
          }
        }}
        options={[
          { label: 'ten', value: '10' },
          { label: 'none', value: '' }
        ]}
      />
    );
    const trigger = screen.getByRole('button');
    fireEvent.mouseDown(trigger);
    expect(handleBlur).toHaveBeenCalledTimes(0);
    expect(screen.getByRole('listbox')).toBeInTheDocument();
    const o = screen.getAllByRole('option');
    fireEvent.mouseDown(o[0]);
    o[0].click();
    expect(handleBlur).toHaveBeenCalledTimes(0);
    expect(screen.queryByRole('listbox', { hidden: false })).toBe(null);
  });

  it('options should have a data-value attribute', () => {
    renderWithTheme(
      <Select defaultValue={10} onOpen={noOp} open options={options} />
    );
    const o = screen.getAllByRole('option');
    expect(o[0]).toHaveAttribute('data-value', '10');
    expect(o[1]).toHaveAttribute('data-value', '20');
  });

  it('should call onClose when user clicks outside of component', async () => {
    const handleClose = jest.fn();
    renderWithTheme(
      <div>
        <Select onClose={handleClose} options={options} />
        <div data-testid='el'>swag</div>
      </div>
    );
    expect(handleClose).toHaveBeenCalledTimes(0);
    fireEvent.mouseDown(screen.getByRole('button'));
    fireEvent.mouseDown(screen.getByText('swag'));
    expect(handleClose).toHaveBeenCalledTimes(1);
  });

  it('should open and close on mouseDown', async () => {
    renderWithTheme(<Select options={options} />);

    fireEvent.mouseDown(screen.getByRole('button'));
    expect(screen.getByRole('listbox')).toBeInTheDocument();

    fireEvent.mouseDown(screen.getByRole('button'));
    expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
  });

  describe('prop: inputProps', () => {
    it('should apply additional props to trigger element', () => {
      renderWithTheme(
        <Select
          defaultValue={10}
          inputProps={{ 'data-testid': 'SelectInput' }}
          options={options}
        />
      );
      expect(screen.getByTestId('SelectInput')).toHaveProperty(
        'tagName',
        'INPUT'
      );
    });
  });

  describe('prop: menuMaxHeight', () => {
    it('sets max-height to dropdown', () => {
      renderWithTheme(
        <Select
          defaultValue={10}
          onOpen={noOp}
          open
          options={options}
          menuMaxHeight={220}
        />
      );

      const listbox = screen.getByRole('listbox') as HTMLElement;
      expect(
        listbox.getAttribute('style')?.includes('max-height: 220px')
      ).toBeTruthy();
    });
  });

  describe('prop: onClose, onFocus, onKeyDown, onOpen', () => {
    it('passes event through', () => {
      const handler = jest.fn();

      renderWithTheme(
        <>
          <Select
            defaultValue={10}
            onClose={handler}
            onFocus={handler}
            onKeyDown={handler}
            onOpen={handler}
            options={options}
          />
          <div>outside</div>
        </>
      );

      const button = screen.getByRole('button');

      fireEvent.focus(button);
      expect(handler).toHaveBeenCalledWith(
        expect.objectContaining({ type: 'focus' })
      );
      handler.mockClear();

      fireEvent.keyDown(button);
      expect(handler).toHaveBeenCalledWith(
        expect.objectContaining({ type: 'keydown' })
      );
      handler.mockClear();

      fireEvent.mouseDown(button);
      expect(handler).toHaveBeenCalledWith({
        fromEvent: expect.objectContaining({ type: 'mousedown' })
      });
      handler.mockClear();

      fireEvent.mouseDown(screen.getByText('outside'));
      expect(handler).toHaveBeenCalledWith({
        fromEvent: expect.objectContaining({ type: 'mousedown' })
      });
      handler.mockClear();
    });
  });

  describe('prop: onChange', () => {
    it('should get selected option from arguments', () => {
      const onChange = jest.fn();
      renderWithTheme(
        <Select onChange={onChange} defaultValue={0} options={options} />
      );
      fireEvent.mouseDown(screen.getByRole('button'));

      const option = screen.getAllByRole('option')[1];
      fireEvent.mouseEnter(option);
      fireEvent.click(option);

      expect(onChange).toHaveBeenCalledTimes(1);
      expect(onChange).toHaveBeenCalledWith(options[1], {
        fromEvent: expect.anything()
      });
    });
  });

  describe('prop: readOnly', () => {
    it('should not trigger any event with readOnly', () => {
      renderWithTheme(<Select readOnly defaultValue={10} options={options} />);
      screen.getByRole('button').focus();
      const focusedButton = document.activeElement as HTMLElement;
      fireEvent.keyDown(focusedButton, { key: 'ArrowDown' });
      expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
      fireEvent.keyUp(focusedButton, { key: 'ArrowDown' });
      expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
    });
  });

  describe('prop: value', () => {
    it('should select the option based on the value', () => {
      renderWithTheme(
        <Select defaultValue={20} options={options} open onOpen={noOp} />
      );
      const o = screen.getAllByRole('option');
      expect(o[0]).not.toHaveAttribute('aria-selected');
      expect(o[1]).toHaveAttribute('aria-selected', 'true');
      expect(o[2]).not.toHaveAttribute('aria-selected');
    });

    it('should select only the option that matches the object', () => {
      const obj1 = { id: 1 };
      const obj2 = { id: 2 };
      renderWithTheme(
        <Select
          open
          onOpen={noOp}
          defaultValue={obj1}
          options={[
            { label: '1', value: obj1 },
            { label: '2', value: obj2 }
          ]}
        />
      );
      const o = screen.getAllByRole('option');
      expect(o[0]).toHaveAttribute('aria-selected', 'true');
      expect(o[1]).not.toHaveAttribute('aria-selected');
    });

    it('should be able to use an object', () => {
      const value = {};
      renderWithTheme(
        <Select
          open
          onOpen={noOp}
          defaultValue={value}
          options={[...options, { value, label: 'object-label' }]}
        />
      );
      expect(screen.getByRole('button')).toHaveTextContent('object-label');
    });
  });

  describe('prop: open (controlled)', () => {
    // TODO add more tests
    it('should be open when initially true', () => {
      renderWithTheme(<Select open onOpen={noOp} options={options} />);
      expect(screen.getByRole('listbox')).toBeInTheDocument();
    });

    it('open only with the left mouse button click', () => {
      // Right/middle mouse click shouldn't open the Select
      renderWithTheme(<Select defaultValue={10} options={options} />);
      const trigger = screen.getByRole('button');
      // If clicked by the right/middle mouse button, no options list should be opened
      fireEvent.mouseDown(trigger, { button: 1 });
      expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
      fireEvent.mouseDown(trigger, { button: 2 });
      expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
    });
  });

  describe('prop: formatDisplay', () => {
    it('should use the prop to render the value', () => {
      const formatDisplay = (x: SelectOption<number>) =>
        `0b${Number(x.value).toString(2)}`;
      renderWithTheme(
        <Select
          formatDisplay={formatDisplay}
          options={[{ value: 2, label: '2' }]}
        />
      );
      expect(screen.getByRole('button')).toHaveTextContent('0b10');
    });
  });

  describe('prop: ref', () => {
    it('should be able to return the input node via a ref object', () => {
      const ref = React.createRef<SelectRef>();
      renderWithTheme(<Select ref={ref} defaultValue='' />);
      expect(ref.current?.node).toHaveProperty('tagName', 'INPUT');
    });

    it('should be able focus the trigger imperatively', () => {
      const ref = React.createRef<SelectRef>();
      renderWithTheme(<Select ref={ref} defaultValue='' />);
      ref.current?.focus();
      expect(screen.getByRole('button')).toHaveFocus();
    });
  });

  describe('spread props', () => {
    it('should apply additional props to trigger element', () => {
      renderWithTheme(
        <Select data-test='SelectDisplay' defaultValue={10} options={options} />
      );
      expect(screen.getByRole('button')).toHaveAttribute(
        'data-test',
        'SelectDisplay'
      );
    });
  });

  describe('keyboard', () => {
    it.each(['Space', 'ArrowUp', 'ArrowDown', 'Home', 'End'])(
      `should open menu when pressed %s key on select`,
      code => {
        renderWithTheme(
          <Select defaultValue='' options={[{ label: 'none', value: '' }]} />
        );
        screen.getByRole('button').focus();
        const focusedButton = document.activeElement as HTMLButtonElement;
        fireEvent.keyDown(focusedButton, { code });
        expect(
          screen.getByRole('listbox', { hidden: false })
        ).toBeInTheDocument();
        fireEvent.keyUp(focusedButton, { code });
        expect(
          screen.getByRole('listbox', { hidden: false })
        ).toBeInTheDocument();
      }
    );

    it('closes menu when pressing Escape', async () => {
      const onClose = jest.fn();
      renderWithTheme(
        <Select defaultValue={10} options={options} onClose={onClose} />
      );
      const button = screen.getByRole('button');
      fireEvent.mouseDown(button);

      const listbox = screen.getByRole('listbox');

      expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();

      fireEvent.keyDown(listbox, { code: 'ArrowDown' });
      expect(screen.getByRole('option', { name: 'twenty' })).toHaveFocus();

      fireEvent.keyDown(listbox, { code: 'Escape' });

      await waitFor(() => {
        expect(onClose).toHaveBeenCalled();
      });
      expect(listbox).not.toBeInTheDocument();

      expect(button).toHaveFocus();
    });

    it.each(['Enter', 'Space', 'Tab'])(
      'selects the active option by pressing %s, closes menu and maintains focus',
      async keyCode => {
        const onClose = jest.fn();
        const onKeyDown = jest.fn();
        renderWithTheme(
          // eslint-disable-next-line jsx-a11y/no-static-element-interactions
          <div onKeyDown={onKeyDown}>
            <Select defaultValue={10} options={options} onClose={onClose} />
          </div>
        );
        const button = screen.getByRole('button');
        fireEvent.mouseDown(button);

        const listbox = screen.getByRole('listbox');

        expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();

        fireEvent.keyDown(listbox, { code: 'ArrowDown' });
        expect(screen.getByRole('option', { name: 'twenty' })).toHaveFocus();

        fireEvent.keyDown(listbox, { code: keyCode });

        await waitFor(() => {
          expect(onClose).toHaveBeenCalled();
        });
        expect(listbox).not.toBeInTheDocument();

        expect(button).toHaveFocus();
        expect(onKeyDown).toHaveBeenCalledWith(
          expect.objectContaining({ defaultPrevented: true })
        );
      }
    );

    it('passes through Enter, Escape, Tab and Shift + Tab when closed', () => {
      const onKeyDown = jest.fn();
      renderWithTheme(
        // eslint-disable-next-line jsx-a11y/no-static-element-interactions
        <div onKeyDown={onKeyDown}>
          <Select defaultValue={10} options={options} />
        </div>
      );

      const button = screen.getByRole('button');

      const eventOptions = [
        { code: 'Enter' },
        { code: 'Escape' },
        { code: 'Tab' },
        { code: 'Tab', shiftKey: true }
      ];
      eventOptions.forEach(eventOption => {
        fireEvent.keyDown(button, eventOption);
        expect(onKeyDown).toHaveBeenCalledWith(
          expect.objectContaining({ defaultPrevented: false })
        );
      });
    });

    it('passes through keyDown events when modifier keys are pressed', () => {
      const onKeyDown = jest.fn();
      renderWithTheme(
        // eslint-disable-next-line jsx-a11y/no-static-element-interactions
        <div onKeyDown={onKeyDown}>
          <Select defaultValue={10} options={options} />
        </div>
      );

      const button = screen.getByRole('button');
      button.focus();

      const eventOptions = [
        { altKey: true },
        { ctrlKey: true },
        { metaKey: true },
        { shiftKey: true }
      ];
      eventOptions.forEach(eventOption => {
        fireEvent.keyDown(button, { ...eventOption, code: 'KeyT' });
        expect(button).toHaveTextContent('ten');
        expect(onKeyDown).toHaveBeenCalledWith(
          expect.objectContaining({ defaultPrevented: false })
        );
      });
    });

    it('moves options using ArrowUp, ArrowDown, Home and End', async () => {
      renderWithTheme(<Select defaultValue={10} options={options} />);
      const button = screen.getByRole('button');
      fireEvent.mouseDown(button);

      const listbox = screen.getByRole('listbox');

      expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();

      fireEvent.keyDown(listbox, { code: 'ArrowDown' });
      expect(screen.getByRole('option', { name: 'twenty' })).toHaveFocus();

      fireEvent.keyDown(listbox, { code: 'ArrowUp' });
      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();

      fireEvent.keyDown(listbox, { code: 'End' });
      expect(screen.getByRole('option', { name: 'thirty' })).toHaveFocus();

      fireEvent.keyDown(listbox, { code: 'Home' });
      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();
    });

    it('cycles through options when pressing the same key (open menu)', async () => {
      renderWithTheme(<Select defaultValue={10} options={options} />);
      const button = screen.getByRole('button');
      fireEvent.mouseDown(button);

      const listbox = screen.getByRole('listbox');

      expect(screen.getByRole('option', { name: 'ten' })).toBeInTheDocument();

      fireEvent.keyDown(listbox, { code: 'KeyT' });
      expect(screen.getByRole('option', { name: 'ten' })).toHaveFocus();

      fireEvent.keyDown(listbox, { code:
Download .txt
gitextract_rz85rgj9/

├── .babelrc
├── .codesandbox/
│   └── ci.json
├── .editorconfig
├── .eslintrc.js
├── .firebaserc
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── ci.yml
│       └── release.yml
├── .gitignore
├── .prettierrc
├── .storybook/
│   ├── decorators/
│   │   └── withGlobalStyle.tsx
│   ├── main.ts
│   ├── manager.css
│   ├── manager.ts
│   ├── preview.ts
│   ├── theme-picker/
│   │   ├── ThemeButton.tsx
│   │   ├── ThemeList.tsx
│   │   ├── ThemeProvider.tsx
│   │   ├── constants.ts
│   │   └── register.ts
│   └── theme.js
├── LICENSE
├── README.md
├── docs/
│   ├── Contributing.stories.mdx
│   ├── Getting-Started.stories.mdx
│   ├── Submit-your-Project.stories.mdx
│   └── Welcome.stories.mdx
├── firebase.json
├── jest.config.js
├── package.json
├── rollup.config.js
├── src/
│   ├── Anchor/
│   │   ├── Anchor.spec.tsx
│   │   ├── Anchor.stories.tsx
│   │   └── Anchor.tsx
│   ├── AppBar/
│   │   ├── AppBar.spec.tsx
│   │   ├── AppBar.stories.tsx
│   │   └── AppBar.tsx
│   ├── Avatar/
│   │   ├── Avatar.spec.tsx
│   │   ├── Avatar.stories.tsx
│   │   └── Avatar.tsx
│   ├── Button/
│   │   ├── Button.spec.tsx
│   │   ├── Button.stories.tsx
│   │   └── Button.tsx
│   ├── Checkbox/
│   │   ├── Checkbox.spec.tsx
│   │   ├── Checkbox.stories.tsx
│   │   └── Checkbox.tsx
│   ├── ColorInput/
│   │   ├── ColorInput.spec.tsx
│   │   ├── ColorInput.stories.tsx
│   │   └── ColorInput.tsx
│   ├── Counter/
│   │   ├── Counter.spec.tsx
│   │   ├── Counter.stories.tsx
│   │   ├── Counter.tsx
│   │   └── Digit.tsx
│   ├── DatePicker/
│   │   ├── DatePicker.stories.tsx
│   │   └── DatePicker.tsx
│   ├── Frame/
│   │   ├── Frame.spec.tsx
│   │   ├── Frame.stories.tsx
│   │   └── Frame.tsx
│   ├── GroupBox/
│   │   ├── GroupBox.spec.tsx
│   │   ├── GroupBox.stories.tsx
│   │   └── GroupBox.tsx
│   ├── Handle/
│   │   ├── Handle.spec.tsx
│   │   ├── Handle.stories.tsx
│   │   └── Handle.tsx
│   ├── Hourglass/
│   │   ├── Hourglass.spec.tsx
│   │   ├── Hourglass.stories.tsx
│   │   ├── Hourglass.tsx
│   │   └── base64hourglass.tsx
│   ├── MenuList/
│   │   ├── MenuList.spec.tsx
│   │   ├── MenuList.stories.tsx
│   │   ├── MenuList.tsx
│   │   ├── MenuListItem.spec.tsx
│   │   └── MenuListItem.tsx
│   ├── Monitor/
│   │   ├── Monitor.spec.tsx
│   │   ├── Monitor.stories.tsx
│   │   └── Monitor.tsx
│   ├── NumberInput/
│   │   ├── NumberInput.spec.tsx
│   │   ├── NumberInput.stories.tsx
│   │   └── NumberInput.tsx
│   ├── ProgressBar/
│   │   ├── ProgressBar.spec.tsx
│   │   ├── ProgressBar.stories.tsx
│   │   └── ProgressBar.tsx
│   ├── Radio/
│   │   ├── Radio.spec.tsx
│   │   ├── Radio.stories.tsx
│   │   └── Radio.tsx
│   ├── ScrollView/
│   │   ├── ScrollView.spec.tsx
│   │   ├── ScrollView.stories.tsx
│   │   └── ScrollView.tsx
│   ├── Select/
│   │   ├── Select.spec.tsx
│   │   ├── Select.stories.data.ts
│   │   ├── Select.stories.tsx
│   │   ├── Select.styles.tsx
│   │   ├── Select.tsx
│   │   ├── Select.types.ts
│   │   ├── SelectNative.spec.tsx
│   │   ├── SelectNative.tsx
│   │   ├── useSelectCommon.tsx
│   │   └── useSelectState.ts
│   ├── Separator/
│   │   ├── Separator.spec.tsx
│   │   ├── Separator.stories.tsx
│   │   └── Separator.tsx
│   ├── Slider/
│   │   ├── Slider.spec.tsx
│   │   ├── Slider.stories.tsx
│   │   └── Slider.tsx
│   ├── Table/
│   │   ├── Table.spec.tsx
│   │   ├── Table.stories.tsx
│   │   ├── Table.tsx
│   │   ├── TableBody.spec.tsx
│   │   ├── TableBody.tsx
│   │   ├── TableDataCell.spec.tsx
│   │   ├── TableDataCell.tsx
│   │   ├── TableHead.spec.tsx
│   │   ├── TableHead.tsx
│   │   ├── TableHeadCell.spec.tsx
│   │   ├── TableHeadCell.tsx
│   │   ├── TableRow.spec.tsx
│   │   └── TableRow.tsx
│   ├── Tabs/
│   │   ├── Tab.spec.tsx
│   │   ├── Tab.tsx
│   │   ├── TabBody.spec.tsx
│   │   ├── TabBody.tsx
│   │   ├── Tabs.spec.tsx
│   │   ├── Tabs.stories.tsx
│   │   └── Tabs.tsx
│   ├── TextInput/
│   │   ├── TextInput.spec.tsx
│   │   ├── TextInput.stories.tsx
│   │   └── TextInput.tsx
│   ├── Toolbar/
│   │   ├── Toolbar.spec.tsx
│   │   └── Toolbar.tsx
│   ├── Tooltip/
│   │   ├── Tooltip.spec.tsx
│   │   ├── Tooltip.stories.tsx
│   │   └── Tooltip.tsx
│   ├── TreeView/
│   │   ├── TreeView.spec.tsx
│   │   ├── TreeView.stories.tsx
│   │   └── TreeView.tsx
│   ├── Window/
│   │   ├── Window.spec.tsx
│   │   ├── Window.stories.tsx
│   │   ├── Window.tsx
│   │   ├── WindowContent.spec.tsx
│   │   ├── WindowContent.tsx
│   │   ├── WindowHeader.spec.tsx
│   │   └── WindowHeader.tsx
│   ├── assets/
│   │   ├── fonts/
│   │   │   └── src/
│   │   │       ├── ms-sans-serif/
│   │   │       │   ├── license.txt
│   │   │       │   └── readme.txt
│   │   │       └── ms-sans-serif-bold/
│   │   │           ├── license.txt
│   │   │           └── readme.txt
│   │   └── images/
│   │       └── logo.psd
│   ├── common/
│   │   ├── SwitchBase.ts
│   │   ├── constants.ts
│   │   ├── hooks/
│   │   │   ├── useControlledOrUncontrolled.ts
│   │   │   ├── useEventCallback.ts
│   │   │   ├── useForkRef.spec.tsx
│   │   │   ├── useForkRef.ts
│   │   │   ├── useId.spec.ts
│   │   │   ├── useId.ts
│   │   │   └── useIsFocusVisible.ts
│   │   ├── index.ts
│   │   ├── styleReset.ts
│   │   ├── system.ts
│   │   ├── themes/
│   │   │   ├── aiee.ts
│   │   │   ├── ash.ts
│   │   │   ├── azureOrange.ts
│   │   │   ├── bee.ts
│   │   │   ├── blackAndWhite.ts
│   │   │   ├── blue.ts
│   │   │   ├── brick.ts
│   │   │   ├── candy.ts
│   │   │   ├── cherry.ts
│   │   │   ├── coldGray.ts
│   │   │   ├── counterStrike.ts
│   │   │   ├── darkTeal.ts
│   │   │   ├── denim.ts
│   │   │   ├── eggplant.ts
│   │   │   ├── fxDev.ts
│   │   │   ├── highContrast.ts
│   │   │   ├── honey.ts
│   │   │   ├── hotChocolate.ts
│   │   │   ├── hotdogStand.ts
│   │   │   ├── index.ts
│   │   │   ├── lilac.ts
│   │   │   ├── lilacRoseDark.ts
│   │   │   ├── maple.ts
│   │   │   ├── marine.ts
│   │   │   ├── matrix.ts
│   │   │   ├── millenium.ts
│   │   │   ├── modernDark.ts
│   │   │   ├── molecule.ts
│   │   │   ├── monochrome.ts
│   │   │   ├── ninjaTurtles.ts
│   │   │   ├── olive.ts
│   │   │   ├── original.ts
│   │   │   ├── pamelaAnderson.ts
│   │   │   ├── peggysPastels.ts
│   │   │   ├── plum.ts
│   │   │   ├── polarized.ts
│   │   │   ├── powerShell.ts
│   │   │   ├── rainyDay.ts
│   │   │   ├── raspberry.ts
│   │   │   ├── redWine.ts
│   │   │   ├── rose.ts
│   │   │   ├── seawater.ts
│   │   │   ├── shelbiTeal.ts
│   │   │   ├── slate.ts
│   │   │   ├── solarizedDark.ts
│   │   │   ├── solarizedLight.ts
│   │   │   ├── spruce.ts
│   │   │   ├── stormClouds.ts
│   │   │   ├── theSixtiesUSA.ts
│   │   │   ├── tokyoDark.ts
│   │   │   ├── toner.ts
│   │   │   ├── tooSexy.ts
│   │   │   ├── travel.ts
│   │   │   ├── types.ts
│   │   │   ├── vaporTeal.ts
│   │   │   ├── vermillion.ts
│   │   │   ├── violetDark.ts
│   │   │   ├── vistaesqueMidnight.ts
│   │   │   ├── water.ts
│   │   │   ├── white.ts
│   │   │   ├── windows1.ts
│   │   │   └── wmii.ts
│   │   └── utils/
│   │       ├── events.spec.tsx
│   │       ├── events.ts
│   │       ├── index.spec.ts
│   │       └── index.ts
│   ├── index.ts
│   ├── legacy/
│   │   ├── Bar.tsx
│   │   ├── Cutout.tsx
│   │   ├── Desktop.tsx
│   │   ├── Divider.tsx
│   │   ├── Fieldset.tsx
│   │   ├── List.tsx
│   │   ├── ListItem.tsx
│   │   ├── NumberField.tsx
│   │   ├── Panel.tsx
│   │   ├── Progress.tsx
│   │   ├── TextField.tsx
│   │   └── Tree.tsx
│   └── types.ts
├── test/
│   ├── setup-test.ts
│   └── utils.tsx
├── tsconfig.build.index.json
├── tsconfig.build.themes.json
├── tsconfig.json
└── types/
    ├── globals.d.ts
    └── themes.d.ts
Download .txt
SYMBOL INDEX (199 symbols across 108 files)

FILE: .storybook/theme-picker/ThemeButton.tsx
  function ThemeButton (line 6) | function ThemeButton({

FILE: .storybook/theme-picker/ThemeList.tsx
  type ThemesProps (line 38) | type ThemesProps = {
  function ThemeList (line 51) | function ThemeList({ active }: ThemesProps) {

FILE: .storybook/theme-picker/constants.ts
  constant THEMES_ID (line 1) | const THEMES_ID = 'storybook/themes';

FILE: rollup.config.js
  constant NODE_ENV (line 6) | const NODE_ENV = process.env.NODE_ENV || 'development';

FILE: src/Anchor/Anchor.stories.tsx
  function Default (line 17) | function Default() {

FILE: src/Anchor/Anchor.tsx
  type AnchorProps (line 6) | type AnchorProps = {

FILE: src/AppBar/AppBar.stories.tsx
  function Default (line 26) | function Default() {

FILE: src/AppBar/AppBar.tsx
  type AppBarProps (line 7) | type AppBarProps = {

FILE: src/Avatar/Avatar.stories.tsx
  function Default (line 20) | function Default() {

FILE: src/Avatar/Avatar.tsx
  type AvatarProps (line 6) | type AvatarProps = {

FILE: src/Button/Button.stories.tsx
  function Default (line 37) | function Default() {
  function Raised (line 66) | function Raised() {
  function Flat (line 107) | function Flat() {
  function Thin (line 145) | function Thin() {

FILE: src/Button/Button.tsx
  type ButtonProps (line 15) | type ButtonProps = {
  type StyledButtonProps (line 41) | type StyledButtonProps = Pick<

FILE: src/Checkbox/Checkbox.stories.tsx
  function Default (line 27) | function Default() {
  function Flat (line 127) | function Flat() {

FILE: src/Checkbox/Checkbox.tsx
  type CheckboxProps (line 16) | type CheckboxProps = {
  type CheckmarkProps (line 41) | type CheckmarkProps = {

FILE: src/ColorInput/ColorInput.spec.tsx
  function rgb2hex (line 6) | function rgb2hex(str: string) {

FILE: src/ColorInput/ColorInput.stories.tsx
  function Default (line 36) | function Default() {
  function Flat (line 51) | function Flat() {

FILE: src/ColorInput/ColorInput.tsx
  type ColorInputProps (line 10) | type ColorInputProps = {

FILE: src/Counter/Counter.stories.tsx
  function Default (line 28) | function Default() {

FILE: src/Counter/Counter.tsx
  type CounterProps (line 8) | type CounterProps = {

FILE: src/Counter/Digit.tsx
  type DigitProps (line 7) | type DigitProps = {
  function Digit (line 176) | function Digit({ digit = 0, pixelSize = 2, ...otherProps }: DigitProps) {

FILE: src/DatePicker/DatePicker.stories.tsx
  function Default (line 18) | function Default() {

FILE: src/DatePicker/DatePicker.tsx
  type DatePickerProps (line 11) | type DatePickerProps = {
  function daysInMonth (line 72) | function daysInMonth(year: number, month: number) {
  function dayIndex (line 76) | function dayIndex(year: number, month: number, day: number) {
  function convertDateToState (line 80) | function convertDateToState(stringDate: string) {

FILE: src/Frame/Frame.stories.tsx
  function Default (line 27) | function Default() {

FILE: src/Frame/Frame.tsx
  type FrameProps (line 6) | type FrameProps = {

FILE: src/GroupBox/GroupBox.stories.tsx
  function Default (line 17) | function Default() {
  function Flat (line 43) | function Flat() {
  function ToggleExample (line 73) | function ToggleExample() {

FILE: src/GroupBox/GroupBox.tsx
  type GroupBoxProps (line 6) | type GroupBoxProps = {

FILE: src/Handle/Handle.stories.tsx
  function Default (line 17) | function Default() {

FILE: src/Handle/Handle.tsx
  type HandleProps (line 6) | type HandleProps = {

FILE: src/Hourglass/Hourglass.stories.tsx
  function Default (line 17) | function Default() {

FILE: src/Hourglass/Hourglass.tsx
  type HourglassProps (line 7) | type HourglassProps = {

FILE: src/MenuList/MenuList.stories.tsx
  function Default (line 23) | function Default() {

FILE: src/MenuList/MenuList.tsx
  type MenuListProps (line 7) | type MenuListProps = React.HTMLAttributes<HTMLUListElement> & {

FILE: src/MenuList/MenuListItem.tsx
  type MenuListItemProps (line 8) | type MenuListItemProps = {

FILE: src/Monitor/Monitor.stories.tsx
  function Default (line 17) | function Default() {

FILE: src/Monitor/Monitor.tsx
  type MonitorProps (line 6) | type MonitorProps = {

FILE: src/NumberInput/NumberInput.stories.tsx
  function Default (line 29) | function Default() {
  function Flat (line 45) | function Flat() {

FILE: src/NumberInput/NumberInput.tsx
  type NumberInputProps (line 11) | type NumberInputProps = {

FILE: src/ProgressBar/ProgressBar.stories.tsx
  function Default (line 17) | function Default() {
  function Tile (line 42) | function Tile() {
  function HideValue (line 67) | function HideValue() {

FILE: src/ProgressBar/ProgressBar.tsx
  type ProgressBarProps (line 14) | type ProgressBarProps = {

FILE: src/Radio/Radio.stories.tsx
  function Default (line 26) | function Default() {
  function Flat (line 77) | function Flat() {

FILE: src/Radio/Radio.tsx
  type RadioVariant (line 14) | type RadioVariant = 'default' | 'flat';
  type RadioProps (line 16) | type RadioProps = {
  type StyledCheckboxProps (line 42) | type StyledCheckboxProps = {
  type IconProps (line 81) | type IconProps = {

FILE: src/ScrollView/ScrollView.stories.tsx
  function Default (line 17) | function Default() {

FILE: src/ScrollView/ScrollView.tsx
  type ScrollViewProps (line 6) | type ScrollViewProps = {

FILE: src/Select/Select.stories.tsx
  function Default (line 58) | function Default() {
  function Flat (line 107) | function Flat() {
  function CustomDisplayFormatting (line 157) | function CustomDisplayFormatting() {

FILE: src/Select/Select.styles.tsx
  type CommonSelectStyleProps (line 16) | type CommonSelectStyleProps = {

FILE: src/Select/Select.tsx
  type SelectProps (line 22) | type SelectProps<T> = SelectInnerProps<T> &
  function SelectInnerOption (line 29) | function SelectInnerOption<T>({
  function SelectInner (line 76) | function SelectInner<T>(

FILE: src/Select/Select.types.ts
  type SelectChangeEventTargetValue (line 4) | type SelectChangeEventTargetValue<T> = { value: T; name: string | undefi...
  type SelectChangeEvent (line 6) | type SelectChangeEvent<T> =
  type SelectOption (line 19) | type SelectOption<T> = {
  type SelectRef (line 24) | type SelectRef = Pick<HTMLInputElement, 'value' | 'focus'> & {
  type SelectVariants (line 28) | type SelectVariants = 'default' | 'flat';
  type SelectFormatDisplayCallback (line 30) | type SelectFormatDisplayCallback<T> = (
  type SelectCommonProps (line 34) | type SelectCommonProps<T> = {
  type SelectInnerProps (line 56) | type SelectInnerProps<T> = {

FILE: src/Select/SelectNative.tsx
  type SelectNativeProps (line 9) | type SelectNativeProps = SelectCommonProps<string> &

FILE: src/Select/useSelectState.ts
  constant TYPING_RESET_DELAY (line 15) | const TYPING_RESET_DELAY = 1000;

FILE: src/Separator/Separator.stories.tsx
  function Default (line 18) | function Default() {

FILE: src/Separator/Separator.tsx
  type SeparatorProps (line 5) | type SeparatorProps = {

FILE: src/Slider/Slider.spec.tsx
  function createTouches (line 8) | function createTouches(

FILE: src/Slider/Slider.stories.tsx
  function Default (line 43) | function Default() {
  function Flat (line 122) | function Flat() {

FILE: src/Slider/Slider.tsx
  type SliderOnChangeHandler (line 27) | type SliderOnChangeHandler = (value: number) => void;
  type SliderProps (line 29) | type SliderProps = {
  function percentToValue (line 50) | function percentToValue(percent: number, min: number, max: number) {
  function trackFinger (line 54) | function trackFinger(
  function ownerDocument (line 82) | function ownerDocument(node?: Element) {
  function findClosest (line 86) | function findClosest(values: number[], currentValue: number) {
  type StyledSliderProps (line 111) | type StyledSliderProps = Pick<

FILE: src/Table/Table.stories.tsx
  function Default (line 35) | function Default() {

FILE: src/Table/Table.tsx
  type TableProps (line 6) | type TableProps = {

FILE: src/Table/TableBody.spec.tsx
  function mountInTable (line 8) | function mountInTable(node: React.ReactNode) {

FILE: src/Table/TableBody.tsx
  type TableBodyProps (line 6) | type TableBodyProps = {

FILE: src/Table/TableDataCell.spec.tsx
  function mountInTable (line 8) | function mountInTable(node: React.ReactNode) {

FILE: src/Table/TableDataCell.tsx
  type TableDataCellProps (line 5) | type TableDataCellProps = {

FILE: src/Table/TableHead.spec.tsx
  function mountInTable (line 8) | function mountInTable(node: React.ReactNode) {

FILE: src/Table/TableHead.tsx
  type TableHeadProps (line 5) | type TableHeadProps = {

FILE: src/Table/TableHeadCell.spec.tsx
  function mountInTable (line 8) | function mountInTable(node: React.ReactNode) {

FILE: src/Table/TableHeadCell.tsx
  type TableHeadCellProps (line 7) | type TableHeadCellProps = {

FILE: src/Table/TableRow.spec.tsx
  function mountInTable (line 7) | function mountInTable(node: React.ReactNode) {

FILE: src/Table/TableRow.tsx
  type TableRowProps (line 5) | type TableRowProps = {

FILE: src/Tabs/Tab.tsx
  type TabProps (line 8) | type TabProps = {

FILE: src/Tabs/TabBody.tsx
  type TabBodyProps (line 7) | type TabBodyProps = {

FILE: src/Tabs/Tabs.stories.tsx
  function Default (line 29) | function Default() {
  function MultiRow (line 89) | function MultiRow() {

FILE: src/Tabs/Tabs.tsx
  type TabsProps (line 8) | type TabsProps = {
  function splitToChunks (line 51) | function splitToChunks<T>(array: T[], parts: number) {

FILE: src/TextInput/TextInput.stories.tsx
  function Default (line 27) | function Default() {
  function Flat (line 69) | function Flat() {

FILE: src/TextInput/TextInput.tsx
  type TextInputInputProps (line 13) | type TextInputInputProps = {
  type TextInputTextAreaProps (line 23) | type TextInputTextAreaProps = {
  type TextInputProps (line 31) | type TextInputProps = {
  type WrapperProps (line 42) | type WrapperProps = Pick<TextInputProps, 'fullWidth' | 'variant'> &
  type InputProps (line 68) | type InputProps = Pick<TextInputProps, 'disabled' | 'fullWidth' | 'varia...

FILE: src/Toolbar/Toolbar.tsx
  type ToolbarProps (line 4) | type ToolbarProps = {

FILE: src/Tooltip/Tooltip.stories.tsx
  function Default (line 17) | function Default() {

FILE: src/Tooltip/Tooltip.tsx
  type TooltipPosition (line 8) | type TooltipPosition = 'top' | 'bottom' | 'left' | 'right';
  type TooltipProps (line 10) | type TooltipProps = {

FILE: src/TreeView/TreeView.stories.tsx
  function getIds (line 89) | function getIds(item: TreeLeaf<string>) {
  function Basic (line 97) | function Basic() {
  function Controlled (line 111) | function Controlled() {
  function Disabled (line 144) | function Disabled() {
  function DisabledTreeItems (line 158) | function DisabledTreeItems() {

FILE: src/TreeView/TreeView.tsx
  type TreeLeaf (line 8) | type TreeLeaf<T> = {
  type TreeViewProps (line 16) | type TreeViewProps<T> = {
  type TreeBranchProps (line 32) | type TreeBranchProps<T> = {
  function toggleItem (line 211) | function toggleItem<T>(state: T[], id: T) {
  function preventDefault (line 217) | function preventDefault(event: React.SyntheticEvent) {
  function TreeBranch (line 221) | function TreeBranch<T>({
  function TreeInner (line 308) | function TreeInner<T>(

FILE: src/Window/Window.stories.tsx
  function Default (line 72) | function Default() {

FILE: src/Window/Window.tsx
  type WindowProps (line 6) | type WindowProps = {

FILE: src/Window/WindowContent.tsx
  type WindowContentProps (line 5) | type WindowContentProps = {

FILE: src/Window/WindowHeader.tsx
  type WindowHeaderProps (line 6) | type WindowHeaderProps = {

FILE: src/common/constants.ts
  constant KEYBOARD_KEY_CODES (line 1) | const KEYBOARD_KEY_CODES = {

FILE: src/common/hooks/useControlledOrUncontrolled.ts
  function useControlledOrUncontrolled (line 3) | function useControlledOrUncontrolled<T>({

FILE: src/common/hooks/useEventCallback.ts
  function useEventCallback (line 9) | function useEventCallback<Args extends unknown[], Return>(

FILE: src/common/hooks/useForkRef.spec.tsx
  function Component (line 18) | function Component(props: { innerRef: React.RefObject<HTMLDivElement> }) {
  type OuterProps (line 56) | type OuterProps = {
  function Inner (line 72) | function Inner() {
  function Div (line 86) | function Div(

FILE: src/common/hooks/useForkRef.ts
  function setRef (line 5) | function setRef<T>(
  function useForkRef (line 17) | function useForkRef<T>(

FILE: src/common/hooks/useId.ts
  function makeId (line 3) | function makeId() {

FILE: src/common/hooks/useIsFocusVisible.ts
  function focusTriggersKeyboardModality (line 34) | function focusTriggersKeyboardModality(
  function handleKeyDown (line 63) | function handleKeyDown(event: KeyboardEvent) {
  function handlePointerDown (line 77) | function handlePointerDown() {
  function handleVisibilityChange (line 81) | function handleVisibilityChange(this: Document) {
  function prepare (line 93) | function prepare(doc: Document) {
  function teardown (line 101) | function teardown(doc: Document) {
  function isFocusVisible (line 109) | function isFocusVisible(event: React.FocusEvent) {
  function handleBlurVisible (line 128) | function handleBlurVisible() {
  function useIsFocusVisible (line 140) | function useIsFocusVisible<T extends Element = HTMLElement>() {

FILE: src/common/index.ts
  type BorderStyles (line 66) | type BorderStyles =
  type BorderStyle (line 76) | type BorderStyle = {

FILE: src/common/themes/types.ts
  type Color (line 1) | type Color = string;
  type Theme (line 3) | type Theme = {
  type WindowsTheme (line 37) | type WindowsTheme = {

FILE: src/common/utils/events.spec.tsx
  type FireEventMap (line 9) | type FireEventMap = Record<string, EventType>;

FILE: src/common/utils/events.ts
  function isReactFocusEvent (line 28) | function isReactFocusEvent<T>(
  function isReactKeyboardEvent (line 34) | function isReactKeyboardEvent<T>(
  function isReactMouseEvent (line 40) | function isReactMouseEvent<T>(

FILE: src/common/utils/index.ts
  function clamp (line 5) | function clamp(value: number, min: number | null, max: number | null) {
  function linearGradient (line 15) | function linearGradient(left: string, right: string) {
  function mapFromWindowsTheme (line 19) | function mapFromWindowsTheme(
  function getDecimalPrecision (line 88) | function getDecimalPrecision(num: number) {
  function roundValueToStep (line 102) | function roundValueToStep(value: number, step: number, min: number) {
  function getSize (line 107) | function getSize(value: string | number) {

FILE: src/legacy/Bar.tsx
  type BarProps (line 4) | type BarProps = HandleProps;

FILE: src/legacy/Cutout.tsx
  type CutoutProps (line 4) | type CutoutProps = ScrollViewProps;

FILE: src/legacy/Desktop.tsx
  type DesktopProps (line 4) | type DesktopProps = MonitorProps;

FILE: src/legacy/Divider.tsx
  type DividerProps (line 4) | type DividerProps = SeparatorProps;

FILE: src/legacy/Fieldset.tsx
  type FieldsetProps (line 4) | type FieldsetProps = GroupBoxProps;

FILE: src/legacy/List.tsx
  type ListProps (line 4) | type ListProps = MenuListProps;

FILE: src/legacy/ListItem.tsx
  type ListItemProps (line 8) | type ListItemProps = MenuListItemProps;

FILE: src/legacy/NumberField.tsx
  type NumberFieldProps (line 4) | type NumberFieldProps = NumberInputProps;

FILE: src/legacy/Panel.tsx
  type PanelProps (line 4) | type PanelProps = FrameProps;

FILE: src/legacy/Progress.tsx
  type ProgressProps (line 4) | type ProgressProps = ProgressBarProps;

FILE: src/legacy/TextField.tsx
  type TextFieldProps (line 4) | type TextFieldProps = TextInputProps;

FILE: src/legacy/Tree.tsx
  type TreeProps (line 4) | type TreeProps<T> = TreeViewProps<T>;

FILE: src/types.ts
  type Sizes (line 5) | type Sizes = 'sm' | 'md' | 'lg';
  type Orientation (line 7) | type Orientation = 'horizontal' | 'vertical';
  type Direction (line 9) | type Direction = 'up' | 'down' | 'left' | 'right';
  type DimensionValue (line 11) | type DimensionValue = undefined | number | string;
  type CommonStyledProps (line 13) | type CommonStyledProps = {
  type HTMLDataAttributes (line 22) | type HTMLDataAttributes = Record<`data-${string}`, any>;
  type CommonThemeProps (line 24) | type CommonThemeProps = {

FILE: test/utils.tsx
  class Touch (line 12) | class Touch {
    method constructor (line 23) | constructor({
    method identifier (line 43) | get identifier() {
    method pageX (line 47) | get pageX() {
    method pageY (line 51) | get pageY() {
    method clientX (line 55) | get clientX() {
    method clientY (line 59) | get clientY() {

FILE: types/themes.d.ts
  type DefaultTheme (line 7) | interface DefaultTheme extends Theme {}
Condensed preview — 246 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (510K chars).
[
  {
    "path": ".babelrc",
    "chars": 1323,
    "preview": "{\n  \"sourceType\": \"unambiguous\",\n  \"presets\": [\n    [\n      \"@babel/preset-env\",\n      {\n        \"shippedProposals\": tru"
  },
  {
    "path": ".codesandbox/ci.json",
    "chars": 102,
    "preview": "{\n  \"buildCommand\": \"build:prod\",\n  \"node\": \"16\",\n  \"sandboxes\": [\n    \"react95-template-xkfj0\"\n  ]\n}\n"
  },
  {
    "path": ".editorconfig",
    "chars": 145,
    "preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = yes\nin"
  },
  {
    "path": ".eslintrc.js",
    "chars": 2057,
    "preview": "module.exports = {\n  extends: [\n    'plugin:@typescript-eslint/recommended',\n    'airbnb',\n    'plugin:prettier/recommen"
  },
  {
    "path": ".firebaserc",
    "chars": 59,
    "preview": "{\n  \"projects\": {\n    \"default\": \"react95-storybook\"\n  }\n}\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 320,
    "preview": "# These are supported funding model platforms\n\ngithub: arturbien\npatreon: arturbien\nopen_collective: # Replace with a si"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 3099,
    "preview": "name: CI\n\non:\n  pull_request:\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Git Checkout\n        us"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 1830,
    "preview": "name: Release\n\non:\n  push:\n    branches:\n      - master\n      - next\n      - beta\n      - alpha\n      - '*.x' # maintena"
  },
  {
    "path": ".gitignore",
    "chars": 406,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
  },
  {
    "path": ".prettierrc",
    "chars": 380,
    "preview": "{\n  \"arrowParens\": \"avoid\",\n  \"bracketSpacing\": true,\n  \"htmlWhitespaceSensitivity\": \"css\",\n  \"insertPragma\": false,\n  \""
  },
  {
    "path": ".storybook/decorators/withGlobalStyle.tsx",
    "chars": 982,
    "preview": "import { DecoratorFn } from '@storybook/react';\nimport React from 'react';\nimport { createGlobalStyle } from 'styled-com"
  },
  {
    "path": ".storybook/main.ts",
    "chars": 1209,
    "preview": "import type { StorybookConfig } from '@storybook/react/types';\nimport type { PropItem } from 'react-docgen-typescript';\n"
  },
  {
    "path": ".storybook/manager.css",
    "chars": 283,
    "preview": "/* Remove from the sidebar menu stories that contains \"unstable\" */\na[data-item-id$='-unstable'].sidebar-item,\na[data-it"
  },
  {
    "path": ".storybook/manager.ts",
    "chars": 130,
    "preview": "import './manager.css';\n\nimport { addons } from '@storybook/addons';\nimport theme from './theme';\n\naddons.setConfig({\n  "
  },
  {
    "path": ".storybook/preview.ts",
    "chars": 658,
    "preview": "import { DecoratorFn, Parameters } from '@storybook/react';\nimport { withGlobalStyle } from './decorators/withGlobalStyl"
  },
  {
    "path": ".storybook/theme-picker/ThemeButton.tsx",
    "chars": 575,
    "preview": "import React, { useCallback } from 'react';\nimport { ThemeProvider } from 'styled-components';\nimport { Button } from '."
  },
  {
    "path": ".storybook/theme-picker/ThemeList.tsx",
    "chars": 1522,
    "preview": "import { useAddonState } from '@storybook/api';\nimport React, { useCallback } from 'react';\nimport styled from 'styled-c"
  },
  {
    "path": ".storybook/theme-picker/ThemeProvider.tsx",
    "chars": 516,
    "preview": "import { useAddonState } from '@storybook/client-api';\nimport { DecoratorFn } from '@storybook/react';\nimport React from"
  },
  {
    "path": ".storybook/theme-picker/constants.ts",
    "chars": 45,
    "preview": "export const THEMES_ID = 'storybook/themes';\n"
  },
  {
    "path": ".storybook/theme-picker/register.ts",
    "chars": 446,
    "preview": "import addons, { makeDecorator, types } from '@storybook/addons';\nimport { THEMES_ID } from './constants';\nimport { Them"
  },
  {
    "path": ".storybook/theme.js",
    "chars": 741,
    "preview": "import { create } from '@storybook/theming';\n\nimport brandImage from './logo.png';\n\nexport default create({\n  base: 'lig"
  },
  {
    "path": "LICENSE",
    "chars": 1067,
    "preview": "MIT License\n\nCopyright (c) 2019 Artur Bień\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
  },
  {
    "path": "README.md",
    "chars": 3680,
    "preview": "<h1 align=\"center\">React95</h1>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/react95\"><img src=\"https://"
  },
  {
    "path": "docs/Contributing.stories.mdx",
    "chars": 741,
    "preview": "import { Meta } from '@storybook/addon-docs';\n\n<Meta title='Docs/Contributing' />\n\n# Contributing\n\nAny help from UI/UX d"
  },
  {
    "path": "docs/Getting-Started.stories.mdx",
    "chars": 1924,
    "preview": "import { Meta } from '@storybook/addon-docs';\n\n<Meta title='Docs/Getting Started' />\n\n# Installation\n\nReact95 is availab"
  },
  {
    "path": "docs/Submit-your-Project.stories.mdx",
    "chars": 330,
    "preview": "import { Meta } from '@storybook/addon-docs';\n\n<Meta title='Docs/Submit your Project' />\n\n# Submit your Project\n\nApps bu"
  },
  {
    "path": "docs/Welcome.stories.mdx",
    "chars": 1754,
    "preview": "import { Meta } from '@storybook/addon-docs';\n\n<Meta title='Docs/Welcome to React95' />\n\n# Welcome to React95\n\n<a href='"
  },
  {
    "path": "firebase.json",
    "chars": 239,
    "preview": "{\n  \"hosting\": {\n    \"public\": \"storybook\",\n    \"ignore\": [\n      \"firebase.json\",\n      \"**/.*\",\n      \"**/node_modules"
  },
  {
    "path": "jest.config.js",
    "chars": 280,
    "preview": "module.exports = {\n  globals: {\n    'ts-jest': {\n      diagnostics: false,\n      isolatedModules: true\n    }\n  },\n  cove"
  },
  {
    "path": "package.json",
    "chars": 4919,
    "preview": "{\n  \"name\": \"react95\",\n  \"version\": \"0.0.0-development\",\n  \"description\": \"Refreshed Windows95 UI components for modern "
  },
  {
    "path": "rollup.config.js",
    "chars": 1710,
    "preview": "import typescript from '@rollup/plugin-typescript';\nimport copy from 'rollup-plugin-copy';\nimport esbuild from 'rollup-p"
  },
  {
    "path": "src/Anchor/Anchor.spec.tsx",
    "chars": 1247,
    "preview": "import React from 'react';\n\nimport { render } from '@testing-library/react';\n\nimport { Anchor } from './Anchor';\n\nconst "
  },
  {
    "path": "src/Anchor/Anchor.stories.tsx",
    "chars": 647,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Anchor } from 'react95';\nimport st"
  },
  {
    "path": "src/Anchor/Anchor.tsx",
    "chars": 879,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\n\nimport { CommonStyledProps } from '."
  },
  {
    "path": "src/AppBar/AppBar.spec.tsx",
    "chars": 1845,
    "preview": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { AppBar } from './AppBar';\n\nconst d"
  },
  {
    "path": "src/AppBar/AppBar.stories.tsx",
    "chars": 2110,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport {\n  AppBar,\n  Button,\n"
  },
  {
    "path": "src/AppBar/AppBar.tsx",
    "chars": 1121,
    "preview": "import React, { forwardRef } from 'react';\nimport styled, { CSSProperties } from 'styled-components';\n\nimport { createBo"
  },
  {
    "path": "src/Avatar/Avatar.spec.tsx",
    "chars": 2614,
    "preview": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { renderWithTheme, theme } from '../"
  },
  {
    "path": "src/Avatar/Avatar.stories.tsx",
    "chars": 933,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Avatar } from 'react95';\nimport st"
  },
  {
    "path": "src/Avatar/Avatar.tsx",
    "chars": 1859,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { getSize } from '../common/ut"
  },
  {
    "path": "src/Button/Button.spec.tsx",
    "chars": 3899,
    "preview": "import { fireEvent, render } from '@testing-library/react';\nimport React from 'react';\n\nimport { renderWithTheme, theme "
  },
  {
    "path": "src/Button/Button.stories.tsx",
    "chars": 5063,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport {\n  Button,\n  MenuList"
  },
  {
    "path": "src/Button/Button.tsx",
    "chars": 5717,
    "preview": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport {\n  createBorderStyle"
  },
  {
    "path": "src/Checkbox/Checkbox.spec.tsx",
    "chars": 4633,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { Checkbox } from './Checkbox';\n\n"
  },
  {
    "path": "src/Checkbox/Checkbox.stories.tsx",
    "chars": 5792,
    "preview": "import React, { useState } from 'react';\nimport styled from 'styled-components';\n\nimport { ComponentMeta } from '@storyb"
  },
  {
    "path": "src/Checkbox/Checkbox.tsx",
    "chars": 4860,
    "preview": "import React, { forwardRef, useCallback } from 'react';\nimport styled, { css } from 'styled-components';\n\nimport { creat"
  },
  {
    "path": "src/ColorInput/ColorInput.spec.tsx",
    "chars": 2108,
    "preview": "import { fireEvent } from '@testing-library/react';\nimport React from 'react';\nimport { renderWithTheme } from '../../te"
  },
  {
    "path": "src/ColorInput/ColorInput.stories.tsx",
    "chars": 1400,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport styled from 'styled-components';\n\nim"
  },
  {
    "path": "src/ColorInput/ColorInput.tsx",
    "chars": 4190,
    "preview": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { StyledButton } from"
  },
  {
    "path": "src/Counter/Counter.spec.tsx",
    "chars": 1559,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Counter } from './Counter';\n\nd"
  },
  {
    "path": "src/Counter/Counter.stories.tsx",
    "chars": 1060,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport { Button, Counter, Fra"
  },
  {
    "path": "src/Counter/Counter.tsx",
    "chars": 1111,
    "preview": "import React, { forwardRef, useMemo } from 'react';\nimport styled from 'styled-components';\n\nimport { createBorderStyles"
  },
  {
    "path": "src/Counter/Digit.tsx",
    "chars": 4833,
    "preview": "import React from 'react';\nimport styled, { css } from 'styled-components';\n\nimport { createHatchedBackground } from '.."
  },
  {
    "path": "src/DatePicker/DatePicker.stories.tsx",
    "chars": 657,
    "preview": "/* eslint-disable camelcase, react/jsx-pascal-case */\nimport { ComponentMeta } from '@storybook/react';\nimport React fro"
  },
  {
    "path": "src/DatePicker/DatePicker.tsx",
    "chars": 5998,
    "preview": "import React, { forwardRef, useCallback, useMemo, useState } from 'react';\nimport styled from 'styled-components';\n\nimpo"
  },
  {
    "path": "src/Frame/Frame.spec.tsx",
    "chars": 1101,
    "preview": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { Frame } from './Frame';\n\ndescribe("
  },
  {
    "path": "src/Frame/Frame.stories.tsx",
    "chars": 1835,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Frame } from 'react95';\nimport sty"
  },
  {
    "path": "src/Frame/Frame.tsx",
    "chars": 1683,
    "preview": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { createBorderStyles,"
  },
  {
    "path": "src/GroupBox/GroupBox.spec.tsx",
    "chars": 1805,
    "preview": "import React from 'react';\n\nimport { renderWithTheme, theme } from '../../test/utils';\n\nimport { GroupBox } from './Grou"
  },
  {
    "path": "src/GroupBox/GroupBox.stories.tsx",
    "chars": 2283,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport { Checkbox, GroupBox, "
  },
  {
    "path": "src/GroupBox/GroupBox.tsx",
    "chars": 1864,
    "preview": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { createDisabledTextS"
  },
  {
    "path": "src/Handle/Handle.spec.tsx",
    "chars": 1352,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Handle } from './Handle';\n\ndes"
  },
  {
    "path": "src/Handle/Handle.stories.tsx",
    "chars": 762,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { AppBar, Button, Handle, Toolbar } "
  },
  {
    "path": "src/Handle/Handle.tsx",
    "chars": 855,
    "preview": "import React from 'react';\nimport styled from 'styled-components';\nimport { CommonStyledProps } from '../types';\nimport "
  },
  {
    "path": "src/Hourglass/Hourglass.spec.tsx",
    "chars": 1027,
    "preview": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { Hourglass } from './Hourglass';\n\nd"
  },
  {
    "path": "src/Hourglass/Hourglass.stories.tsx",
    "chars": 550,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Hourglass } from 'react95';\nimport"
  },
  {
    "path": "src/Hourglass/Hourglass.tsx",
    "chars": 1000,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { getSize } from '../common/ut"
  },
  {
    "path": "src/Hourglass/base64hourglass.tsx",
    "chars": 4711,
    "preview": "const base64hourglass =\n  \"url('data:image/gif;base64,R0lGODlhPAA8APQAADc3N6+vr4+Pj05OTvn5+V1dXZ+fn29vby8vLw8PD/X19d/f37"
  },
  {
    "path": "src/MenuList/MenuList.spec.tsx",
    "chars": 1377,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { MenuList } from './MenuList';\n"
  },
  {
    "path": "src/MenuList/MenuList.stories.tsx",
    "chars": 2306,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Handle, MenuList, MenuListItem, Se"
  },
  {
    "path": "src/MenuList/MenuList.tsx",
    "chars": 848,
    "preview": "import React from 'react';\n\nimport styled from 'styled-components';\nimport { createBorderStyles, createBoxStyles } from "
  },
  {
    "path": "src/MenuList/MenuListItem.spec.tsx",
    "chars": 3513,
    "preview": "import React from 'react';\n\nimport { renderWithTheme, theme } from '../../test/utils';\nimport { blockSizes } from '../co"
  },
  {
    "path": "src/MenuList/MenuListItem.tsx",
    "chars": 2253,
    "preview": "import React, { forwardRef } from 'react';\n\nimport styled from 'styled-components';\nimport { createDisabledTextStyles } "
  },
  {
    "path": "src/Monitor/Monitor.spec.tsx",
    "chars": 1422,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Monitor } from './Monitor';\n\nd"
  },
  {
    "path": "src/Monitor/Monitor.stories.tsx",
    "chars": 549,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Monitor } from 'react95';\nimport s"
  },
  {
    "path": "src/Monitor/Monitor.tsx",
    "chars": 3313,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\n\nimport { StyledScrollView } from '.."
  },
  {
    "path": "src/NumberInput/NumberInput.spec.tsx",
    "chars": 5965,
    "preview": "import { fireEvent } from '@testing-library/react';\nimport React from 'react';\n\nimport { renderWithTheme } from '../../t"
  },
  {
    "path": "src/NumberInput/NumberInput.stories.tsx",
    "chars": 1533,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { ScrollView, NumberInput } from 're"
  },
  {
    "path": "src/NumberInput/NumberInput.tsx",
    "chars": 5036,
    "preview": "import React, { forwardRef, useCallback } from 'react';\nimport styled, { css } from 'styled-components';\n\nimport { Butto"
  },
  {
    "path": "src/ProgressBar/ProgressBar.spec.tsx",
    "chars": 2843,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { ProgressBar } from './ProgressB"
  },
  {
    "path": "src/ProgressBar/ProgressBar.stories.tsx",
    "chars": 2029,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React, { useEffect, useState } from 'react';\nimport { ProgressB"
  },
  {
    "path": "src/ProgressBar/ProgressBar.tsx",
    "chars": 4078,
    "preview": "import React, {\n  forwardRef,\n  useCallback,\n  useEffect,\n  useRef,\n  useState\n} from 'react';\nimport styled, { css } fr"
  },
  {
    "path": "src/Radio/Radio.spec.tsx",
    "chars": 2378,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { Radio } from './Radio';\n\ndescri"
  },
  {
    "path": "src/Radio/Radio.stories.tsx",
    "chars": 3411,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport { GroupBox, Radio, Scr"
  },
  {
    "path": "src/Radio/Radio.tsx",
    "chars": 3445,
    "preview": "import React, { forwardRef } from 'react';\nimport styled, { css, CSSProperties } from 'styled-components';\n\nimport { cre"
  },
  {
    "path": "src/ScrollView/ScrollView.spec.tsx",
    "chars": 1214,
    "preview": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { ScrollView } from './ScrollView';\n"
  },
  {
    "path": "src/ScrollView/ScrollView.stories.tsx",
    "chars": 1347,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { ScrollView, Window, WindowContent "
  },
  {
    "path": "src/ScrollView/ScrollView.tsx",
    "chars": 1827,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { insetShadow, createScrollbar"
  },
  {
    "path": "src/Select/Select.spec.tsx",
    "chars": 24139,
    "preview": "import { fireEvent, screen, waitFor } from '@testing-library/react';\nimport React from 'react';\nimport { renderWithTheme"
  },
  {
    "path": "src/Select/Select.stories.data.ts",
    "chars": 2096,
    "preview": "export const PokemonOptions = [\n  'Bulbasaur',\n  'Ivysaur',\n  'Venusaur',\n  'Charmander',\n  'Charmeleon',\n  'Charizard',"
  },
  {
    "path": "src/Select/Select.stories.tsx",
    "chars": 3936,
    "preview": "/* eslint-disable no-console */\n\nimport { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport {\n "
  },
  {
    "path": "src/Select/Select.styles.tsx",
    "chars": 5340,
    "preview": "import styled, { css } from 'styled-components';\n\nimport { StyledButton as Button } from '../Button/Button';\nimport {\n  "
  },
  {
    "path": "src/Select/Select.tsx",
    "chars": 6554,
    "preview": "import React, {\n  forwardRef,\n  useCallback,\n  useImperativeHandle,\n  useMemo,\n  useRef\n} from 'react';\n\nimport { useId "
  },
  {
    "path": "src/Select/Select.types.ts",
    "chars": 1964,
    "preview": "import React from 'react';\nimport { HTMLDataAttributes } from '../types';\n\ntype SelectChangeEventTargetValue<T> = { valu"
  },
  {
    "path": "src/Select/SelectNative.spec.tsx",
    "chars": 2368,
    "preview": "// Bsased on https://github.com/mui-org/material-ui\n\nimport { fireEvent, screen } from '@testing-library/react';\nimport "
  },
  {
    "path": "src/Select/SelectNative.tsx",
    "chars": 2180,
    "preview": "import React, { forwardRef, useCallback } from 'react';\n\nimport { noOp } from '../common/utils';\n\nimport { StyledInner, "
  },
  {
    "path": "src/Select/useSelectCommon.tsx",
    "chars": 1898,
    "preview": "import React, { useMemo } from 'react';\nimport useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontro"
  },
  {
    "path": "src/Select/useSelectState.ts",
    "chars": 16130,
    "preview": "import React, {\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\n  useState\n} from 'react';\n\nimport { KEYBOARD_KEY_CODES"
  },
  {
    "path": "src/Separator/Separator.spec.tsx",
    "chars": 2318,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Separator } from './Separator'"
  },
  {
    "path": "src/Separator/Separator.stories.tsx",
    "chars": 1072,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport styled from 'styled-components';\n\nim"
  },
  {
    "path": "src/Separator/Separator.tsx",
    "chars": 735,
    "preview": "import styled from 'styled-components';\nimport { getSize } from '../common/utils';\nimport { Orientation } from '../types"
  },
  {
    "path": "src/Slider/Slider.spec.tsx",
    "chars": 12097,
    "preview": "// Pretty much straight out copied from https://github.com/mui-org/material-ui 😂\nimport { fireEvent } from '@testing-lib"
  },
  {
    "path": "src/Slider/Slider.stories.tsx",
    "chars": 3460,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { ScrollView, Slider, SliderOnChange"
  },
  {
    "path": "src/Slider/Slider.tsx",
    "chars": 17137,
    "preview": "// helper functions and event handling basically copied from Material UI (https://github.com/mui-org/material-ui) Slider"
  },
  {
    "path": "src/Table/Table.spec.tsx",
    "chars": 695,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Table } from './Table';\n\ndescr"
  },
  {
    "path": "src/Table/Table.stories.tsx",
    "chars": 2222,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport {\n  Table,\n  TableBody,\n  TableDataC"
  },
  {
    "path": "src/Table/Table.tsx",
    "chars": 1002,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { StyledScrollView } from '../"
  },
  {
    "path": "src/Table/TableBody.spec.tsx",
    "chars": 785,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { TableBody } from './TableBody'"
  },
  {
    "path": "src/Table/TableBody.tsx",
    "chars": 791,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { insetShadow } from '../commo"
  },
  {
    "path": "src/Table/TableDataCell.spec.tsx",
    "chars": 780,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { TableDataCell } from './TableD"
  },
  {
    "path": "src/Table/TableDataCell.tsx",
    "chars": 646,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { CommonStyledProps } from '.."
  },
  {
    "path": "src/Table/TableHead.spec.tsx",
    "chars": 785,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { TableHead } from './TableHead'"
  },
  {
    "path": "src/Table/TableHead.tsx",
    "chars": 656,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { CommonStyledProps } from '.."
  },
  {
    "path": "src/Table/TableHeadCell.spec.tsx",
    "chars": 1829,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { TableHeadCell } from './TableH"
  },
  {
    "path": "src/Table/TableHeadCell.tsx",
    "chars": 2273,
    "preview": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { createBorderStyles,"
  },
  {
    "path": "src/Table/TableRow.spec.tsx",
    "chars": 768,
    "preview": "import React from 'react';\nimport { renderWithTheme } from '../../test/utils';\n\nimport { TableRow } from './TableRow';\n\n"
  },
  {
    "path": "src/Table/TableRow.tsx",
    "chars": 905,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { blockSizes } from '../common"
  },
  {
    "path": "src/Tabs/Tab.spec.tsx",
    "chars": 950,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { Tab } from './Tab';\n\ndescribe('"
  },
  {
    "path": "src/Tabs/Tab.tsx",
    "chars": 2274,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\n\nimport { createBorderStyles, createB"
  },
  {
    "path": "src/Tabs/TabBody.spec.tsx",
    "chars": 410,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { TabBody } from './TabBody';\n\nde"
  },
  {
    "path": "src/Tabs/TabBody.tsx",
    "chars": 776,
    "preview": "import React, { forwardRef } from 'react';\n\nimport styled from 'styled-components';\nimport { createBorderStyles, createB"
  },
  {
    "path": "src/Tabs/Tabs.spec.tsx",
    "chars": 3329,
    "preview": "import { fireEvent } from '@testing-library/react';\nimport React from 'react';\n\nimport { Tab } from '..';\nimport { rende"
  },
  {
    "path": "src/Tabs/Tabs.stories.tsx",
    "chars": 3533,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport {\n  Anchor,\n  Checkbox"
  },
  {
    "path": "src/Tabs/Tabs.tsx",
    "chars": 2773,
    "preview": "import React, { forwardRef, useMemo } from 'react';\n\nimport styled from 'styled-components';\nimport { noOp } from '../co"
  },
  {
    "path": "src/TextInput/TextInput.spec.tsx",
    "chars": 4076,
    "preview": "// Pretty much straight out copied from https://github.com/mui-org/material-ui 😂\n\nimport React from 'react';\nimport { fi"
  },
  {
    "path": "src/TextInput/TextInput.stories.tsx",
    "chars": 3238,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React, { useState } from 'react';\nimport { Button, ScrollView, "
  },
  {
    "path": "src/TextInput/TextInput.tsx",
    "chars": 3927,
    "preview": "import React, { forwardRef, useMemo } from 'react';\nimport styled, { css } from 'styled-components';\nimport {\n  createDi"
  },
  {
    "path": "src/Toolbar/Toolbar.spec.tsx",
    "chars": 786,
    "preview": "import { render } from '@testing-library/react';\nimport React from 'react';\n\nimport { Toolbar } from './Toolbar';\n\ndescr"
  },
  {
    "path": "src/Toolbar/Toolbar.tsx",
    "chars": 688,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\n\ntype ToolbarProps = {\n  children?: R"
  },
  {
    "path": "src/Tooltip/Tooltip.spec.tsx",
    "chars": 6446,
    "preview": "import { fireEvent, render, waitFor } from '@testing-library/react';\nimport React from 'react';\n\nimport { Tooltip, Toolt"
  },
  {
    "path": "src/Tooltip/Tooltip.stories.tsx",
    "chars": 628,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport { Button, Tooltip } from 'react95';\n"
  },
  {
    "path": "src/Tooltip/Tooltip.tsx",
    "chars": 5261,
    "preview": "import React, { forwardRef, useState } from 'react';\nimport styled from 'styled-components';\n\nimport { shadow } from '.."
  },
  {
    "path": "src/TreeView/TreeView.spec.tsx",
    "chars": 4726,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\nimport { TreeView } from './TreeView';\n\n"
  },
  {
    "path": "src/TreeView/TreeView.stories.tsx",
    "chars": 4171,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React, { useCallback, useState } from 'react';\nimport { GroupBo"
  },
  {
    "path": "src/TreeView/TreeView.tsx",
    "chars": 9660,
    "preview": "import React, { forwardRef, useCallback } from 'react';\nimport styled, { css } from 'styled-components';\n\nimport useCont"
  },
  {
    "path": "src/Window/Window.spec.tsx",
    "chars": 1320,
    "preview": "import React, { createRef } from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { Window } from '"
  },
  {
    "path": "src/Window/Window.stories.tsx",
    "chars": 2739,
    "preview": "import { ComponentMeta } from '@storybook/react';\nimport React from 'react';\nimport {\n  Button,\n  Frame,\n  Toolbar,\n  Wi"
  },
  {
    "path": "src/Window/Window.tsx",
    "chars": 1823,
    "preview": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { createBorderStyles,"
  },
  {
    "path": "src/Window/WindowContent.spec.tsx",
    "chars": 638,
    "preview": "import React from 'react';\n\nimport { renderWithTheme } from '../../test/utils';\n\nimport { WindowContent } from './Window"
  },
  {
    "path": "src/Window/WindowContent.tsx",
    "chars": 667,
    "preview": "import React, { forwardRef } from 'react';\nimport styled from 'styled-components';\nimport { CommonStyledProps } from '.."
  },
  {
    "path": "src/Window/WindowHeader.spec.tsx",
    "chars": 1822,
    "preview": "import React from 'react';\n\nimport { renderWithTheme, theme } from '../../test/utils';\n\nimport { WindowHeader } from './"
  },
  {
    "path": "src/Window/WindowHeader.tsx",
    "chars": 1467,
    "preview": "import React, { forwardRef } from 'react';\nimport styled, { css } from 'styled-components';\nimport { StyledButton } from"
  },
  {
    "path": "src/assets/fonts/src/ms-sans-serif/license.txt",
    "chars": 215,
    "preview": "The FontStruction “MS Sans Serif”\n(https://fontstruct.com/fontstructions/show/1384746) by “lou” is licensed\nunder a Crea"
  },
  {
    "path": "src/assets/fonts/src/ms-sans-serif/readme.txt",
    "chars": 1105,
    "preview": "The font file in this archive was created using Fontstruct the free, online\nfont-building tool.\nThis font was created by"
  },
  {
    "path": "src/assets/fonts/src/ms-sans-serif-bold/license.txt",
    "chars": 220,
    "preview": "The FontStruction “MS Sans Serif Bold”\n(https://fontstruct.com/fontstructions/show/1384862) by “lou” is licensed\nunder a"
  },
  {
    "path": "src/assets/fonts/src/ms-sans-serif-bold/readme.txt",
    "chars": 1105,
    "preview": "The font file in this archive was created using Fontstruct the free, online\nfont-building tool.\nThis font was created by"
  },
  {
    "path": "src/common/SwitchBase.ts",
    "chars": 1240,
    "preview": "import styled, { css } from 'styled-components';\n\nimport { createDisabledTextStyles, focusOutline } from '.';\nimport { S"
  },
  {
    "path": "src/common/constants.ts",
    "chars": 241,
    "preview": "export const KEYBOARD_KEY_CODES = {\n  ARROW_DOWN: 'ArrowDown',\n  ARROW_LEFT: 'ArrowLeft',\n  ARROW_RIGHT: 'ArrowRight',\n "
  },
  {
    "path": "src/common/hooks/useControlledOrUncontrolled.ts",
    "chars": 1713,
    "preview": "import React, { useState, useCallback } from 'react';\n\nexport default function useControlledOrUncontrolled<T>({\n  defaul"
  },
  {
    "path": "src/common/hooks/useEventCallback.ts",
    "chars": 653,
    "preview": "import * as React from 'react';\n\nconst useEnhancedEffect =\n  typeof window !== 'undefined' ? React.useLayoutEffect : Rea"
  },
  {
    "path": "src/common/hooks/useForkRef.spec.tsx",
    "chars": 4075,
    "preview": "import { render } from '@testing-library/react';\nimport React, { useCallback, useEffect, useRef, useState } from 'react'"
  },
  {
    "path": "src/common/hooks/useForkRef.ts",
    "chars": 995,
    "preview": "// Straight out copied from https://github.com/mui-org/material-ui 😂\n\nimport { useMemo } from 'react';\n\nfunction setRef<"
  },
  {
    "path": "src/common/hooks/useId.spec.ts",
    "chars": 430,
    "preview": "import { renderHook } from '@testing-library/react-hooks';\nimport { useId } from './useId';\n\ndescribe(useId, () => {\n  i"
  },
  {
    "path": "src/common/hooks/useId.ts",
    "chars": 359,
    "preview": "import { useMemo } from 'react';\n\nfunction makeId() {\n  const chars =\n    '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi"
  },
  {
    "path": "src/common/hooks/useIsFocusVisible.ts",
    "chars": 4800,
    "preview": "// Straight out copied from https://github.com/mui-org/material-ui 😂\n// based on https://github.com/WICG/focus-visible/b"
  },
  {
    "path": "src/common/index.ts",
    "chars": 8243,
    "preview": "import { css } from 'styled-components';\nimport { Color, CommonThemeProps, Theme } from '../types';\n\nexport const shadow"
  },
  {
    "path": "src/common/styleReset.ts",
    "chars": 1564,
    "preview": "export default `\n  html,\nbody,\ndiv,\nspan,\napplet,\nobject,\niframe,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\np,\nblockquote,\npre,\na,\nabbr,\na"
  },
  {
    "path": "src/common/system.ts",
    "chars": 165,
    "preview": "// TODO - implement styled-system\n\nimport { Sizes } from '../types';\n\nexport const blockSizes: Record<Sizes, string> = {"
  },
  {
    "path": "src/common/themes/aiee.ts",
    "chars": 1360,
    "preview": "/* \"AIEE\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Aiee-668092636\n */\n\nimport { Theme } from './type"
  },
  {
    "path": "src/common/themes/ash.ts",
    "chars": 1361,
    "preview": "/* \"Ash\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Ash-575566643\n */\nimport { Theme } from './types';"
  },
  {
    "path": "src/common/themes/azureOrange.ts",
    "chars": 947,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'azureOrange',\n\n  anchor: '#1034a6',\n  anchorVisited: '#44038"
  },
  {
    "path": "src/common/themes/bee.ts",
    "chars": 939,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'bee',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  bo"
  },
  {
    "path": "src/common/themes/blackAndWhite.ts",
    "chars": 949,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'blackAndWhite',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440"
  },
  {
    "path": "src/common/themes/blue.ts",
    "chars": 1410,
    "preview": "/* \"Blue\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Blue-525167751\n */\n\nimport { Theme } from './type"
  },
  {
    "path": "src/common/themes/brick.ts",
    "chars": 941,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'brick',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  "
  },
  {
    "path": "src/common/themes/candy.ts",
    "chars": 941,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'candy',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  "
  },
  {
    "path": "src/common/themes/cherry.ts",
    "chars": 1432,
    "preview": "/* \"Cherry\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Cherry-747961418\n */\n\nimport { Theme } from './"
  },
  {
    "path": "src/common/themes/coldGray.ts",
    "chars": 969,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'coldGray',\n\n  anchor: '#8d88c2',\n  anchorVisited: '#440381',"
  },
  {
    "path": "src/common/themes/counterStrike.ts",
    "chars": 949,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'counterStrike',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440"
  },
  {
    "path": "src/common/themes/darkTeal.ts",
    "chars": 1435,
    "preview": "/* \"Teal for Shelbi - Dark\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Teal-for-Shelbi-Dark-631177772\n"
  },
  {
    "path": "src/common/themes/denim.ts",
    "chars": 1399,
    "preview": "/* \"Denim\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Denim-870494744\n */\n\nimport { Theme } from './ty"
  },
  {
    "path": "src/common/themes/eggplant.ts",
    "chars": 944,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'eggplant',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',"
  },
  {
    "path": "src/common/themes/fxDev.ts",
    "chars": 1412,
    "preview": "/* \"FxDev\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/FxDev-701274128\n */\n\nimport { Theme } from './ty"
  },
  {
    "path": "src/common/themes/highContrast.ts",
    "chars": 948,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'highContrast',\n\n  anchor: '#1034a6',\n  anchorVisited: '#4403"
  },
  {
    "path": "src/common/themes/honey.ts",
    "chars": 1398,
    "preview": "/* \"Honey\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Honey-632126512\n */\n\nimport { Theme } from './ty"
  },
  {
    "path": "src/common/themes/hotChocolate.ts",
    "chars": 1448,
    "preview": "/* \"Hot Chocolate\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Hot-Chocolate-654380979\n */\n\nimport { Th"
  },
  {
    "path": "src/common/themes/hotdogStand.ts",
    "chars": 1365,
    "preview": "/* \"Hotdog Stand\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Hotdog-Stand-525606846\n */\n\nimport { Them"
  },
  {
    "path": "src/common/themes/index.ts",
    "chars": 2823,
    "preview": "import aiee from './aiee';\nimport ash from './ash';\nimport azureOrange from './azureOrange';\nimport bee from './bee';\nim"
  },
  {
    "path": "src/common/themes/lilac.ts",
    "chars": 941,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'lilac',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  "
  },
  {
    "path": "src/common/themes/lilacRoseDark.ts",
    "chars": 974,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'lilacRoseDark',\n\n  anchor: '#a65387',\n  anchorVisited: '#440"
  },
  {
    "path": "src/common/themes/maple.ts",
    "chars": 941,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'maple',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  "
  },
  {
    "path": "src/common/themes/marine.ts",
    "chars": 942,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'marine',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n "
  },
  {
    "path": "src/common/themes/matrix.ts",
    "chars": 942,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'matrix',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n "
  },
  {
    "path": "src/common/themes/millenium.ts",
    "chars": 973,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'millenium',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381'"
  },
  {
    "path": "src/common/themes/modernDark.ts",
    "chars": 950,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'modernDark',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381"
  },
  {
    "path": "src/common/themes/molecule.ts",
    "chars": 944,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'molecule',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',"
  },
  {
    "path": "src/common/themes/monochrome.ts",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/common/themes/ninjaTurtles.ts",
    "chars": 948,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'ninjaTurtles',\n\n  anchor: '#1034a6',\n  anchorVisited: '#4403"
  },
  {
    "path": "src/common/themes/olive.ts",
    "chars": 941,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'olive',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  "
  },
  {
    "path": "src/common/themes/original.ts",
    "chars": 944,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'original',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',"
  },
  {
    "path": "src/common/themes/pamelaAnderson.ts",
    "chars": 950,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'pamelaAnderson',\n\n  anchor: '#1034a6',\n  anchorVisited: '#44"
  },
  {
    "path": "src/common/themes/peggysPastels.ts",
    "chars": 1436,
    "preview": "/* \"Peggy's Pastels\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Peggy-s-Pastels-505540096\n */\n\nimport "
  },
  {
    "path": "src/common/themes/plum.ts",
    "chars": 940,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'plum',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  b"
  },
  {
    "path": "src/common/themes/polarized.ts",
    "chars": 1423,
    "preview": "/* \"Polarized\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Polarized-557712217\n */\n\nimport { Theme } fr"
  },
  {
    "path": "src/common/themes/powerShell.ts",
    "chars": 1432,
    "preview": "/* \"PowerShell\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/PowerShell-646065752\n */\n\nimport { Theme } "
  },
  {
    "path": "src/common/themes/rainyDay.ts",
    "chars": 944,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'rainyDay',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',"
  },
  {
    "path": "src/common/themes/raspberry.ts",
    "chars": 1432,
    "preview": "/* \"Raspberry\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Raspberry-539289720\n */\n\nimport { Theme } fr"
  },
  {
    "path": "src/common/themes/redWine.ts",
    "chars": 1376,
    "preview": "/* \"Red Wine\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Red-Wine-545729607\n */\n\nimport { Theme } from"
  },
  {
    "path": "src/common/themes/rose.ts",
    "chars": 940,
    "preview": "import { Theme } from './types';\n\nexport default {\n  name: 'rose',\n\n  anchor: '#1034a6',\n  anchorVisited: '#440381',\n  b"
  },
  {
    "path": "src/common/themes/seawater.ts",
    "chars": 1418,
    "preview": "/* \"Seawater\" by tPenguinLTG\n * https://www.deviantart.com/tpenguinltg/art/Seawater-736002425\n */\n\nimport { Theme } from"
  }
]

// ... and 46 more files (download for full content)

About this extraction

This page contains the full source code of the react95-io/React95 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 246 files (466.6 KB), approximately 136.4k tokens, and a symbol index with 199 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!