├── src
├── _scss
│ ├── w98
│ │ ├── _cursors.scss
│ │ ├── var
│ │ │ ├── var.scss
│ │ │ ├── colors.scss
│ │ │ └── uris.scss
│ │ ├── cursors
│ │ │ ├── NO-min.png
│ │ │ ├── X-min.png
│ │ │ ├── Copy-min.png
│ │ │ ├── Hand-min.png
│ │ │ ├── Help-min.png
│ │ │ ├── Link-min.png
│ │ │ ├── Move-min.png
│ │ │ ├── Text-min.png
│ │ │ ├── Wait-min.png
│ │ │ ├── Arrow-min.png
│ │ │ ├── Circle-min.png
│ │ │ ├── Cross-min.png
│ │ │ ├── DND-ask-min.png
│ │ │ ├── SizeAll-min.png
│ │ │ ├── UpArrow-min.png
│ │ │ ├── VText-min.png
│ │ │ ├── ZoomIn-min.png
│ │ │ ├── ZoomOut-min.png
│ │ │ ├── Circle-min-1.png
│ │ │ ├── ColRezise-min.png
│ │ │ ├── Crosshair-min.png
│ │ │ ├── DND-copy-min.png
│ │ │ ├── DND-link-min.png
│ │ │ ├── DownArrow-min.png
│ │ │ ├── LeftArrow-min.png
│ │ │ ├── LeftRight-min.png
│ │ │ ├── RowResize-min.png
│ │ │ ├── textSmall-min.png
│ │ │ ├── AngleUpLeft-min.png
│ │ │ ├── AngleUpRight-min.png
│ │ │ ├── AppStarting-min.png
│ │ │ ├── ArrowRight-min.png
│ │ │ ├── HandPointer-min.png
│ │ │ ├── Handsqueezed-min.png
│ │ │ ├── Handwriting-min.png
│ │ │ ├── RightArrow-min.png
│ │ │ ├── UpDownArrow-min.png
│ │ │ ├── UpLeftArrow-min.png
│ │ │ ├── UpRightArrow-min.png
│ │ │ ├── DownLeftArrow-min.png
│ │ │ ├── DownRightArrow-min.png
│ │ │ └── LeftRightArrow-min.png
│ │ ├── fonts
│ │ │ ├── ms98_11.ttf
│ │ │ ├── MSSansSerif.woff2
│ │ │ ├── ms98s11a10d02.ttf
│ │ │ └── MSSansSerifBold.woff2
│ │ ├── index.scss
│ │ ├── mixins
│ │ │ └── box-shadows.scss
│ │ ├── functions
│ │ │ └── box-shadows.scss
│ │ ├── _icons.scss
│ │ ├── _inputs.scss
│ │ ├── _window.scss
│ │ └── _task-bar.scss
│ └── w98_simple.scss
├── images
│ ├── uris.css
│ ├── help.gif
│ ├── close.gif
│ ├── close.png
│ ├── restore.gif
│ ├── restore.png
│ ├── arrow-up.gif
│ ├── icons
│ │ ├── cut.png
│ │ ├── back.png
│ │ ├── copy.png
│ │ ├── delete.png
│ │ ├── paste.png
│ │ ├── undo.png
│ │ ├── upDir.png
│ │ ├── views.png
│ │ ├── forward.png
│ │ ├── properties.png
│ │ ├── backDisabled.png
│ │ └── forwardDisabled.png
│ ├── maximize.gif
│ ├── maximize.png
│ ├── minimize.gif
│ ├── minimize.png
│ ├── radio-off.gif
│ ├── radio-on.gif
│ ├── uris.css.map
│ ├── windows98.gif
│ ├── arrow-down.gif
│ ├── arrow-left.gif
│ ├── arrow-right.gif
│ ├── cursor-pixel.gif
│ ├── menu-checked.gif
│ ├── menu-radio.gif
│ ├── more-options.gif
│ ├── rgba0-0-0-50.png
│ ├── rgba0-0-0-75.png
│ ├── start-trans.png
│ ├── close-disabled.gif
│ ├── help-disabled.gif
│ ├── rgba0-0-180-75.gif
│ ├── rgba0-0-180-75.png
│ ├── rgba70-70-180.gif
│ ├── cursor-pixel-x2.gif
│ ├── radio-on-disabled.gif
│ ├── rgba-204-204-204.gif
│ ├── arrow-down-disabled.gif
│ ├── radio-off-disabled.gif
│ ├── rgba-204-204-204-50.png
│ ├── rgba-204-204-204-75.png
│ ├── rgba-204-204-204-85.png
│ ├── rgba-255-255-255-50.png
│ ├── arrow-right-inverted.gif
│ ├── menu-checked-disabled.gif
│ └── uris.scss
├── components
│ ├── TaskBar
│ │ ├── index.js
│ │ ├── _TaskBar.scss
│ │ ├── Notifier.jsx
│ │ ├── __tests__
│ │ │ ├── Notifier.spec.jsx
│ │ │ ├── Notifications.spec.jsx
│ │ │ └── TaskBar.spec.jsx
│ │ ├── Notifications.jsx
│ │ └── TaskBar.jsx
│ ├── MenuBar
│ │ ├── index.js
│ │ ├── _MenuBar.scss
│ │ ├── MenuBar.jsx
│ │ └── index.spec.js
│ ├── FormRadio
│ │ ├── index.js
│ │ ├── _Radio.scss
│ │ ├── Radio.jsx
│ │ └── Radio.spec.jsx
│ ├── Frame
│ │ ├── index.js
│ │ ├── _Frame.scss
│ │ ├── Frame.jsx
│ │ └── Frame.spec.jsx
│ ├── StartMenu
│ │ ├── index.js
│ │ ├── StartMenu.spec.jsx
│ │ └── StartMenu.jsx
│ ├── ButtonForm
│ │ ├── index.js
│ │ ├── _ButtonForm.scss
│ │ ├── ButtonForm.jsx
│ │ └── FormButton.spec.jsx
│ ├── ButtonNav
│ │ ├── index.js
│ │ ├── _ButtonNav.scss
│ │ ├── ButtonNav.jsx
│ │ └── NavButton.spec.jsx
│ ├── FormInputText
│ │ ├── index.js
│ │ ├── _InputText.scss
│ │ ├── InputText.spec.jsx
│ │ └── InputText.jsx
│ ├── FormSelectBox
│ │ ├── index.js
│ │ ├── SelectBox.spec.jsx
│ │ ├── SelectBox.jsx
│ │ └── _SelectBox.scss
│ ├── FormSelectDISABLED
│ │ ├── index.js
│ │ ├── README.md
│ │ ├── Select.spec.jsx
│ │ └── _Select.scss
│ ├── FormToggle
│ │ ├── index.js
│ │ ├── Toggle.jsx
│ │ └── Toggle.spec.jsx
│ ├── Window
│ │ ├── index.js
│ │ ├── WindowAbstract.spec.jsx
│ │ ├── _WindowAbstract.scss
│ │ └── Window.jsx
│ ├── ButtonStart
│ │ ├── index.js
│ │ ├── _StartButton.scss
│ │ ├── ButtonStart.jsx
│ │ └── ButtonStart.spec.jsx
│ ├── FormCheckbox
│ │ ├── index.js
│ │ ├── _Checkbox.scss
│ │ ├── Checkbox.jsx
│ │ └── Checkbox.spec.jsx
│ ├── FormFakeSelect
│ │ ├── index.js
│ │ ├── FakeSelect.jsx
│ │ └── _FakeSelect.scss
│ ├── WindowAlert
│ │ ├── index.js
│ │ ├── _WindowAlert.scss
│ │ ├── WindowAlert.spec.jsx
│ │ └── WindowAlert.jsx
│ ├── ExplorerView
│ │ ├── index.js
│ │ ├── styles
│ │ │ ├── ExplorerView.css.map
│ │ │ ├── ExplorerView.scss
│ │ │ └── ExplorerView.css
│ │ ├── __tests__
│ │ │ └── ExplorerView.spec.jsx
│ │ └── ExplorerView.jsx
│ ├── Icon
│ │ ├── _AbstractIcon.scss
│ │ ├── index.js
│ │ ├── AbstractIcon.spec.jsx
│ │ └── Icon.jsx
│ ├── IconListIcon
│ │ ├── index.js
│ │ ├── _ListIcon.scss
│ │ ├── ListIcon.spec.jsx
│ │ └── IconListIcon.jsx
│ ├── WindowAction
│ │ ├── index.js
│ │ ├── assets
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── 5.png
│ │ ├── _styles.scss
│ │ └── WindowAction.jsx
│ ├── ButtonProgram
│ │ ├── index.js
│ │ ├── _ButtonProgram.scss
│ │ ├── ButtonProgram.jsx
│ │ └── ProgramButton.spec.jsx
│ ├── WindowProgram
│ │ ├── index.js
│ │ ├── _WindowProgram.scss
│ │ ├── WindowProgram.spec.jsx
│ │ └── WindowProgram.jsx
│ ├── Button
│ │ ├── _AbstractButton.scss
│ │ ├── index.js
│ │ ├── Button.jsx
│ │ └── Button.spec.jsx
│ ├── ButtonIconLarge
│ │ ├── index.js
│ │ ├── _ButtonIconLarge.scss
│ │ ├── ButtonIconLarge.jsx
│ │ └── LargeIconButton.spec.jsx
│ ├── ButtonIconSmall
│ │ ├── index.js
│ │ ├── _ButtonIconSmall.scss
│ │ ├── ButtonIconSmall.jsx
│ │ └── SmallIconButton.spec.jsx
│ ├── DetailsSection
│ │ ├── index.js
│ │ ├── _details-section.scss
│ │ ├── DetailsSection.jsx
│ │ └── index.spec.js
│ ├── IconExplorerIcon
│ │ ├── index.js
│ │ ├── _ExplorerIcon.scss
│ │ ├── ExplorerIcon.spec.jsx
│ │ └── IconExplorerIcon.jsx
│ ├── StandardMenuHOC
│ │ ├── index.js
│ │ └── StandardMenuHOC.jsx
│ ├── WindowExplorer
│ │ ├── index.js
│ │ ├── WindowExplorer.spec.jsx
│ │ ├── WindowExplorer.jsx
│ │ └── _WindowExplorer.scss
│ ├── ResizableIconsList
│ │ ├── index.js
│ │ ├── _options-list.scss
│ │ └── ResizableIconsList.jsx
│ ├── FormSelectBoxSimple
│ │ ├── index.js
│ │ ├── _SelectMultipleSimple.scss
│ │ ├── SelectMultipleSimple.spec.jsx
│ │ └── SelectMultipleSimple.jsx
│ ├── StandardMenu
│ │ ├── index.js
│ │ ├── _StandardMenu.scss
│ │ ├── StandardMenuItem.jsx
│ │ └── StandardMenu.jsx
│ └── Theme.jsx
├── setupTests.js
└── index.js
├── static.json
├── .storybook
├── stories
│ ├── directory_closed.png
│ ├── scrollbar.stories.jsx
│ ├── desktop.stories.jsx
│ ├── start.stories.jsx
│ ├── icons.stories.jsx
│ ├── buttons.stories.jsx
│ ├── taskbar.stories.jsx
│ ├── contextMenu.stories.jsx
│ └── windows.stories.jsx
├── addons.old.js
├── preview.jsx
├── old.webpack.old.config.js
├── main.js
├── config.old.js
└── manager-head.html
├── .travis.yml
├── playroom
├── FrameComponent.js
└── index.js
├── .codeclimate.yml
├── .editorconfig
├── rename.sh
├── jest.config.js
├── .babelrc
├── .eslintrc.js
├── LICENSE
├── .gitignore
├── rollup.config.babel.js
├── .stylelintrc.json
├── playroom.config.js
├── TODO.md
└── package.json
/src/_scss/w98/_cursors.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/images/uris.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | /*# sourceMappingURL=uris.css.map */
4 |
--------------------------------------------------------------------------------
/src/_scss/w98/var/var.scss:
--------------------------------------------------------------------------------
1 | $window-padding: 3px;
2 | $base-font-size: 11px;
3 |
--------------------------------------------------------------------------------
/src/components/TaskBar/index.js:
--------------------------------------------------------------------------------
1 | import TaskBar from './TaskBar';
2 | export default TaskBar;
3 |
--------------------------------------------------------------------------------
/src/images/help.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/help.gif
--------------------------------------------------------------------------------
/src/components/MenuBar/index.js:
--------------------------------------------------------------------------------
1 | import MenuBar from './MenuBar';
2 |
3 | export default MenuBar;
4 |
--------------------------------------------------------------------------------
/src/images/close.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/close.gif
--------------------------------------------------------------------------------
/src/images/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/close.png
--------------------------------------------------------------------------------
/src/images/restore.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/restore.gif
--------------------------------------------------------------------------------
/src/images/restore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/restore.png
--------------------------------------------------------------------------------
/static.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": ".out",
3 | "routes": {
4 | "/**": "index.html"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/FormRadio/index.js:
--------------------------------------------------------------------------------
1 | import FormRadio from './Radio';
2 |
3 | export default FormRadio;
4 |
--------------------------------------------------------------------------------
/src/components/Frame/index.js:
--------------------------------------------------------------------------------
1 | import WindowFrame from './Frame';
2 |
3 | export default WindowFrame;
4 |
--------------------------------------------------------------------------------
/src/components/StartMenu/index.js:
--------------------------------------------------------------------------------
1 | import StartMenu from './StartMenu';
2 | export default StartMenu;
3 |
--------------------------------------------------------------------------------
/src/images/arrow-up.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/arrow-up.gif
--------------------------------------------------------------------------------
/src/images/icons/cut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/cut.png
--------------------------------------------------------------------------------
/src/images/maximize.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/maximize.gif
--------------------------------------------------------------------------------
/src/images/maximize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/maximize.png
--------------------------------------------------------------------------------
/src/images/minimize.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/minimize.gif
--------------------------------------------------------------------------------
/src/images/minimize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/minimize.png
--------------------------------------------------------------------------------
/src/images/radio-off.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/radio-off.gif
--------------------------------------------------------------------------------
/src/images/radio-on.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/radio-on.gif
--------------------------------------------------------------------------------
/src/images/uris.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":[],"names":[],"mappings":"","file":"uris.css"}
--------------------------------------------------------------------------------
/src/images/windows98.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/windows98.gif
--------------------------------------------------------------------------------
/src/components/ButtonForm/index.js:
--------------------------------------------------------------------------------
1 | import ButtonForm from './ButtonForm';
2 |
3 | export default ButtonForm;
4 |
--------------------------------------------------------------------------------
/src/components/ButtonNav/index.js:
--------------------------------------------------------------------------------
1 | import ButtonNav from './ButtonNav';
2 |
3 | export default ButtonNav;
4 |
--------------------------------------------------------------------------------
/src/components/FormInputText/index.js:
--------------------------------------------------------------------------------
1 | import InputText from './InputText';
2 |
3 | export default InputText;
4 |
--------------------------------------------------------------------------------
/src/components/FormSelectBox/index.js:
--------------------------------------------------------------------------------
1 | import SelectBox from './SelectBox';
2 |
3 | export default SelectBox;
4 |
--------------------------------------------------------------------------------
/src/components/FormSelectDISABLED/index.js:
--------------------------------------------------------------------------------
1 | import Select from './Select';
2 |
3 | export default Select;
4 |
--------------------------------------------------------------------------------
/src/components/FormToggle/index.js:
--------------------------------------------------------------------------------
1 | import FormToggle from './Toggle';
2 |
3 | export default FormToggle;
4 |
--------------------------------------------------------------------------------
/src/components/Window/index.js:
--------------------------------------------------------------------------------
1 | import WindowAbstract from './Window';
2 |
3 | export default WindowAbstract;
4 |
--------------------------------------------------------------------------------
/src/images/arrow-down.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/arrow-down.gif
--------------------------------------------------------------------------------
/src/images/arrow-left.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/arrow-left.gif
--------------------------------------------------------------------------------
/src/images/arrow-right.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/arrow-right.gif
--------------------------------------------------------------------------------
/src/images/cursor-pixel.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/cursor-pixel.gif
--------------------------------------------------------------------------------
/src/images/icons/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/back.png
--------------------------------------------------------------------------------
/src/images/icons/copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/copy.png
--------------------------------------------------------------------------------
/src/images/icons/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/delete.png
--------------------------------------------------------------------------------
/src/images/icons/paste.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/paste.png
--------------------------------------------------------------------------------
/src/images/icons/undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/undo.png
--------------------------------------------------------------------------------
/src/images/icons/upDir.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/upDir.png
--------------------------------------------------------------------------------
/src/images/icons/views.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/views.png
--------------------------------------------------------------------------------
/src/images/menu-checked.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/menu-checked.gif
--------------------------------------------------------------------------------
/src/images/menu-radio.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/menu-radio.gif
--------------------------------------------------------------------------------
/src/images/more-options.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/more-options.gif
--------------------------------------------------------------------------------
/src/images/rgba0-0-0-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/rgba0-0-0-50.png
--------------------------------------------------------------------------------
/src/images/rgba0-0-0-75.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/rgba0-0-0-75.png
--------------------------------------------------------------------------------
/src/images/start-trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/start-trans.png
--------------------------------------------------------------------------------
/src/components/ButtonStart/index.js:
--------------------------------------------------------------------------------
1 | import ButtonStart from './ButtonStart';
2 |
3 | export default ButtonStart;
4 |
--------------------------------------------------------------------------------
/src/components/FormCheckbox/index.js:
--------------------------------------------------------------------------------
1 | import FormCheckbox from './Checkbox';
2 |
3 | export default FormCheckbox;
4 |
--------------------------------------------------------------------------------
/src/components/FormFakeSelect/index.js:
--------------------------------------------------------------------------------
1 | import FakeSelect from './FakeSelect';
2 |
3 | export default FakeSelect;
4 |
--------------------------------------------------------------------------------
/src/components/WindowAlert/index.js:
--------------------------------------------------------------------------------
1 | import WindowAlert from './WindowAlert';
2 |
3 | export default WindowAlert;
4 |
--------------------------------------------------------------------------------
/src/images/close-disabled.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/close-disabled.gif
--------------------------------------------------------------------------------
/src/images/help-disabled.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/help-disabled.gif
--------------------------------------------------------------------------------
/src/images/icons/forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/forward.png
--------------------------------------------------------------------------------
/src/images/rgba0-0-180-75.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/rgba0-0-180-75.gif
--------------------------------------------------------------------------------
/src/images/rgba0-0-180-75.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/rgba0-0-180-75.png
--------------------------------------------------------------------------------
/src/images/rgba70-70-180.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/rgba70-70-180.gif
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/NO-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/NO-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/X-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/X-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/fonts/ms98_11.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/fonts/ms98_11.ttf
--------------------------------------------------------------------------------
/src/components/ExplorerView/index.js:
--------------------------------------------------------------------------------
1 | import ExplorerView from './ExplorerView';
2 |
3 | export default ExplorerView;
4 |
--------------------------------------------------------------------------------
/src/components/Icon/_AbstractIcon.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_icons";
2 |
3 | .icon {
4 | @include icon();
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/IconListIcon/index.js:
--------------------------------------------------------------------------------
1 | import IconListIcon from './IconListIcon';
2 |
3 | export default IconListIcon;
4 |
--------------------------------------------------------------------------------
/src/components/WindowAction/index.js:
--------------------------------------------------------------------------------
1 | import WindowAction from './WindowAction';
2 |
3 | export default WindowAction;
4 |
--------------------------------------------------------------------------------
/src/images/cursor-pixel-x2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/cursor-pixel-x2.gif
--------------------------------------------------------------------------------
/src/images/icons/properties.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/properties.png
--------------------------------------------------------------------------------
/src/images/radio-on-disabled.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/radio-on-disabled.gif
--------------------------------------------------------------------------------
/src/images/rgba-204-204-204.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/rgba-204-204-204.gif
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Copy-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Copy-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Hand-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Hand-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Help-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Help-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Link-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Link-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Move-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Move-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Text-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Text-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Wait-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Wait-min.png
--------------------------------------------------------------------------------
/src/components/ButtonProgram/index.js:
--------------------------------------------------------------------------------
1 | import ButtonProgram from './ButtonProgram';
2 |
3 | export default ButtonProgram;
4 |
--------------------------------------------------------------------------------
/src/components/Frame/_Frame.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_window";
2 |
3 | .Frame {
4 | @include window-basic();
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/WindowProgram/index.js:
--------------------------------------------------------------------------------
1 | import WindowProgram from './WindowProgram';
2 |
3 | export default WindowProgram;
4 |
--------------------------------------------------------------------------------
/src/images/arrow-down-disabled.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/arrow-down-disabled.gif
--------------------------------------------------------------------------------
/src/images/icons/backDisabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/backDisabled.png
--------------------------------------------------------------------------------
/src/images/radio-off-disabled.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/radio-off-disabled.gif
--------------------------------------------------------------------------------
/src/images/rgba-204-204-204-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/rgba-204-204-204-50.png
--------------------------------------------------------------------------------
/src/images/rgba-204-204-204-75.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/rgba-204-204-204-75.png
--------------------------------------------------------------------------------
/src/images/rgba-204-204-204-85.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/rgba-204-204-204-85.png
--------------------------------------------------------------------------------
/src/images/rgba-255-255-255-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/rgba-255-255-255-50.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Arrow-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Arrow-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Circle-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Circle-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Cross-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Cross-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/DND-ask-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/DND-ask-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/SizeAll-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/SizeAll-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/UpArrow-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/UpArrow-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/VText-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/VText-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/ZoomIn-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/ZoomIn-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/ZoomOut-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/ZoomOut-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/fonts/MSSansSerif.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/fonts/MSSansSerif.woff2
--------------------------------------------------------------------------------
/src/_scss/w98/fonts/ms98s11a10d02.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/fonts/ms98s11a10d02.ttf
--------------------------------------------------------------------------------
/src/components/Button/_AbstractButton.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_buttons";
2 |
3 | .btn {
4 | @include button();
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/ButtonIconLarge/index.js:
--------------------------------------------------------------------------------
1 | import ButtonIconLarge from './ButtonIconLarge';
2 |
3 | export default ButtonIconLarge;
4 |
--------------------------------------------------------------------------------
/src/components/ButtonIconSmall/index.js:
--------------------------------------------------------------------------------
1 | import ButtonIconSmall from './ButtonIconSmall';
2 |
3 | export default ButtonIconSmall;
4 |
--------------------------------------------------------------------------------
/src/components/DetailsSection/index.js:
--------------------------------------------------------------------------------
1 | import DetailsSection from './DetailsSection';
2 |
3 | export default DetailsSection;
4 |
--------------------------------------------------------------------------------
/src/components/IconExplorerIcon/index.js:
--------------------------------------------------------------------------------
1 | import ExplorerIcon from './IconExplorerIcon';
2 |
3 | export default ExplorerIcon;
4 |
--------------------------------------------------------------------------------
/src/components/MenuBar/_MenuBar.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_window";
2 |
3 | .MenuBar {
4 | @include window-menu();
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/StandardMenuHOC/index.js:
--------------------------------------------------------------------------------
1 | import withStandardMenu from './StandardMenuHOC';
2 | export default withStandardMenu;
3 |
--------------------------------------------------------------------------------
/src/components/WindowExplorer/index.js:
--------------------------------------------------------------------------------
1 | import WindowExplorer from './WindowExplorer';
2 |
3 | export default WindowExplorer;
4 |
--------------------------------------------------------------------------------
/src/images/arrow-right-inverted.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/arrow-right-inverted.gif
--------------------------------------------------------------------------------
/src/images/icons/forwardDisabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/icons/forwardDisabled.png
--------------------------------------------------------------------------------
/src/images/menu-checked-disabled.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/images/menu-checked-disabled.gif
--------------------------------------------------------------------------------
/.storybook/stories/directory_closed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/.storybook/stories/directory_closed.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Circle-min-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Circle-min-1.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/ColRezise-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/ColRezise-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Crosshair-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Crosshair-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/DND-copy-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/DND-copy-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/DND-link-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/DND-link-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/DownArrow-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/DownArrow-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/LeftArrow-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/LeftArrow-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/LeftRight-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/LeftRight-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/RowResize-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/RowResize-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/textSmall-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/textSmall-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/AngleUpLeft-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/AngleUpLeft-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/AngleUpRight-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/AngleUpRight-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/AppStarting-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/AppStarting-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/ArrowRight-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/ArrowRight-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/HandPointer-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/HandPointer-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Handsqueezed-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Handsqueezed-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/Handwriting-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/Handwriting-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/RightArrow-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/RightArrow-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/UpDownArrow-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/UpDownArrow-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/UpLeftArrow-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/UpLeftArrow-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/UpRightArrow-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/UpRightArrow-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/fonts/MSSansSerifBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/fonts/MSSansSerifBold.woff2
--------------------------------------------------------------------------------
/src/components/ButtonNav/_ButtonNav.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_buttons";
2 |
3 | .btn.ButtonNav {
4 | @include button-nav();
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/IconListIcon/_ListIcon.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_icons";
2 |
3 | .icon.ListIcon {
4 | @include icon-list();
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/TaskBar/_TaskBar.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_task-bar";
2 |
3 | .TaskBar {
4 | @include task-bar(".TaskBar");
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/WindowAction/assets/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/components/WindowAction/assets/1.png
--------------------------------------------------------------------------------
/src/components/WindowAction/assets/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/components/WindowAction/assets/2.png
--------------------------------------------------------------------------------
/src/components/WindowAction/assets/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/components/WindowAction/assets/3.png
--------------------------------------------------------------------------------
/src/components/WindowAction/assets/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/components/WindowAction/assets/4.png
--------------------------------------------------------------------------------
/src/components/WindowAction/assets/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/components/WindowAction/assets/5.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/DownLeftArrow-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/DownLeftArrow-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/DownRightArrow-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/DownRightArrow-min.png
--------------------------------------------------------------------------------
/src/_scss/w98/cursors/LeftRightArrow-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/padraigfl/packard-belle/HEAD/src/_scss/w98/cursors/LeftRightArrow-min.png
--------------------------------------------------------------------------------
/src/components/ButtonForm/_ButtonForm.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_buttons";
2 |
3 | .btn.ButtonForm {
4 | @include button-form();
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/FormInputText/_InputText.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_inputs.scss";
2 |
3 | .InputText {
4 | @include input-text();
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/ResizableIconsList/index.js:
--------------------------------------------------------------------------------
1 | import ResizableIconsList from './ResizableIconsList';
2 |
3 | export default ResizableIconsList;
4 |
--------------------------------------------------------------------------------
/src/components/WindowAlert/_WindowAlert.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_window";
2 |
3 | .WindowAlert {
4 | @include window-alert();
5 | }
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "8"
4 | - "9"
5 | - "10"
6 | after_success:
7 | - npm run coverage
8 | - npm run coveralls
9 |
--------------------------------------------------------------------------------
/src/components/ButtonStart/_StartButton.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_buttons";
2 |
3 | .btn.StartButton {
4 | @include button-start();
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/FormSelectBoxSimple/index.js:
--------------------------------------------------------------------------------
1 | import SelectMultipleSimple from './SelectMultipleSimple';
2 |
3 | export default SelectMultipleSimple;
4 |
--------------------------------------------------------------------------------
/src/components/Icon/index.js:
--------------------------------------------------------------------------------
1 | import AbstractIcon from './Icon';
2 | export const iconProps = AbstractIcon.propTypes;
3 | export default AbstractIcon;
4 |
--------------------------------------------------------------------------------
/src/components/ButtonProgram/_ButtonProgram.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_buttons";
2 |
3 | .btn.ButtonProgram {
4 | @include button-program();
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/IconExplorerIcon/_ExplorerIcon.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_icons";
2 |
3 | .icon.ExplorerIcon {
4 | @include icon-explorer();
5 | }
6 |
--------------------------------------------------------------------------------
/playroom/FrameComponent.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import React from 'react';
3 |
4 | export default props =>
{props.children}
;
5 |
--------------------------------------------------------------------------------
/src/components/Button/index.js:
--------------------------------------------------------------------------------
1 | import AbstractButton from './Button';
2 | export const commonButtonPropTypes = AbstractButton.propTypes;
3 | export default AbstractButton;
4 |
--------------------------------------------------------------------------------
/src/components/StandardMenu/index.js:
--------------------------------------------------------------------------------
1 | import StandardMenu from './StandardMenu';
2 | export const standardMenuProps = StandardMenu.propTypes;
3 | export default StandardMenu;
4 |
--------------------------------------------------------------------------------
/.storybook/addons.old.js:
--------------------------------------------------------------------------------
1 | // import '@storybook/addon-options/register';
2 | // import '@storybook/addon-notes/register';
3 | // import '@storybook/addon-storysource/register';
4 |
--------------------------------------------------------------------------------
/src/components/DetailsSection/_details-section.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_window";
2 |
3 | .DetailsSection,
4 | .window__section {
5 | @include window-settings-section();
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/FormRadio/_Radio.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_inputs.scss";
2 |
3 | .Radio {
4 | display: inline-block;
5 | input[type="radio"] {
6 | @include radio();
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | const Enzyme = require('enzyme');
2 | const Adapter = require('enzyme-adapter-react-16');
3 |
4 | require('raf/polyfill');
5 |
6 | Enzyme.configure({ adapter: new Adapter() });
7 |
8 |
--------------------------------------------------------------------------------
/src/components/ButtonIconSmall/_ButtonIconSmall.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_buttons";
2 | @import "../../_scss/w98/var/uris.scss";
3 |
4 | .btn.ButtonIconSmall {
5 | @include button-small-icon();
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/FormCheckbox/_Checkbox.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_inputs.scss";
2 |
3 | .Checkbox {
4 | display: inline-block;
5 | input[type="checkbox"] {
6 | @include checkbox();
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/_scss/w98/index.scss:
--------------------------------------------------------------------------------
1 | @import "./_theme";
2 | @import "./buttons/index";
3 | @import "./menu/index";
4 | @import "./inputs/index";
5 | @import "./icons/index";
6 | @import "./task-bar";
7 | @import "./window";
8 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | engines:
2 | eslint:
3 | enabled: true
4 |
5 | ratings:
6 | paths:
7 | - src/**
8 |
9 | exclude_paths:
10 | - .out/**
11 | - .storybook/**
12 | - build/**
13 | - src/**/*.spec.js
14 |
--------------------------------------------------------------------------------
/src/components/FormSelectBoxSimple/_SelectMultipleSimple.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_inputs.scss";
2 |
3 | .SelectMultipleSimple {
4 | select[multiple] {
5 | @include select-multiple-simple();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | insert_newline_at_end_of_file = true
7 | indent_size = 2
8 | indent_style = space
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/src/components/ExplorerView/styles/ExplorerView.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["ExplorerView.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEF;EACE;EACA","file":"ExplorerView.css"}
--------------------------------------------------------------------------------
/src/_scss/w98/mixins/box-shadows.scss:
--------------------------------------------------------------------------------
1 | @import "../var/colors";
2 | @import "../functions/box-shadows";
3 |
4 | @mixin shadow-input {
5 | box-shadow: inset -1px -1px 0px #ffffff, inset 1px 1px 0px 0px $darkgrey,
6 | inset -2px -2px 0px $grey, inset 2px 2px 0px 0px $black;
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/FormRadio/Radio.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Toggle from '../FormToggle/Toggle';
3 |
4 | import './_Radio.scss';
5 |
6 | const Radio = props => ;
7 |
8 | Radio.propTypes = Toggle.propTypes;
9 |
10 | export default Radio;
11 |
--------------------------------------------------------------------------------
/src/components/ButtonIconLarge/_ButtonIconLarge.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_buttons";
2 |
3 | .ButtonIconLarge {
4 | @include button-large-icon;
5 | &__icon {
6 | flex-grow: 1;
7 | width: 20px;
8 | height: 20px;
9 | margin: 1px auto 2px;
10 | align-content: center;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/_scss/w98/var/colors.scss:
--------------------------------------------------------------------------------
1 | $blue: #0000a2;
2 | $grey: #bbc3c4;
3 | $black: #0c0c0c;
4 | $darkgrey: #808088;
5 |
6 | // stripe
7 | $red: #fb0006;
8 | $yellow: #ffff09;
9 | $lightgreen: #22ff04;
10 | $lightblue: #21ffff;
11 |
12 | $folderYellow: #ffff09;
13 | $folderInnerBorder: #a2a44f;
14 | $folderOuterBorder: #000000;
15 |
--------------------------------------------------------------------------------
/src/components/StartMenu/StartMenu.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import StartMenu from './StartMenu';
4 |
5 | describe('StartMenu', () => {
6 | it('renders', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('.StartMenu').length).toBeTruthy();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/src/components/IconListIcon/ListIcon.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import ListIcon from './IconListIcon';
4 |
5 | describe('ListIcon', () => {
6 | it('uses AbstractIcon', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('AbstractIcon').length).toBeTruthy();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/rename.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # find all files either in /data or /data/subdir
4 | find .storybook/stories -type f -maxdepth 5 | while read file; do
5 | if [[ "$file" =~ \.js$ ]];
6 | then
7 | echo $file;
8 | mv $file ${file}x
9 | fi
10 | done
11 | # find /data/ -type f -print0 | while read -d $'\0' file; do
12 | # echo "Processing $file"
13 | # done
--------------------------------------------------------------------------------
/src/components/ExplorerView/styles/ExplorerView.scss:
--------------------------------------------------------------------------------
1 | .ExplorerView {
2 | display: flex;
3 | flex-flow: column wrap;
4 | height: 100%;
5 | width: 100%;
6 | align-content: flex-start;
7 |
8 | &--fixed-width {
9 | overflow-y: scroll;
10 | height: initial;
11 | }
12 | &--fixed-height {
13 | overflow-x: scroll;
14 | width: initial;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/IconExplorerIcon/ExplorerIcon.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import ExplorerIcon from './IconExplorerIcon';
4 |
5 | describe('ExplorerIcon', () => {
6 | it('uses AbstractIcon', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('AbstractIcon').length).toBeTruthy();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/src/components/FormSelectDISABLED/README.md:
--------------------------------------------------------------------------------
1 | # Select Dropdown (DISABLED)
2 |
3 | I updated React Select so I could easily update other more important packages. As I don't even use this component in my implementation of this project I'm going to disable it and will look at re-adding it if someone wants it added back or if I need it later.
4 |
5 | It is sort of styled, but there's so much more required
6 |
--------------------------------------------------------------------------------
/src/components/StandardMenu/_StandardMenu.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_menu";
2 |
3 | .StandardMenu {
4 | @include standard-menu(".StandardMenu");
5 |
6 | @include standard-menu-css(".StandardMenu");
7 | }
8 |
9 | .StandardMenuItem {
10 | &--empty {
11 | .StandardMenuItem__button {
12 | text-shadow: 1px 1px #ffffff;
13 | text-align: center;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line
2 | module.exports = {
3 | verbose: true,
4 | setupFilesAfterEnv: ['./src/setupTests.js'],
5 | collectCoverageFrom: ['src/**/*.js'],
6 | testEnvironment: 'jsdom',
7 | transform: {
8 | '^.+\\.js?$': 'babel-jest',
9 | },
10 | testURL: 'http://localhost/',
11 | moduleNameMapper: {
12 | '^.+\\.(css|less|scss)$': 'babel-jest',
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/src/_scss/w98/functions/box-shadows.scss:
--------------------------------------------------------------------------------
1 | @import "../var/colors";
2 |
3 | @function shadow($color, $px1, $px2: "") {
4 | @if ($px2 == "") {
5 | @return inset $px1 * 1px $px1 * 1px 0px $color;
6 | } @else {
7 | @return inset $px1 * 1px $px2 * 1px 0px $color;
8 | }
9 | }
10 |
11 | @function dualShadow($color1, $color2, $px: 1) {
12 | @return shadow($color2, -$px), shadow($color1, $px);
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/ExplorerView/styles/ExplorerView.css:
--------------------------------------------------------------------------------
1 | .ExplorerView {
2 | display: flex;
3 | flex-flow: column wrap;
4 | height: 100%;
5 | width: 100%;
6 | align-content: flex-start;
7 | }
8 | .ExplorerView--fixed-width {
9 | overflow-y: scroll;
10 | height: initial;
11 | }
12 | .ExplorerView--fixed-height {
13 | overflow-x: scroll;
14 | width: initial;
15 | }
16 |
17 | /*# sourceMappingURL=ExplorerView.css.map */
18 |
--------------------------------------------------------------------------------
/src/components/FormRadio/Radio.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import Radio from './Radio';
4 |
5 | describe('Radio', () => {
6 | it('uses toggle with radio type', () => {
7 | const wrapper = shallow();
8 | expect(
9 | wrapper
10 | .find('Toggle')
11 | .at(0)
12 | .props().type
13 | ).toBe('radio');
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/src/components/FormCheckbox/Checkbox.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import Toggle from '../FormToggle/Toggle';
4 |
5 | import './_Checkbox.scss';
6 |
7 | const Checkbox = props => (
8 |
13 | );
14 |
15 | Checkbox.propTypes = Toggle.propTypes;
16 |
17 | export default Checkbox;
18 |
--------------------------------------------------------------------------------
/src/components/FormCheckbox/Checkbox.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import Checkbox from './Checkbox';
4 |
5 | describe('Checkbox', () => {
6 | it('uses toggle with radio type', () => {
7 | const wrapper = shallow();
8 | expect(
9 | wrapper
10 | .find('Toggle')
11 | .at(0)
12 | .props().type
13 | ).toBe('checkbox');
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env", {"modules": false}],
4 | "@babel/preset-react"
5 | ],
6 | "plugins": [
7 | "@babel/plugin-proposal-object-rest-spread",
8 | "@babel/plugin-proposal-class-properties"
9 | ],
10 | "env": {
11 | "test": {
12 | "presets": ["@babel/preset-env", "@babel/preset-react"],
13 | "plugins": [
14 | "@babel/plugin-proposal-class-properties"
15 | ]
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/Frame/Frame.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 | import './_Frame.scss';
5 |
6 | const WindowFrame = props => (
7 |
8 | {props.children}
9 |
10 | );
11 |
12 | WindowFrame.propTypes = {
13 | children: PropTypes.node,
14 | className: PropTypes.string,
15 | };
16 |
17 | export default WindowFrame;
18 |
--------------------------------------------------------------------------------
/src/components/Theme.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import '../_scss/w98/theme.scss';
5 |
6 | const Theme = props => (
7 |
8 | {props.children}
9 |
10 | );
11 |
12 | Theme.propTypes = {
13 | children: PropTypes.node,
14 | className: PropTypes.string,
15 | style: PropTypes.shape(),
16 | };
17 |
18 | export default Theme;
19 |
--------------------------------------------------------------------------------
/.storybook/preview.jsx:
--------------------------------------------------------------------------------
1 |
2 |
3 | import React from 'react';
4 | import Theme from '../src/components/Theme';
5 |
6 | export const decorators = [
7 | (Story) => (
8 |
18 |
19 |
20 | )
21 | ]
--------------------------------------------------------------------------------
/src/components/FormFakeSelect/FakeSelect.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import './_FakeSelect.scss';
4 |
5 | const FakeSelect = props => (
6 |
7 | {props.icon &&

}
8 |
{props.title}
9 |
10 |
11 | );
12 |
13 | export default FakeSelect;
14 |
--------------------------------------------------------------------------------
/src/components/ButtonNav/ButtonNav.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import Button, { commonButtonPropTypes } from '../Button';
4 |
5 | import './_ButtonNav.scss';
6 |
7 | const ButtonNav = props => (
8 |
14 | );
15 |
16 | ButtonNav.propTypes = commonButtonPropTypes;
17 |
18 | export default ButtonNav;
19 |
--------------------------------------------------------------------------------
/src/components/ButtonStart/ButtonStart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import Button, { commonButtonPropTypes } from '../Button';
4 |
5 | import './_StartButton.scss';
6 |
7 | const StartButton = props => (
8 |
14 | );
15 |
16 | StartButton.propTypes = commonButtonPropTypes;
17 |
18 | export default StartButton;
19 |
--------------------------------------------------------------------------------
/src/components/StartMenu/StartMenu.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import Start from '../ButtonStart/ButtonStart';
4 | import standardMenuWrapper from '../StandardMenuHOC';
5 |
6 | const Started = standardMenuWrapper(Start);
7 |
8 | const StartMenu = props => {
9 | const { className, ...otherProps } = props;
10 | return ;
11 | };
12 |
13 | StartMenu.propTypes = Started.propTypes;
14 |
15 | export default StartMenu;
16 |
--------------------------------------------------------------------------------
/src/components/DetailsSection/DetailsSection.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import './_details-section.scss';
4 |
5 | const DetailsSection = props =>
6 | props.children ? (
7 |
8 | {props.title}
9 | {props.children}
10 |
11 | ) : null;
12 |
13 | DetailsSection.propTypes = {
14 | title: PropTypes.node,
15 | children: PropTypes.node,
16 | };
17 |
18 | export default DetailsSection;
19 |
--------------------------------------------------------------------------------
/src/components/WindowExplorer/WindowExplorer.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { mount } from 'enzyme';
3 | import WindowExplorer from './WindowExplorer';
4 |
5 | xdescribe('WindowExplorer', () => {
6 | // eslint-disable-line
7 | const func = jest.fn();
8 | const wrapper = mount();
9 | it('renders', () => {
10 | expect(wrapper.find('.WindowExplorer').length).toBeTruthy();
11 | });
12 |
13 | it('can be clicked', () => {
14 | wrapper.simulate('click');
15 | expect(func).toHaveBeenCalled();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/components/ButtonForm/ButtonForm.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import Button, { commonButtonPropTypes } from '../Button';
4 |
5 | import './_ButtonForm.scss';
6 |
7 | const ButtonForm = props => (
8 |
16 | );
17 |
18 | Button.propTypes = {
19 | ...commonButtonPropTypes,
20 | };
21 |
22 | export default ButtonForm;
23 |
--------------------------------------------------------------------------------
/src/components/TaskBar/Notifier.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | const Notifier = props => (
5 |
11 | );
12 |
13 | Notifier.propTypes = {
14 | icon: PropTypes.string,
15 | onClick: PropTypes.func,
16 | title: PropTypes.string,
17 | };
18 |
19 | Notifier.defaultProps = {
20 | onClick: () => {},
21 | };
22 |
23 | export default Notifier;
24 |
--------------------------------------------------------------------------------
/src/components/DetailsSection/index.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import DetailsSection from './DetailsSection';
4 |
5 | describe('DetailsSection', () => {
6 | const wrapper = shallow(
7 | Children
8 | );
9 | it('renders', () => {
10 | expect(wrapper.find('.DetailsSection').length).toBeTruthy();
11 | });
12 |
13 | it('has title', () => {
14 | expect(
15 | wrapper
16 | .find('.DetailsSection__title')
17 | .at(0)
18 | .text()
19 | ).toBe('Test');
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/components/WindowAlert/WindowAlert.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import WindowAlert from '../WindowAlert';
4 |
5 | describe('WindowAlert', () => {
6 | const func = jest.fn();
7 | const wrapper = shallow();
8 | it('renders', () => {
9 | expect(wrapper.find('.WindowAlert').length).toBeTruthy();
10 | });
11 |
12 | it('can be clicked', () => {
13 | wrapper.find('.WindowAlert__cancel').at(0).simulate('click');
14 | wrapper.find('.WindowAlert__ok').at(0).simulate('click');
15 | expect(func).toHaveBeenCalledTimes(2);
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/components/IconExplorerIcon/IconExplorerIcon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import AbstractIcon, { iconProps } from '../Icon';
4 | import './_ExplorerIcon.scss';
5 |
6 | const ExplorerIcon = props => (
7 |
18 | );
19 |
20 | ExplorerIcon.propTypes = iconProps;
21 |
22 | export default ExplorerIcon;
23 |
--------------------------------------------------------------------------------
/src/components/IconListIcon/IconListIcon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import AbstractIcon, { iconProps } from '../Icon/Icon';
4 | import './_ListIcon.scss';
5 |
6 | const ListIcon = props => (
7 |
19 | );
20 |
21 | ListIcon.propTypes = iconProps;
22 |
23 | export default ListIcon;
24 |
--------------------------------------------------------------------------------
/.storybook/old.webpack.old.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = ({ config: defaultConfig }) => {
4 | // Extend defaultConfig as you need.
5 |
6 | // For example, add typescript loader:
7 | defaultConfig.module.rules.push({
8 | test: /\.scss$/,
9 | loaders: ['style-loader', 'css-loader', 'sass-loader'],
10 | include: path.resolve(__dirname, '../'),
11 | });
12 | defaultConfig.module.rules.push({
13 | test: /\.jsx?$/,
14 | loaders: [require.resolve('@storybook/addon-storysource/loader')],
15 | enforce: 'pre',
16 | });
17 |
18 | defaultConfig.resolve.extensions.push('.scss');
19 |
20 | return defaultConfig;
21 | };
22 |
--------------------------------------------------------------------------------
/src/components/ButtonProgram/ButtonProgram.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 | import Button, { commonButtonPropTypes } from '../Button';
5 |
6 | import './_ButtonProgram.scss';
7 |
8 | const ButtonProgram = props => (
9 |
17 | );
18 |
19 | ButtonProgram.propTypes = {
20 | ...commonButtonPropTypes,
21 | icon: PropTypes.any,
22 | };
23 |
24 | export default ButtonProgram;
25 |
--------------------------------------------------------------------------------
/src/components/TaskBar/__tests__/Notifier.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { mount } from 'enzyme';
4 | import Notifier from '../Notifier';
5 |
6 | const options = (onClick = jest.fn()) => ([
7 | { alt: 'open', onClick, title: 'testButton' },
8 | { alt: 'find', onClick, options: 'testOption' },
9 | ]);
10 |
11 | describe('Notifier', () => {
12 | const func = jest.fn();
13 | const wrapper = mount();
14 | it('renders', () => {
15 | expect(wrapper.find('.Notifier').length).toBeTruthy();
16 | });
17 |
18 | it('can be clicked', () => {
19 | wrapper.simulate('click');
20 | expect(func).toHaveBeenCalled();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/components/Frame/Frame.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import WindowFrame from './Frame';
4 |
5 | const options = (onClick = jest.fn()) => [
6 | { alt: 'open', onClick, title: 'testButton' },
7 | { alt: 'find', onClick, options: 'testOption' },
8 | ];
9 |
10 | describe('WindowFrame', () => {
11 | const func = jest.fn();
12 | const wrapper = shallow(
13 |
14 | );
15 |
16 | it('renders', () => {
17 | expect(wrapper.find('.Frame').length).toBeTruthy();
18 | });
19 |
20 | it('accepts classes and props', () => {
21 | wrapper.setProps({ className: 'test' });
22 | expect(wrapper.render().hasClass('test')).toBe(true);
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true
5 | },
6 | parser: 'babel-eslint',
7 | extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:jest/recommended', 'plugin:storybook/recommended'],
8 | parserOptions: {
9 | ecmaFeatures: {
10 | experimentalObjectRestSpread: true,
11 | jsx: true
12 | },
13 | sourceType: 'module'
14 | },
15 | rules: {
16 | indent: ['error', 2],
17 | 'linebreak-style': ['error', 'unix'],
18 | quotes: ['error', 'single'],
19 | semi: ['error', 'always'],
20 | 'comma-dangle': ['error', 'always-multiline'],
21 | 'react/prop-types': [0] // imported props fail
22 | },
23 | settings: {
24 | react: {
25 | version: "detect"
26 | }
27 | }
28 | };
--------------------------------------------------------------------------------
/src/components/ButtonIconSmall/ButtonIconSmall.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 | import Button, { commonButtonPropTypes } from '../Button';
5 |
6 | import './_ButtonIconSmall.scss';
7 |
8 | const ButtonIconSmall = props => (
9 |
20 | );
21 |
22 | ButtonIconSmall.propTypes = {
23 | ...commonButtonPropTypes,
24 | icon: PropTypes.string,
25 | };
26 |
27 | export default ButtonIconSmall;
28 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | const main = {
2 | framework: {
3 | name: '@storybook/react-webpack5',
4 | options: { fastRefresh: true },
5 | },
6 | stories: [
7 | './stories/taskbar.stories.jsx',
8 | './stories/buttons.stories.jsx',
9 | './stories/windows.stories.jsx',
10 | './stories/contextMenu.stories.jsx',
11 | './stories/icons.stories.jsx',
12 | './stories/scrollbar.stories.jsx',
13 | './stories/inputs.stories.jsx',
14 | './stories/start.stories.jsx',
15 | './stories/desktop.stories.jsx',
16 | ],
17 | core: {
18 | builder: {
19 | name: '@storybook/builder-vite', // 👈 The builder enabled here.
20 | options: {
21 | loader: {
22 | '.js': 'jsx',
23 | },
24 | },
25 | }
26 | },
27 | }
28 |
29 | export default main
--------------------------------------------------------------------------------
/src/components/ExplorerView/__tests__/ExplorerView.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | shallow,
4 | } from 'enzyme';
5 | import ExplorerView from '../ExplorerView';
6 |
7 | describe('ExplorerView', () => {
8 | it('renders', () => {
9 | const wrapper = shallow();
10 | expect(wrapper.find('.ExplorerView').length).toBeTruthy();
11 | });
12 |
13 | it('accepts classes passed in', () => {
14 | const wrapper = shallow(
15 |
20 | ).render();
21 | expect(wrapper.hasClass('test')).toBe(true);
22 | expect(wrapper.hasClass('ExplorerView--fixed-height')).toBe(true);
23 | expect(wrapper.hasClass('ExplorerView--fixed-width')).toBe(true);
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/components/ButtonIconLarge/ButtonIconLarge.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 | import Button, { commonButtonPropTypes } from '../Button/Button';
5 |
6 | import './_ButtonIconLarge.scss';
7 |
8 | const ButtonIconLarge = props => (
9 |
19 | );
20 |
21 | ButtonIconLarge.propTypes = {
22 | ...commonButtonPropTypes,
23 | icon: PropTypes.string,
24 | title: PropTypes.string,
25 | };
26 |
27 | export default ButtonIconLarge;
28 |
--------------------------------------------------------------------------------
/src/components/FormSelectBoxSimple/SelectMultipleSimple.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import SelectMultipleSimple from './SelectMultipleSimple';
4 |
5 | describe('SelectMultipleSimple', () => {
6 | it('renders', () => {
7 | const wrapper = shallow(
8 |
9 | );
10 | expect(wrapper.find('div.SelectMultipleSimple').length).toBeTruthy();
11 | expect(wrapper.find('option')).toHaveLength(2);
12 | });
13 |
14 | it('whole box can be disabled', () => {
15 | const wrapper = shallow(
16 |
17 | );
18 | expect(wrapper.find('select').props().disabled).toBe(true);
19 | });
20 |
21 | xit('needs more tests, low priority', () => {});
22 | });
23 |
--------------------------------------------------------------------------------
/src/components/WindowProgram/_WindowProgram.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/var/colors";
2 | @import "../../_scss/w98/functions/box-shadows";
3 | @import "../../_scss/w98/mixins/box-shadows";
4 | @import "../../_scss/w98/var/var";
5 |
6 | .w98 .WindowProgram {
7 | display: inline-flex;
8 | flex-direction: column;
9 |
10 | > footer {
11 | display: flex;
12 | > div {
13 | white-space: nowrap;
14 | text-overflow: ellipsis;
15 | overflow: hidden;
16 | min-width: 0px; // hack to prevent overflow
17 | flex-grow: 1;
18 | padding: 2px;
19 | height: 12px;
20 | box-shadow: dualShadow($black, #ffffff);
21 | &:not(:last-child) {
22 | margin-right: 2px;
23 | }
24 | &:last-child {
25 | padding-right: 12px;
26 | }
27 | }
28 | }
29 | > div:last-child {
30 | margin-top: 2px;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.storybook/stories/scrollbar.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const Scrollbar = {
4 | render: () => (
5 |
6 |
16 |
sdfsadf
17 |
sdfsadf
18 |
19 |
20 | ),
21 | notes: '~Scrollbar only works correctly on Chrome at the moment',
22 | };
23 |
24 | const meta = {
25 | component: Scrollbar,
26 | }
27 | export default meta;
--------------------------------------------------------------------------------
/src/components/MenuBar/MenuBar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 | import Button from '../Button';
5 | import withMenuWrapper from '../StandardMenuHOC';
6 | import './_MenuBar.scss';
7 |
8 | const MenuEntry = withMenuWrapper(Button);
9 |
10 | const MenuBar = props => (
11 |
23 | );
24 |
25 | MenuBar.propTypes = {
26 | options: PropTypes.arrayOf(PropTypes.shape()),
27 | className: PropTypes.string,
28 | };
29 |
30 | export default MenuBar;
31 |
--------------------------------------------------------------------------------
/src/components/FormToggle/Toggle.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 |
5 | const Toggle = props => (
6 |
7 |
16 |
21 |
22 | );
23 |
24 | const toggleProps = {
25 | label: PropTypes.string,
26 | type: PropTypes.string,
27 | id: PropTypes.string,
28 | name: PropTypes.string,
29 | checked: PropTypes.bool,
30 | onChange: PropTypes.func,
31 | isDisabled: PropTypes.bool,
32 | };
33 |
34 | Toggle.propTypes = toggleProps;
35 |
36 | export default Toggle;
37 |
--------------------------------------------------------------------------------
/src/components/Window/WindowAbstract.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import WindowAbstract from './Window';
4 |
5 | describe('WindowAbstract', () => {
6 | const wrapper = shallow(
7 |
8 | );
9 | it('renders all constants', () => {
10 | expect(wrapper.find('.Window').length).toBeTruthy();
11 | expect(wrapper.find('.Window__heading').length).toBeTruthy();
12 | expect(wrapper.find('.Window__title').length).toBeTruthy();
13 | });
14 |
15 | it('displays correct heading buttons', () => {
16 | expect(wrapper.find('.Window__close').length).toBeTruthy();
17 | expect(wrapper.find('.Window__maximize').length).toBeTruthy();
18 | expect(wrapper.find('.Window__help').length).toBeTruthy();
19 | wrapper.setState({ maximized: true });
20 | expect(wrapper.find('.Window__restore').length).toBeTruthy();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/components/WindowProgram/WindowProgram.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { mount } from 'enzyme';
3 | import WindowProgram from '.';
4 |
5 | const options = (onClick = jest.fn()) => [
6 | { title: 'testButton', options: [{ title: 'testOption', onClick }] },
7 | { title: 'test', options: [{ title: 'testOption', onClick }] },
8 | ];
9 |
10 | describe('WindowProgram', () => {
11 | const func = jest.fn();
12 | const wrapper = mount(
13 |
14 |
15 |
16 | );
17 | it('renders', () => {
18 | expect(wrapper.find('.WindowProgram').length).toBeTruthy();
19 | });
20 | it('hasChildren', () => {
21 | expect(wrapper.find('.test').length).toBeTruthy();
22 | });
23 | it('has menus', () => {
24 | expect(wrapper.find('.WindowProgram__menu').length).toBeTruthy();
25 | expect(wrapper.find('.StandardMenuWrapper')).toHaveLength(2);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/src/components/TaskBar/__tests__/Notifications.spec.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import {
4 | mount,
5 | } from 'enzyme';
6 | import Notifications, { Time } from '../Notifications';
7 |
8 | const menuOptions = (onClick = jest.fn()) => ([
9 | { alt: 'open', onClick, title: 'testButton' },
10 | { alt: 'find', onClick, options: 'testOption' },
11 | ]);
12 |
13 | describe('Notifications', () => {
14 | const func = jest.fn();
15 | const options = menuOptions(func);
16 | const wrapper = mount();
17 | const notifers = wrapper.find('Notifier');
18 | it('renders', () => {
19 | expect(wrapper.find('.TaskBar__notifications').length).toBeTruthy();
20 | expect(notifers).toHaveLength(2);
21 | expect(wrapper.find(Time).length).toBeTruthy();
22 | });
23 |
24 | it('can be clicked', () => {
25 | notifers.forEach(notifier => notifier.simulate('click'));
26 | expect(func).toHaveBeenCalledTimes(2);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/components/Window/_WindowAbstract.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/_window";
2 |
3 | .w98 .Window {
4 | &__heading {
5 | @include window-heading();
6 | }
7 | &__icon {
8 | @include window-icon();
9 | }
10 | &__title {
11 | @include window-title();
12 | }
13 | &__close {
14 | margin-left: 2px;
15 | background-image: url($close);
16 | }
17 | &__restore {
18 | background-image: url($restore);
19 | }
20 | &__minimize {
21 | background-image: url($minimize);
22 | }
23 | &__maximize {
24 | background-image: url($maximize);
25 | }
26 | &__help {
27 | background-image: url($whelp);
28 | }
29 | &--resizable {
30 | &:after {
31 | position: absolute;
32 | bottom: 4px;
33 | right: 4px;
34 | height: 12px;
35 | width: 12px;
36 | content: "";
37 | background-image: url($resize);
38 | }
39 | }
40 | &--maximized {
41 | @include window-maximized();
42 | }
43 |
44 | &--drag {
45 | @include window-drag();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/ExplorerView/ExplorerView.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 | import './styles/ExplorerView.scss';
5 |
6 | const ExplorerView = props => (
7 |
22 | { props.children }
23 |
24 | );
25 |
26 | ExplorerView.defaultProps = {
27 | style: {},
28 | };
29 |
30 | ExplorerView.propTypes = {
31 | children: PropTypes.node,
32 | fixedHeight: PropTypes.bool,
33 | fixedWidth: PropTypes.bool,
34 | className: PropTypes.string,
35 | };
36 |
37 | export default ExplorerView;
38 |
--------------------------------------------------------------------------------
/src/components/FormToggle/Toggle.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import Toggle from './Toggle';
4 |
5 | describe('Toggle', () => {
6 | it('renders', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('input').length).toBeTruthy();
9 | });
10 |
11 | it('renders div when passed class', () => {
12 | const wrapper = shallow();
13 | expect(wrapper.find('div').length).toBeTruthy();
14 | });
15 |
16 | it('checking states with props', () => {
17 | const wrapper = shallow();
18 | const wrapperProps = wrapper
19 | .find('input')
20 | .at(0)
21 | .props();
22 | expect(wrapperProps.disabled).toBe(true);
23 | expect(wrapperProps.checked).toBe(true);
24 | });
25 |
26 | it('onchange fires', () => {
27 | const clickFunc = jest.fn();
28 | const wrapper = mount();
29 | wrapper.find('input').simulate('change');
30 | expect(clickFunc).toHaveBeenCalled();
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 @padraigfl
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19 | OR OTHER DEALINGS IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/src/components/ButtonNav/NavButton.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import ButtonNav from './ButtonNav';
4 |
5 | describe('ButtonNav', () => {
6 | it('renders', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('.ButtonNav').length).toBeTruthy();
9 | });
10 |
11 | it('No children', () => {
12 | const wrapper = shallow(
13 |
14 |
15 |
16 | );
17 | expect(wrapper.find('#test').length).toBeFalsy();
18 | });
19 |
20 | it('accepts classes passed in', () => {
21 | const wrapper = shallow(
22 |
23 | ).render();
24 | expect(wrapper.hasClass('test')).toBe(true);
25 | expect(wrapper.hasClass('btn--active')).toBe(true);
26 | expect(wrapper.hasClass('btn--disabled')).toBe(true);
27 | });
28 |
29 | it('onclick fires prop', () => {
30 | const clickFunc = jest.fn();
31 | const wrapper = mount();
32 | wrapper.simulate('click');
33 | expect(clickFunc).toHaveBeenCalled();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/components/ButtonIconSmall/SmallIconButton.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import ButtonIconSmall from './ButtonIconSmall';
4 |
5 | describe('ButtonIconSmall', () => {
6 | it('renders', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('.ButtonIconSmall').length).toBeTruthy();
9 | });
10 |
11 | it('renders icon', () => {
12 | const wrapper = mount();
13 | expect(wrapper.find('img').props().src).toBe('TEST');
14 | });
15 |
16 | it('accepts classes passed in', () => {
17 | const wrapper = shallow(
18 |
19 | ).render();
20 | expect(wrapper.hasClass('test')).toBe(true);
21 | expect(wrapper.hasClass('btn--active')).toBe(true);
22 | expect(wrapper.hasClass('btn--disabled')).toBe(true);
23 | });
24 |
25 | it('onclick focuses and fires prop', () => {
26 | const clickFunc = jest.fn();
27 | const wrapper = mount();
28 | wrapper.simulate('click');
29 | expect(clickFunc).toHaveBeenCalled();
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/src/components/ButtonStart/ButtonStart.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import StartButton from './ButtonStart';
4 |
5 | describe('StartButton', () => {
6 | it('renders', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('.StartButton').length).toBeTruthy();
9 | });
10 |
11 | it('accepts classes passed in', () => {
12 | const wrapper = shallow(
13 |
14 | ).render();
15 | expect(wrapper.hasClass('test')).toBe(true);
16 | expect(wrapper.hasClass('btn--active')).toBe(true);
17 | expect(wrapper.hasClass('btn--disabled')).toBe(false);
18 | });
19 |
20 | it('onclick fires prop', () => {
21 | const clickFunc = jest.fn();
22 | const wrapper = mount();
23 | wrapper.simulate('click');
24 | expect(clickFunc).toHaveBeenCalled();
25 | });
26 |
27 | it('onblur fires prop', () => {
28 | const clickFunc = jest.fn();
29 | const wrapper = mount();
30 | wrapper.simulate('blur');
31 | expect(clickFunc).toHaveBeenCalled();
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/src/components/FormFakeSelect/_FakeSelect.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/var/colors";
2 | @import "../../_scss/w98/mixins/box-shadows";
3 | @import "../../_scss/w98/functions/box-shadows";
4 | @import "../../_scss/w98/var/var";
5 | @import "../../_scss/w98/var/uris";
6 |
7 | $select-height: 22px;
8 |
9 | .FakeSelect {
10 | position: relative;
11 | display: flex;
12 | height: $select-height;
13 | align-self: center;
14 | align-items: center;
15 | background-color: #ffffff;
16 | overflow: hidden;
17 | @include shadow-input;
18 | &__icon {
19 | margin-left: 6px;
20 | height: 16px;
21 | }
22 | &__children {
23 | margin-left: 6px;
24 | margin-right: 28px;
25 | white-space: nowrap;
26 | overflow: hidden;
27 | text-overflow: ellipsis;
28 | }
29 |
30 | &__arrow {
31 | position: absolute;
32 | box-shadow: dualShadow($grey, $black), dualShadow(#ffffff, $darkgrey, 2);
33 | height: $select-height - 4;
34 | width: $select-height - 4;
35 | left: calc(100% - 20px);
36 | top: 2px;
37 | background-color: $grey;
38 | background-repeat: no-repeat;
39 | background-position: center;
40 | background-image: url($arrow-down);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Logs
3 | logs
4 | *.log
5 | npm-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 |
9 | # Runtime data
10 | pids
11 | *.pid
12 | *.seed
13 | *.pid.lock
14 |
15 | # Directory for instrumented libs generated by jscoverage/JSCover
16 | lib-cov
17 |
18 | # Coverage directory used by tools like istanbul
19 | coverage
20 |
21 | # nyc test coverage
22 | .nyc_output
23 |
24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
25 | .grunt
26 |
27 | # Bower dependency directory (https://bower.io/)
28 | bower_components
29 |
30 | # node-waf configuration
31 | .lock-wscript
32 |
33 | # Compiled binary addons (http://nodejs.org/api/addons.html)
34 | build/Release
35 |
36 | # Dependency directories
37 | node_modules/
38 | jspm_packages/
39 |
40 | # Typescript v1 declaration files
41 | typings/
42 |
43 | # Optional npm cache directory
44 | .npm
45 |
46 | # Optional eslint cache
47 | .eslintcache
48 |
49 | # Optional REPL history
50 | .node_repl_history
51 |
52 | # Output of 'npm pack'
53 | *.tgz
54 |
55 | # Yarn Integrity file
56 | .yarn-integrity
57 |
58 | # dotenv environment variables file
59 | .env
60 |
61 | # customs
62 | **/.DS_Store
63 | /.nyc_output/
64 | /.vscode/
65 | /.cache/
66 | /.out/
67 | .out
--------------------------------------------------------------------------------
/src/components/ButtonForm/FormButton.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import ButtonForm from './ButtonForm';
4 |
5 | describe('ButtonForm', () => {
6 | it('renders', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('.ButtonForm').length).toBeTruthy();
9 | });
10 |
11 | it('renders children', () => {
12 | const wrapper = shallow(
13 |
14 |
15 |
16 | );
17 | expect(wrapper.find('#test').length).toBeTruthy();
18 | });
19 |
20 | it('accepts classes passed in', () => {
21 | const wrapper = shallow(
22 |
23 | ).render();
24 | expect(wrapper.hasClass('test')).toBe(true);
25 | expect(wrapper.hasClass('btn--active')).toBe(true);
26 | expect(wrapper.hasClass('btn--disabled')).toBe(true);
27 | });
28 |
29 | it('onclick focuses and fires prop', () => {
30 | const clickFunc = jest.fn();
31 | const testString = 'TestString';
32 | const wrapper = mount(
33 | {testString}
34 | );
35 | wrapper.simulate('click');
36 | expect(document.activeElement.innerHTML).toBe(testString);
37 | expect(clickFunc).toHaveBeenCalled();
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/src/components/TaskBar/__tests__/TaskBar.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { mount } from 'enzyme';
3 | import TaskBar from '../TaskBar';
4 |
5 | const menuOptions = (onClick = jest.fn()) => [
6 | { alt: 'open', onClick, title: 'testButton', icon: 'icon1' },
7 | { alt: 'find', onClick, title: 'testOption', icon: 'icon2' },
8 | ];
9 |
10 | describe('TaskBar', () => {
11 | const quickLaunchFunc = jest.fn();
12 | const notiferFunc = jest.fn();
13 | const programFunc = jest.fn();
14 | const wrapper = mount(
15 |
21 | );
22 | it('renders all as expected', () => {
23 | expect(wrapper.find('.TaskBar').length).toBeTruthy();
24 | expect(wrapper.find('.TaskBar__quick-launch').length).toBeTruthy();
25 | expect(wrapper.find('Notifier')).toHaveLength(2);
26 | expect(wrapper.find('ButtonProgram')).toHaveLength(2);
27 | expect(wrapper.find('ButtonIconSmall')).toHaveLength(2);
28 | });
29 |
30 | it('only renders quicklaunch holder when passed props', () => {
31 | wrapper.setProps({ quickLaunch: null });
32 | expect(wrapper.find('.TaskBar__quick-launch').length).toBeFalsy();
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/src/components/ButtonIconLarge/LargeIconButton.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import ButtonIconLarge from './ButtonIconLarge';
4 |
5 | describe('ButtonIconLarge', () => {
6 | it('renders', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('.ButtonIconLarge').length).toBeTruthy();
9 | });
10 |
11 | it('renders icon', () => {
12 | const wrapper = mount();
13 | expect(wrapper.find('img').props().src).toBe('TEST');
14 | expect(
15 | wrapper
16 | .find('AbstractButton')
17 | .at(0)
18 | .text()
19 | .includes('TEST')
20 | ).toBeTruthy();
21 | });
22 |
23 | it('accepts classes passed in', () => {
24 | const wrapper = shallow(
25 |
26 | ).render();
27 | expect(wrapper.hasClass('test')).toBe(true);
28 | expect(wrapper.hasClass('btn--active')).toBe(false);
29 | expect(wrapper.hasClass('btn--disabled')).toBe(true);
30 | });
31 |
32 | it('onclick focuses and fires prop', () => {
33 | const clickFunc = jest.fn();
34 | const wrapper = mount();
35 | wrapper.simulate('click');
36 | expect(clickFunc).toHaveBeenCalled();
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/src/components/FormInputText/InputText.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import InputText from './InputText';
4 |
5 | describe('InputText', () => {
6 | it('renders', () => {
7 | const wrapper = shallow();
8 | expect(
9 | wrapper
10 | .find('input')
11 | .at(0)
12 | .props().type
13 | ).toBe('text');
14 | });
15 |
16 | it('renders passed class name', () => {
17 | const wrapper = shallow();
18 | expect(wrapper.find('input').hasClass('InputText')).toBeTruthy();
19 | });
20 |
21 | it('disabled field', () => {
22 | const wrapper = shallow();
23 | const wrapperProps = wrapper
24 | .find('input')
25 | .at(0)
26 | .props();
27 | expect(wrapperProps.disabled).toBe(true);
28 | });
29 |
30 | it('onchange fires', () => {
31 | const clickFunc = jest.fn();
32 | const wrapper = mount();
33 | wrapper.find('input').simulate('change');
34 | expect(clickFunc).toHaveBeenCalled();
35 | });
36 | it('onblur fires', () => {
37 | const clickFunc = jest.fn();
38 | const wrapper = mount();
39 | wrapper.find('input').simulate('blur');
40 | expect(clickFunc).toHaveBeenCalled();
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/src/components/WindowAlert/WindowAlert.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 | import WindowAbstract from '../Window/Window';
5 | import ButtonForm from '../ButtonForm';
6 |
7 | import './_WindowAlert.scss';
8 |
9 | const WindowAlert = props => (
10 |
17 |
21 | {props.children}
22 |
23 |
24 | {props.onOK && (
25 |
26 | OK
27 |
28 | )}
29 | {props.onCancel && (
30 |
31 | Cancel
32 |
33 | )}
34 |
35 |
36 | );
37 |
38 | WindowAlert.propTypes = {
39 | ...WindowAbstract.propTypes,
40 | onOK: PropTypes.func,
41 | onCancel: PropTypes.func,
42 | children: PropTypes.node,
43 | icon: PropTypes.string,
44 | };
45 |
46 | export default WindowAlert;
47 |
--------------------------------------------------------------------------------
/src/components/WindowAction/_styles.scss:
--------------------------------------------------------------------------------
1 | .WindowAction {
2 | width: 80%;
3 | max-width: 350px;
4 | min-width: 280px;
5 |
6 | > :not(:first-child) {
7 | margin: 4px 5px;
8 | width: auto;
9 | }
10 |
11 | .Window__title {
12 | text-transform: capitalize;
13 | }
14 |
15 | &__location {
16 | display: flex;
17 | align-items: center;
18 | margin: 3px;
19 | text-transform: capitalize;
20 |
21 | .btn {
22 | margin-right: 2px;
23 | }
24 |
25 | .FakeSelect {
26 | flex-grow: 1;
27 | margin: auto 4px;
28 | }
29 | }
30 |
31 | &__files {
32 | width: initial;
33 | margin: 5px 3px;
34 | }
35 |
36 | &__footer {
37 | margin: 3px;
38 | display: flex;
39 | }
40 |
41 | &__input {
42 | display: flex;
43 | align-items: center;
44 | .FakeSelect {
45 | margin-left: auto;
46 | width: 75%;
47 | max-width: 180px;
48 | min-width: 140px;
49 | }
50 | .InputText {
51 | margin-left: auto;
52 | width: calc(75% - 6px);
53 | max-width: 174px;
54 | min-width: 136px;
55 | }
56 | }
57 |
58 | &__action-inputs {
59 | flex-grow: 1;
60 | }
61 |
62 | &__input,
63 | .ButtonForm {
64 | margin-top: 4px;
65 | }
66 |
67 | &__action-buttons {
68 | display: inline-flex;
69 | flex-direction: column;
70 | margin-left: 8px;
71 | .btn {
72 | text-transform: capitalize;
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/.storybook/config.old.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { configure, addDecorator } from '@storybook/react';
3 | import { withOptions } from '@storybook/addon-options';
4 | import { withNotes } from '@storybook/addon-notes';
5 |
6 | import Theme from '../src/components/Theme';
7 |
8 | addDecorator(story => (
9 |
10 |
17 | {story()}
18 |
19 |
20 | ));
21 |
22 | addDecorator(
23 | withOptions({
24 | name: 'Packard-Belle',
25 | url: 'https://github.com/padraigfl/packard-belle?selector',
26 | theme: {
27 | mainBackground: '#bbc3c4',
28 | mainBorder: '2px solid rgba(0,0,0,0)',
29 | mainTextFace: 'monospace',
30 | barFill: 'linear-gradient(to right, #0000a2, #126fc2)',
31 | barTextColor: 'white',
32 | },
33 | })
34 | );
35 |
36 | addDecorator(withNotes);
37 |
38 | function loadStories() {
39 | require('./stories/taskbar.js');
40 | require('./stories/buttons.js');
41 | require('./stories/windows.js');
42 | require('./stories/contextMenu.js');
43 | require('./stories/icons.js');
44 | require('./stories/scrollbar.js');
45 | require('./stories/inputs.js');
46 | require('./stories/start.js');
47 | require('./stories/desktop.js');
48 | }
49 |
50 | configure(loadStories, module);
51 |
--------------------------------------------------------------------------------
/src/components/ButtonProgram/ProgramButton.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import ButtonProgram from '../ButtonProgram';
4 | import AbstractButton from '../Button';
5 |
6 | describe('ButtonProgram', () => {
7 | it('renders', () => {
8 | const wrapper = shallow();
9 | expect(wrapper.find('.ButtonProgram').length).toBeTruthy();
10 | });
11 |
12 | it('renders children', () => {
13 | const wrapper = shallow(
14 |
15 |
16 |
17 | );
18 | expect(wrapper.find('#test').length).toBeTruthy();
19 | });
20 |
21 | it('accepts classes passed in', () => {
22 | const wrapper = shallow(
23 |
24 | ).render();
25 | expect(wrapper.hasClass('test')).toBe(true);
26 | expect(wrapper.hasClass('btn--active')).toBe(true);
27 | expect(wrapper.hasClass('btn--disabled')).toBe(false);
28 | });
29 |
30 | it('onclick focuses and fires prop', () => {
31 | const clickFunc = jest.fn();
32 | const wrapper = mount();
33 | wrapper.simulate('click');
34 | expect(clickFunc).toHaveBeenCalled();
35 | });
36 |
37 | it('icon is passed into style', () => {
38 | const wrapper = mount();
39 | expect(wrapper.find(AbstractButton).props().style.backgroundImage).toBe(
40 | 'url(ICON)'
41 | );
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/.storybook/stories/desktop.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ExplorerView from '../../src/components/ExplorerView';
3 | import img from './directory_closed.png';
4 |
5 | const noop = () => {
6 | console.log('run');
7 | };
8 |
9 | const options = [
10 | {
11 | title: 'Test0 With a very very very long name oh wait is this okay',
12 | onClick: noop,
13 | icon: img,
14 | },
15 | { title: 'Test1', onClick: noop, icon: img },
16 | { title: 'Test2', onClick: noop, icon: img },
17 | { title: 'Test3', onClick: noop, icon: img },
18 | { title: 'Test4', onClick: noop, icon: img },
19 | { title: 'Test5', onClick: noop, icon: img },
20 | { title: 'Test6', onClick: noop, icon: img },
21 | { title: 'Test7', onClick: noop, icon: img },
22 | { title: 'Test8', onClick: noop, icon: img },
23 | { title: 'Test9', onClick: noop, icon: img },
24 | { title: 'TestA', onClick: noop, icon: img },
25 | { title: 'TestB', onClick: noop, icon: img },
26 | { title: 'TestC', onClick: noop, icon: img },
27 | { title: 'TestD', onClick: noop, icon: img },
28 | { title: 'TestE', onClick: noop, icon: img },
29 | { title: 'TestF', onClick: noop, icon: img },
30 | ];
31 |
32 | const Desktop = {
33 | render: () => (
34 |
41 |
42 |
43 | )
44 | }
45 |
46 | const meta = {
47 | component: Desktop,
48 | }
49 | export default meta;
--------------------------------------------------------------------------------
/.storybook/stories/start.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import StartMenu from '../../src/components/StartMenu';
3 |
4 | import img from './directory_closed.png';
5 |
6 | const noop = () => {};
7 |
8 | const optionsSample = [
9 | {
10 | onClick: noop,
11 | title: 'Windows Update',
12 | icon: img,
13 | },
14 | [
15 | {
16 | onClick: noop,
17 | title: 'Programs',
18 | icon: img,
19 | options: [
20 | {
21 | onClick: noop,
22 | title: 'Accessories',
23 | icon: img,
24 | options: [
25 | {
26 | onClick: noop,
27 | title: 'Notepad?',
28 | icon: img,
29 | },
30 | {
31 | onClick: noop,
32 | title: 'fly it',
33 | icon: img,
34 | },
35 | ],
36 | },
37 | {
38 | onClick: noop,
39 | title: 'open file?',
40 | icon: img,
41 | },
42 | ]
43 | },
44 | {
45 | onClick: noop,
46 | title: 'Control Panel',
47 | icon: img,
48 | options: [],
49 | }
50 | ],
51 | {
52 | onClick: noop,
53 | title: 'Shut Down',
54 | icon: img,
55 | },
56 | ];
57 |
58 | const Start = {
59 | render: () => (
60 |
61 |
64 |
65 | )
66 | };
67 |
68 | const meta = {
69 | component: Start,
70 | }
71 | export default meta;
--------------------------------------------------------------------------------
/src/components/FormSelectDISABLED/Select.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Select from 'react-select';
3 | import { shallow } from 'enzyme';
4 | import WSelect from './Select';
5 |
6 | describe('Select', () => {
7 | it('renders react select', () => {
8 | const wrapper = shallow(
9 |
10 | );
11 | expect(wrapper.find(Select).length).toBeTruthy();
12 | });
13 |
14 | it('renders options', () => {
15 | const options = [{ value: 'abc' }, { value: 'def' }];
16 | const wrapper = shallow();
17 | expect(
18 | wrapper
19 | .find(Select)
20 | .at(0)
21 | .props().options
22 | ).toBe(options);
23 | });
24 |
25 | it('whole box can be disabled', () => {
26 | const wrapper = shallow(
27 |
28 | );
29 | expect(wrapper.find(Select).props().disabled).toBe(true);
30 | });
31 |
32 | it('no state value if controlled component', () => {
33 | const wrapper = shallow(
34 |
35 | );
36 | expect(wrapper.state().value).toBe(null);
37 | });
38 |
39 | it('state value if controlled component, uses prop value as initial value', () => {
40 | const wrapper = shallow(
41 |
42 | );
43 | expect(wrapper.state().value).toBe('abc');
44 | });
45 |
46 | xit('needs more tests, low priority', () => {});
47 | });
48 |
--------------------------------------------------------------------------------
/src/components/FormSelectBox/SelectBox.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import SelectBox from './SelectBox';
4 |
5 | describe('SelectBox', () => {
6 | it('renders', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('div.SelectBox').length).toBeTruthy();
9 | });
10 |
11 | it('whole box can be disabled', () => {
12 | const wrapper = shallow();
13 | expect(wrapper.find('div.SelectBox.disabled').length).toBeTruthy();
14 | });
15 |
16 | it('uses passed component for options', () => {
17 | const FakeComp = () => ;
18 | const wrapper = shallow(
19 |
27 | );
28 | expect(wrapper.find(FakeComp)).toHaveLength(2);
29 | });
30 |
31 | it('selected options have is-active class', () => {
32 | const options = [
33 | { value: 'abc', title: 'def' },
34 | { value: 'ghi', title: 'jkl' },
35 | ];
36 | const wrapper = mount();
37 | expect(wrapper.find('.is-active')).toHaveLength(0);
38 |
39 | wrapper.setProps({ selected: 'ghi' });
40 | expect(wrapper.find('.is-active')).toHaveLength(1);
41 |
42 | wrapper.setProps({ selected: ['abc', 'ghi'] });
43 | expect(wrapper.find('.is-active')).toHaveLength(2);
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/rollup.config.babel.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel';
2 | import resolve from 'rollup-plugin-node-resolve';
3 | import commonjs from 'rollup-plugin-commonjs';
4 | import replace from 'rollup-plugin-replace';
5 | import postcss from 'rollup-plugin-postcss';
6 | import image from 'rollup-plugin-img';
7 | import autoExternal from 'rollup-plugin-auto-external';
8 |
9 | const moduleOptions = {
10 | name: 'PackardBelle',
11 | globals: {
12 | react: 'React',
13 | 'react-dom': 'ReactDOM',
14 | clone: 'clone',
15 | 'prop-types': 'PropTypes',
16 | classnames: 'classnames',
17 | 'react-select': 'ReactSelect',
18 | },
19 | sourcemap: true,
20 | };
21 |
22 | export default {
23 | input: './src/index.js',
24 |
25 | output: [
26 | {
27 | ...moduleOptions,
28 | file: './build/pb.js',
29 | format: 'umd',
30 | },
31 | {
32 | ...moduleOptions,
33 | file: 'build/pb.module.js',
34 | format: 'es',
35 | },
36 | ],
37 |
38 | plugins: [
39 | image({
40 | output: './build/images', // default the root
41 | extensions: /\.(png|jpg|jpeg|gif|svg)$/, // support png|jpg|jpeg|gif|svg, and it's alse the default value
42 | limit: 8192, // default 8192(8k)
43 | exclude: 'node_modules/**',
44 | }),
45 | postcss({
46 | plugins: [],
47 | // modules: true,
48 | }),
49 | resolve({
50 | extensions: ['.mjs', '.js', '.jsx', '.json', '.node', '.ttf'],
51 | }),
52 | autoExternal(),
53 | babel({
54 | exclude: 'node_modules/**',
55 | }),
56 | replace({
57 | 'process.env.NODE_ENV': JSON.stringify('development'),
58 | }),
59 | commonjs(),
60 | ],
61 |
62 | external: ['react', 'react-dom'],
63 | };
64 |
--------------------------------------------------------------------------------
/playroom/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | module.exports = {
3 | Theme: require('../src/components/Theme').default,
4 | ButtonForm: require('../src/components/ButtonForm').default,
5 | ButtonNav: require('../src/components/ButtonNav').default,
6 | ButtonProgram: require('../src/components/ButtonProgram').default,
7 | StartButton: require('../src/components/ButtonStart').default,
8 | ButtonIconLarge: require('../src/components/ButtonIconLarge').default,
9 | ButtonIconSmall: require('../src/components/ButtonIconSmall').default,
10 | StandardMenu: require('../src/components/StandardMenu').default,
11 | ExplorerIcon: require('../src/components/IconExplorerIcon').default,
12 | ListIcon: require('../src/components/IconListIcon').default,
13 | ExplorerView: require('../src/components/ExplorerView').default,
14 | Checkbox: require('../src/components/FormCheckbox').default,
15 | Radio: require('../src/components/FormRadio').default,
16 | InputText: require('../src/components/FormInputText').default,
17 | Select: require('../src/components/FormSelect').default,
18 | FakeSelect: require('../src/components/FormFakeSelect').default,
19 | SelectBox: require('../src/components/FormSelectBox').default,
20 | SelectBoxSimple: require('../src/components/FormSelectBoxSimple').default,
21 | StartMenu: require('../src/components/StartMenu').default,
22 | TaskBar: require('../src/components/TaskBar').default,
23 | Window: require('../src/components/Window').default,
24 | WindowAlert: require('../src/components/WindowAlert').default,
25 | WindowExplorer: require('../src/components/WindowExplorer').default,
26 | WindowProgram: require('../src/components/WindowProgram').default,
27 | DetailsSection: require('../src/components/DetailsSection').default,
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/FormInputText/InputText.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import cx from 'classnames';
3 | import PropTypes from 'prop-types';
4 |
5 | import './_InputText.scss';
6 |
7 | class InputText extends Component {
8 | static defaultProps = {
9 | onChange: () => {},
10 | onKeyDown: () => {},
11 | onBlur: () => {},
12 | onFocus: () => {},
13 | };
14 |
15 | state = {
16 | value: this.props.initialValue,
17 | };
18 |
19 | handleChange = e => {
20 | if (this.props.initialValue) {
21 | this.setState({
22 | value: e.target.value,
23 | });
24 | }
25 |
26 | this.props.onChange(e.target.value);
27 | };
28 |
29 | handleBlur = () => {
30 | this.props.onBlur(this.state.value);
31 | };
32 |
33 | render() {
34 | return (
35 |
47 | );
48 | }
49 | }
50 |
51 | InputText.propTypes = {
52 | className: PropTypes.string,
53 | value: PropTypes.string,
54 | initialValue: PropTypes.string,
55 | isDisabled: PropTypes.bool,
56 | id: PropTypes.string,
57 | name: PropTypes.string,
58 | onBlur: PropTypes.func.isRequired,
59 | onChange: PropTypes.func.isRequired,
60 | onFocus: PropTypes.func.isRequired,
61 | onKeyDown: PropTypes.func.isRequired,
62 | };
63 |
64 | export default InputText;
65 |
--------------------------------------------------------------------------------
/.storybook/manager-head.html:
--------------------------------------------------------------------------------
1 |
49 |
--------------------------------------------------------------------------------
/src/components/TaskBar/Notifications.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Notifier from './Notifier';
4 |
5 | const INTERVALS = 20000;
6 |
7 | const formatTime = date => {
8 | let hour = date.getHours();
9 | let min = date.getMinutes();
10 |
11 | if (hour < 10) { hour = '0' + hour; }
12 | if (min < 10) { min = '0' + min; }
13 |
14 | return hour+':'+min;
15 | };
16 |
17 | export class Time extends React.Component {
18 | state = {
19 | time: this.props.time? new Date(this.props.time) : new Date(),
20 | }
21 |
22 | componentDidMount() {
23 | if (!this.props.fixedTime) {
24 | this.timerId = setInterval(() => {
25 | this.getDate();
26 | }, INTERVALS);
27 | }
28 | }
29 |
30 | componentWillUnmount() {
31 | if(this.timerId) {
32 | clearInterval(this.timerId);
33 | }
34 | }
35 |
36 | getDate() {
37 | this.setState({ time: new Date(this.state.time.getTime() + INTERVALS) });
38 | }
39 |
40 | render() {
41 | return (
42 |
43 | { formatTime(this.state.time) }
44 |
45 | );
46 | }
47 | }
48 |
49 | const Notifications = props => (
50 |
51 | {
52 | props.notifiers.map( notifier => (
53 |
59 | ))
60 | }
61 |
62 |
63 | );
64 |
65 | Notifications.propsTypes = {
66 | notifiers: PropTypes.arrayOf(PropTypes.shape(Notifier.propTypes)),
67 | };
68 |
69 | Notifications.defaultProps = {
70 | notifiers: [],
71 | };
72 |
73 | export default Notifications;
74 |
--------------------------------------------------------------------------------
/src/components/WindowExplorer/WindowExplorer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import WindowProgram from '../WindowProgram';
4 | import Select from '../FormFakeSelect';
5 | import OptionsList from '../ResizableIconsList';
6 | import './_WindowExplorer.scss';
7 |
8 | class WindowExplorer extends React.Component {
9 | static defaultProps = {
10 | footer: [],
11 | menuOptions: [],
12 | };
13 |
14 | render() {
15 | const { props } = this;
16 | return (
17 |
31 | {props.explorerOptions && (
32 |
36 | )}
37 |
45 | {props.children}
46 |
47 | );
48 | }
49 | }
50 |
51 | WindowExplorer.propTypes = {
52 | ...WindowProgram.propTypes,
53 | explorerOptions: OptionsList.propTypes.options,
54 | };
55 |
56 | export default WindowExplorer;
57 |
--------------------------------------------------------------------------------
/src/components/TaskBar/TaskBar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Notifiations from './Notifications';
4 | import StartMenu from '../StartMenu';
5 | import ButtonProgram from '../ButtonProgram';
6 | import ButtonIconSmall from '../ButtonIconSmall';
7 |
8 | import './_TaskBar.scss';
9 |
10 | const TaskBar = props => (
11 |
12 |
13 | {props.quickLaunch && (
14 |
15 | {props.quickLaunch.map(qlEntry => (
16 |
22 | ))}
23 |
24 | )}
25 |
26 | {props.openWindows &&
27 | props.openWindows.map(openWindow => (
28 |
36 | {openWindow.title}
37 |
38 | ))}
39 |
40 |
41 |
42 | );
43 |
44 | TaskBar.defaultProps = {
45 | openWindows: [],
46 | notifiers: [],
47 | quickLaunch: [],
48 | options: [],
49 | };
50 |
51 | TaskBar.propTypes = {
52 | options: PropTypes.array,
53 | quickLaunch: PropTypes.arrayOf(PropTypes.shape(ButtonIconSmall.propTypes)),
54 | openWindows: PropTypes.arrayOf(PropTypes.shape(ButtonProgram.propTypes)),
55 | notifiers: PropTypes.arrayOf(PropTypes.shape(Notifiations.propsTypes)),
56 | };
57 |
58 | export default TaskBar;
59 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export { default as Theme } from './components/Theme';
2 |
3 | export { default as ButtonForm } from './components/ButtonForm';
4 | export { default as ButtonNav } from './components/ButtonNav';
5 | export { default as ButtonProgram } from './components/ButtonProgram';
6 | export { default as StartButton } from './components/ButtonStart';
7 | export { default as ButtonIconLarge } from './components/ButtonIconLarge';
8 | export { default as ButtonIconSmall } from './components/ButtonIconSmall';
9 |
10 | export { default as StandardMenu } from './components/StandardMenu';
11 |
12 | export { default as ExplorerIcon } from './components/IconExplorerIcon';
13 | export { default as ListIcon } from './components/IconListIcon';
14 |
15 | export { default as ExplorerView } from './components/ExplorerView';
16 |
17 | export { default as Checkbox } from './components/FormCheckbox';
18 | export { default as Radio } from './components/FormRadio';
19 | export { default as InputText } from './components/FormInputText';
20 | // export { default as Select } from './components/FormSelect';
21 | export { default as FakeSelect } from './components/FormFakeSelect';
22 | export { default as SelectBox } from './components/FormSelectBox';
23 | export { default as SelectBoxSimple } from './components/FormSelectBoxSimple';
24 |
25 | export { default as StartMenu } from './components/StartMenu';
26 |
27 | export { default as TaskBar } from './components/TaskBar';
28 | export { default as MenuBar } from './components/MenuBar';
29 | export { default as Window } from './components/Window';
30 | export { default as WindowAlert } from './components/WindowAlert';
31 | export { default as WindowAction } from './components/WindowAction';
32 | export { default as WindowExplorer } from './components/WindowExplorer';
33 | export { default as WindowProgram } from './components/WindowProgram';
34 | export { default as DetailsSection } from './components/DetailsSection';
35 |
--------------------------------------------------------------------------------
/src/components/ResizableIconsList/_options-list.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/functions/box-shadows";
2 | @import "../../_scss/w98/var/colors";
3 | @import "../../_scss/w98/var/uris";
4 |
5 | .OptionsList {
6 | display: block;
7 | min-height: initial;
8 | z-index: 10;
9 | &__large-icons {
10 | display: flex;
11 | overflow: hidden;
12 | }
13 | &__dropdown {
14 | &--empty {
15 | display: none;
16 | }
17 | position: absolute;
18 | right: 2px;
19 | top: 2px;
20 | height: calc(100% - 4px);
21 | &__button {
22 | height: 100%;
23 | border: none;
24 | background-color: $grey;
25 | background-image: url($more-options);
26 | background-repeat: no-repeat;
27 | background-position: 2px 3px;
28 | padding: 0px 6px;
29 | font-size: 0.7rem;
30 | user-select: none;
31 | letter-spacing: -2px;
32 | display: flex;
33 | flex-direction: column;
34 | &:hover {
35 | box-shadow: dualShadow(#ffffff, $darkgrey);
36 | }
37 | &:active,
38 | &:focus,
39 | &:active:focus {
40 | outline: none;
41 | background-position: 3px 4px;
42 | box-shadow: dualShadow($darkgrey, #ffffff);
43 | // stylelint-disable-next-line selector-max-specificity
44 | + .OptionsList__dropdown__list {
45 | position: absolute;
46 | top: 100%;
47 | right: 0px;
48 | display: block;
49 | z-index: 10;
50 | }
51 | }
52 | }
53 | }
54 | .OptionsList__dropdown__list {
55 | display: none;
56 | }
57 | .OptionsList__dropdown__button {
58 | margin-left: auto;
59 | }
60 | .StandardMenuItem__button:hover {
61 | background-color: $blue;
62 | color: #ffffff;
63 | }
64 | .divider {
65 | border-left: 1px solid $darkgrey;
66 | border-right: 1px solid #ffffff;
67 | width: 1px;
68 | margin: 2px 3px;
69 | + .divider {
70 | display: none;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/components/Icon/AbstractIcon.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import AbstractIcon from './Icon';
4 |
5 | describe('AbstractIcon', () => {
6 | it('renders', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('div').length).toBeTruthy();
9 | });
10 |
11 | it('renders button when passed onclick', () => {
12 | const wrapper = shallow();
13 | expect(wrapper.find('button').length).toBeTruthy();
14 | });
15 |
16 | it('onclick focuses and fires prop', () => {
17 | const clickFunc = jest.fn();
18 | const wrapper = mount();
19 | wrapper.simulate('click');
20 | expect(clickFunc).toHaveBeenCalled();
21 | });
22 |
23 | it('onDoubleClick if passed in as prop', () => {
24 | const dblClck = jest.fn();
25 | const wrapper = mount();
26 | wrapper.simulate('click');
27 | expect(wrapper.state().doubleReady).toEqual(true);
28 | wrapper.simulate('click');
29 | expect(dblClck).toHaveBeenCalled();
30 | });
31 |
32 | it('onDoubleClick if touchend', () => {
33 | const dblClck = jest.fn();
34 | const wrapper = shallow();
35 | wrapper.simulate('touchend');
36 | expect(dblClck).toHaveBeenCalled();
37 | });
38 |
39 | it('onContextMenu on works if passed in as prop', () => {
40 | const mockEvent = {
41 | preventDefault: jest.fn(),
42 | stopPropagation: jest.fn(),
43 | };
44 | const onContextMenu = jest.fn();
45 | const wrapper = mount();
46 | wrapper.simulate('contextmenu');
47 | expect(onContextMenu).not.toHaveBeenCalled();
48 |
49 | wrapper.setProps({ onContextMenu });
50 | wrapper.simulate('contextmenu', mockEvent);
51 | expect(onContextMenu).toHaveBeenCalled();
52 | expect(mockEvent.preventDefault).toHaveBeenCalled();
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/src/components/MenuBar/index.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import MenuBar from '.';
4 |
5 | const menuOptions = (onClick, onOptionClick = jest.fn()) => [
6 | {
7 | title: 'file',
8 | options: [
9 | { title: 'open', onClick, className: 'testButton' },
10 | {
11 | title: 'find',
12 | onOptionClick,
13 | className: 'testOption',
14 | options: [{ title: 'test', onClick }],
15 | },
16 | ],
17 | },
18 | ];
19 |
20 | describe('MenuBar', () => {
21 | it('renders', () => {
22 | const wrapper = shallow();
23 | expect(wrapper.find('menu').length).toBeTruthy();
24 | });
25 |
26 | it('renders button and standard menu if passed props', () => {
27 | const options = menuOptions(jest.fn());
28 | const wrapper = mount();
29 | expect(wrapper.find('AbstractButton').length).toBeTruthy();
30 | expect(wrapper.find('StandardMenu').length).toBeTruthy();
31 | });
32 |
33 | it('cant call onclick on button with option', () => {
34 | const func = jest.fn();
35 | const options = menuOptions(jest.fn(), func);
36 | const wrapper = mount();
37 | expect(wrapper.find('.testOption').length).toBeTruthy();
38 | wrapper
39 | .find('.testButton')
40 | .at(0)
41 | .find('button')
42 | .at(0)
43 | .simulate('click');
44 | expect(func).not.toHaveBeenCalled();
45 | });
46 |
47 | it('can call onclick prop child option', () => {
48 | const func = jest.fn();
49 | const options = menuOptions(func);
50 | const wrapper = mount();
51 | expect(wrapper.find('.testButton').length).toBeTruthy();
52 | wrapper
53 | .find('.testButton')
54 | .at(0)
55 | .find('button')
56 | .at(0)
57 | .simulate('click');
58 | expect(func).toHaveBeenCalled();
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/src/components/StandardMenu/StandardMenuItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 |
5 | const StandardMenuItem = props => (
6 |
16 |
30 | {props.options && (
31 |
38 | )}
39 |
40 | );
41 |
42 | StandardMenuItem.defaultProps = {
43 | onClick: () => {},
44 | closeOnClick: () => {
45 | console.error('Menus require a closeOnClick prop to function correctly'); // eslint-disable-line
46 | },
47 | value: [],
48 | };
49 |
50 | StandardMenuItem.propTypes = {
51 | className: PropTypes.string,
52 | title: PropTypes.string.isRequired,
53 | icon: PropTypes.string,
54 | value: PropTypes.arrayOf(PropTypes.string),
55 | mouseEnterItem: PropTypes.func,
56 | options: PropTypes.any,
57 | isDisabled: PropTypes.bool,
58 | isActive: PropTypes.bool,
59 | onClick: PropTypes.func,
60 | type: PropTypes.string,
61 | };
62 |
63 | export const standardMenuItemProps = StandardMenuItem.propTypes;
64 |
65 | export default StandardMenuItem;
66 |
--------------------------------------------------------------------------------
/src/components/FormSelectBox/SelectBox.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 |
5 | import './_SelectBox.scss';
6 |
7 | const isSelected = (selected, val) =>
8 | Array.isArray(selected)
9 | ? selected.some(selectedEntry => selectedEntry === val)
10 | : selected === val;
11 |
12 | const SelectBox = props => {
13 | const Comp = props.component ? props.component : 'button';
14 | return (
15 |
24 |
25 | {props.options.map(option => (
26 | props.onClick(option.value)}
33 | alt={props.component ? option.alt : undefined}
34 | className={cx(option.className, {
35 | 'is-active': isSelected(props.selected, option.value),
36 | })}
37 | icon={props.component ? option.icon : undefined}
38 | title={
39 | option.title ||
40 | (typeof option.value === 'string' ? option.value : '')
41 | }
42 | value={option.value}
43 | />
44 | ))}
45 |
46 |
47 | );
48 | };
49 |
50 | SelectBox.propTypes = {
51 | component: PropTypes.func,
52 | className: PropTypes.string,
53 | title: PropTypes.string,
54 | selected: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
55 | isDisabled: PropTypes.bool,
56 | options: PropTypes.arrayOf(
57 | PropTypes.shape({
58 | value: PropTypes.any,
59 | title: PropTypes.string,
60 | icon: PropTypes.string,
61 | alt: PropTypes.string,
62 | className: PropTypes.string,
63 | })
64 | ),
65 | };
66 |
67 | export default SelectBox;
68 |
--------------------------------------------------------------------------------
/src/_scss/w98_simple.scss:
--------------------------------------------------------------------------------
1 | @import "./w98/theme";
2 | @import "./w98/_buttons";
3 | @import "./w98/_menu";
4 | @import "./w98/_icons";
5 | @import "./w98/_task-bar";
6 | @import "./w98/_inputs";
7 | @import "./w98/_window";
8 |
9 | .btn--form {
10 | @include button-form();
11 | }
12 | .btn--large-icon {
13 | @include button-large-icon();
14 | }
15 | .btn--small-icon {
16 | @include button-small-icon();
17 | }
18 | .btn--nav {
19 | @include button-nav();
20 | }
21 | .btn--program {
22 | @include button-program();
23 | }
24 | .btn--start {
25 | @include button-start();
26 | }
27 |
28 | .icon--list {
29 | @include icon-list();
30 | }
31 | .icon--explorer {
32 | @include icon-explorer();
33 | }
34 |
35 | .standard-menu {
36 | @include standard-menu(".standard-menu");
37 |
38 | @include standard-menu-css(".standard-menu");
39 | }
40 |
41 | input[type="radio"] {
42 | @include radio();
43 | }
44 | input[type="checkbox"] {
45 | @include checkbox();
46 | }
47 | input[type="text"] {
48 | @include input-text();
49 | }
50 | select[multiple] {
51 | @include select-multiple-simple();
52 | }
53 |
54 | .window,
55 | .Window {
56 | @include window-basic();
57 | &__heading {
58 | @include window-heading();
59 | }
60 | &__icon {
61 | @include window-icon();
62 | }
63 | &__title {
64 | @include window-title();
65 | }
66 | &__close {
67 | margin-left: 2px;
68 | background-image: url($close);
69 | }
70 | &__restore {
71 | background-image: url($restore);
72 | }
73 | &__minimize {
74 | background-image: url($minimize);
75 | }
76 | &__maximize {
77 | background-image: url($maximize);
78 | }
79 | &__help {
80 | background-image: url($maximize);
81 | background-image: url($whelp);
82 | }
83 |
84 | &__menu {
85 | @include window-menu();
86 | }
87 |
88 | section {
89 | @include window-settings-section();
90 | }
91 |
92 | &--alert {
93 | @include window-alert();
94 | }
95 | }
96 |
97 | .task-bar {
98 | @include task-bar();
99 | }
100 |
--------------------------------------------------------------------------------
/.storybook/stories/icons.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ExplorerIcon from '../../src/components/IconExplorerIcon';
3 | import ListIcon from '../../src/components/IconListIcon';
4 | import img from './directory_closed.png';
5 |
6 | const noop = () => {
7 | console.log('run');
8 | };
9 |
10 | const alertIt = () => {
11 | window.alert('DoubleClick');
12 | };
13 |
14 |
15 | export const ExplorerIcons = {
16 | render: () => (
17 | <>
18 |
25 |
33 | >
34 | ),
35 | notes: 'Click on item to expose full name',
36 | }
37 | export const ListIcons = {
38 | render: () => (
39 |
40 | noop()}
43 | onDoubleClick={alertIt}
44 | alt="Testing this testing some more"
45 | icon={img}
46 | title="Testing this testing some more"
47 | />
48 |
56 |
64 |
73 |
74 | )
75 | };
76 |
77 | const meta = {
78 | component: ExplorerIcons,
79 | }
80 | export default meta;
--------------------------------------------------------------------------------
/src/images/uris.scss:
--------------------------------------------------------------------------------
1 | $arrow-up: "data:image/gif;base64,R0lGODlhBwAEAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAHAAQAAAIHlGEJq8sOCwA7";
2 | $arrow-down: "data:image/gif;base64,R0lGODlhBwAEAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAHAAQAAAIIhA+CKWoNmSgAOw==";
3 | $arrow-left: "data:image/gif;base64,R0lGODlhBAAHAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAEAAcAAAIIlHEIy7ppBCgAOw==";
4 | $arrow-right: "data:image/gif;base64,R0lGODlhBAAHAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAEAAcAAAIIhA4maeyrlCgAOw==";
5 | $arrow-right-inverted: "data:image/gif;base64,R0lGODlhBAAHAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAEAAcAAAIIjB4maeyrlCgAOw==";
6 | $arrow-down-disabled: "data:image/gif;base64,R0lGODlhCAAFAJEAAAAAAP///5mZmf///yH5BAEAAAMALAAAAAAIAAUAAAIMlC8zKBF6nIJyqqcKADs=";
7 | $menu-checked: "data:image/gif;base64,R0lGODlhBwAHAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAHAAcAAAIMlA9nwMj9xGuLIlUAADs=";
8 | $menu-checked-disabled: "data:image/gif;base64,R0lGODlhBwAHAJEAAAAAAP///5mZmf///yH5BAEAAAMALAAAAAAHAAcAAAIMnC9nwsj9xmuLIlUAADs=";
9 | $menu-radio: "data:image/gif;base64,R0lGODlhBgAGAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAGAAYAAAIIFA6Gy816RAEAOw==";
10 | $radio-off: "data:image/gif;base64,R0lGODlhDAAMAKIAAAAAAP///8zMzJmZmf///wAAAAAAAAAAACH5BAEAAAQALAAAAAAMAAwAAAMqSErTs6uBCVqcIQesBtCaEDAfGAaeeaZqILKqyLQyI4hhTWT3nUEKECQBADs=";
11 | $radio-on: "data:image/gif;base64,R0lGODlhDAAMAKIAAAAAAP///8zMzJmZmf///wAAAAAAAAAAACH5BAEAAAQALAAAAAAMAAwAAAMtSErTs6uBCVqcIQesBtCaEDBfhmWiZ1JooG5skJZwOA6g3QliKC4oXg+iAEESADs=";
12 | $radio-off-disabled: "data:image/gif;base64,R0lGODlhDAAMAKIAAAAAAP///8zMzJmZmf///wAAAAAAAAAAACH5BAEAAAQALAAAAAAMAAwAAAMpSErTs6uBCVqccAY1AFTC1n1LOJKE6aEqmorsxggCRMtEENA3vug6SAIAOw==";
13 | $radio-on-disabled: "data:image/gif;base64,R0lGODlhDAAMAKIAAAAAAP///8zMzJmZmf///wAAAAAAAAAAACH5BAEAAAQALAAAAAAMAAwAAAMtSErTs6uBCVqccAY1AFTC1i0YGIwE5REhqppourLiZ3KCAOWbEgQ5Xg/y+0ESADs=";
14 |
15 | $grey-checkered: "data:image/gif;base64,R0lGODlhAgACAJEAAAAAAP///8zMzP///yH5BAEAAAMALAAAAAACAAIAAAID1CYFADs=";
16 | $blue-checkered: "data:image/gif;base64,R0lGODlhAgACAJEAAAAAAP///wAAt////yH5BAEAAAMALAAAAAACAAIAAAID1CYFADs=";
17 | $blue-checkered-lighter: "data:image/gif;base64,R0lGODlhAgACAJEAAAAAAP///0ZGtP///yH5BAEAAAMALAAAAAACAAIAAAID1CYFADs=";
18 |
--------------------------------------------------------------------------------
/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["stylelint-scss"],
3 | "rules": {
4 | "at-rule-blacklist": [],
5 | "at-rule-name-case": "lower",
6 | "at-rule-name-space-after": "always-single-line",
7 | "at-rule-no-vendor-prefix": true,
8 | "at-rule-semicolon-newline-after": "always",
9 | "at-rule-semicolon-space-before": "never",
10 | "block-closing-brace-newline-before": "always-multi-line",
11 | "block-no-empty": true,
12 | "color-hex-case": "lower",
13 | "color-hex-length": "long",
14 | "color-named": "never",
15 | "color-no-invalid-hex": true,
16 | "declaration-block-single-line-max-declarations": 1,
17 | "declaration-block-trailing-semicolon": "always",
18 | "declaration-no-important": true,
19 | "font-family-no-duplicate-names": true,
20 | "font-family-no-missing-generic-family-keyword": true,
21 | "function-calc-no-unspaced-operator": true,
22 | "function-linear-gradient-no-nonstandard-direction": true,
23 | "function-max-empty-lines": 2,
24 | "no-duplicate-selectors": true,
25 | "no-missing-end-of-source-newline": true,
26 | "number-leading-zero": "always",
27 | "number-no-trailing-zeros": true,
28 | "property-no-unknown": true,
29 | "property-no-vendor-prefix": true,
30 | "selector-attribute-quotes": "always",
31 | "selector-max-class": 5,
32 | "selector-max-combinators": 5,
33 | "selector-max-compound-selectors": 5,
34 | "selector-max-id": 2,
35 | "selector-max-pseudo-class": 3,
36 | "selector-max-specificity": "0,3,2",
37 | "selector-no-vendor-prefix": true,
38 | "selector-pseudo-class-case": "lower",
39 | "selector-pseudo-class-no-unknown": true,
40 | "selector-pseudo-class-parentheses-space-inside": "never",
41 | "selector-pseudo-element-case": "lower",
42 | "selector-pseudo-element-colon-notation": "single",
43 | "selector-pseudo-element-no-unknown": true,
44 | "selector-type-case": "lower",
45 | "selector-type-no-unknown": true,
46 | "shorthand-property-no-redundant-values": true,
47 | "string-no-newline": true,
48 | "string-quotes": "double",
49 | "unit-no-unknown": true,
50 | "value-no-vendor-prefix": true
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/FormSelectBox/_SelectBox.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/var/colors";
2 | @import "../../_scss/w98/mixins/box-shadows";
3 | @import "../../_scss/w98/functions/box-shadows";
4 | @import "../../_scss/w98/var/var";
5 |
6 | .SelectBox {
7 | position: relative;
8 | width: 100%;
9 | background-color: #ffffff;
10 | padding: 2px;
11 |
12 | &:disabled,
13 | &.disabled {
14 | pointer-events: none;
15 | > div {
16 | overflow: hidden;
17 | }
18 | background-color: $grey;
19 | button {
20 | // @todo remove important
21 | color: $darkgrey !important; // stylelint-disable-line
22 | }
23 | .icon {
24 | filter: grayscale(1);
25 | }
26 | }
27 | > div {
28 | position: relative;
29 | overflow: auto;
30 | }
31 |
32 | &:after {
33 | position: absolute;
34 | top: 0px;
35 | left: 0px;
36 | width: 100%;
37 | height: 100%;
38 | @include shadow-input;
39 | pointer-events: none;
40 | content: "";
41 | }
42 |
43 | button:not(.icon) {
44 | display: block;
45 | outline: none;
46 | background: transparent;
47 | border: none;
48 | white-space: nowrap;
49 | overflow: hidden;
50 | color: $black;
51 | width: 100%;
52 | text-align: left;
53 | &:after {
54 | content: attr(title);
55 | position: initial;
56 | }
57 | &.is-active {
58 | background-color: $blue;
59 | color: #ffffff;
60 | outline-offset: -1px;
61 | outline: 1px dotted #ffffff;
62 | }
63 | }
64 |
65 | &--ExplorerIcon {
66 | > div {
67 | display: flex;
68 | flex-direction: row;
69 | overflow-y: hidden;
70 | padding-bottom: 20px;
71 | .explorer-icon {
72 | margin: 2px 8px;
73 | }
74 | }
75 | }
76 | .icon--list {
77 | margin: 0px;
78 | padding: 1px;
79 |
80 | .icon__text {
81 | width: initial;
82 | }
83 |
84 | // stylelint-disable selector-max-specificity
85 | &:focus:not(.is-active),
86 | &:active:not(.is-active) {
87 | .icon__text {
88 | background-color: transparent;
89 | color: $black;
90 | outline: none;
91 | outline-offset: -1px;
92 | }
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/playroom.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | components: './playroom',
3 | outputPath: './dist/playroom',
4 |
5 | // Optional:
6 | title: 'My Awesome Library',
7 | // themes: './src/themes',
8 | frameComponent: './playroom/FrameComponent.js',
9 | widths: [320, 375, 768, 1024],
10 | port: 9000,
11 | openBrowser: true,
12 | exampleCode: `
13 |
14 | {}}
18 | onHelp={() => {}}
19 | onOK={() => {}}
20 | onCancel={() => {}}
21 | >
22 | This is the Playroom environment
23 |
24 |
34 |
35 | `,
36 | webpackConfig: () => ({
37 | // Custom webpack config goes here...
38 | module: {
39 | rules: [
40 | {
41 | test: /\.scss$/,
42 | use: [
43 | 'style-loader', // creates style nodes from JS strings
44 | 'css-loader', // translates CSS into CommonJS
45 | 'sass-loader', // compiles Sass to CSS, using Node Sass by default
46 | ],
47 | },
48 | {
49 | test: /\.(svg|gif|jpg|png|woff|woff2)$/,
50 | use: [
51 | 'file-loader', // compiles Sass to CSS, using Node Sass by default
52 | ],
53 | },
54 | {
55 | test: /\.js$/,
56 | exclude: /node_modules/,
57 | use: [
58 | {
59 | loader: 'babel-loader',
60 | options: {
61 | presets: [
62 | ['@babel/preset-env', { modules: false }],
63 | '@babel/preset-react',
64 | ],
65 | plugins: ['@babel/plugin-proposal-class-properties'],
66 | },
67 | },
68 | ],
69 | },
70 | ],
71 | },
72 | }),
73 | };
74 |
--------------------------------------------------------------------------------
/.storybook/stories/buttons.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import FormButton from '../../src/components/ButtonForm';
4 | import NavButton from '../../src/components/ButtonNav';
5 | import StartButton from '../../src/components/ButtonStart';
6 | import ProgramButton from '../../src/components/ButtonProgram';
7 | import SmallIconButton from '../../src/components/ButtonIconSmall';
8 | import LargeIconButton from '../../src/components/ButtonIconLarge';
9 | import img from './directory_closed.png';
10 |
11 | const noop = () => {
12 | console.log('run');
13 | };
14 |
15 | const meta = {
16 | component: FormButton,
17 | };
18 | export default meta;
19 | export const Form = {
20 | render: () => (
21 |
22 |
23 | Regular
24 |
25 |
26 |
27 |
28 | Pressed
29 |
30 |
31 |
32 |
33 | Disabled
34 |
35 |
36 |
37 | )
38 | };
39 | export const Nav = {
40 | render: () =>
41 | }
42 | export const Start = {
43 | render: () =>
44 | }
45 | export const Program = {
46 | render: () => (
47 |
48 |
49 | Inactive
50 |
51 |
52 | Active
53 |
54 |
55 | Clicked
56 |
57 |
58 | )
59 | }
60 | export const SmallIcon = {
61 | render: () => (
62 |
63 | Regular
64 |
65 | Disabled
66 |
67 | {' '}
73 | Active
74 |
75 | )
76 | }
77 | export const LargeIcon = {
78 | render: () => (
79 |
80 |
81 |
82 |
83 | )
84 | }
85 |
--------------------------------------------------------------------------------
/src/components/WindowProgram/WindowProgram.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 | import WindowAbstract from '../Window/Window';
5 | import MenuBar from '../MenuBar';
6 | import './_WindowProgram.scss';
7 |
8 | const insertDefaultFooter = (customFooterElements, minimum = 2) => {
9 | const footer = Array.isArray(customFooterElements)
10 | ? [...customFooterElements]
11 | : [];
12 | for (let i = 0; i < minimum; i++) {
13 | footer[i] = footer && footer.text ? footer[i] : { text: '' };
14 | }
15 | return footer;
16 | };
17 |
18 | const Footer = (props = []) => (
19 |
33 | );
34 |
35 | const footerType = {
36 | text: PropTypes.string,
37 | icon: PropTypes.string,
38 | };
39 |
40 | Footer.propTypes = {
41 | entries: PropTypes.arrayOf(PropTypes.shape(footerType)),
42 | };
43 |
44 | class WindowProgram extends React.Component {
45 | static defaultProps = {
46 | onMaximize: () => {},
47 | };
48 | render() {
49 | const { props } = this;
50 | const footer = insertDefaultFooter(props.footer);
51 | return (
52 |
64 | {Array.isArray(props.menuOptions) && (
65 |
69 | )}
70 | {props.children}
71 | {props.footer && }
72 |
73 | );
74 | }
75 | }
76 |
77 | WindowProgram.propTypes = {
78 | ...WindowAbstract.propTypes,
79 | menuOptions: PropTypes.arrayOf(PropTypes.any),
80 | footer: PropTypes.arrayOf(PropTypes.shape(footerType)),
81 | };
82 |
83 | export default WindowProgram;
84 |
--------------------------------------------------------------------------------
/src/components/StandardMenu/StandardMenu.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 | import Frame from '../Frame';
5 | import StandardMenuItem from './StandardMenuItem';
6 |
7 | import './_StandardMenu.scss';
8 |
9 | const DIVIDER = 'divider';
10 |
11 | export const flattenWithDividers = options =>
12 | options.reduce((acc, val, idx) => {
13 | if (!Array.isArray(val)) {
14 | acc.push(val);
15 | } else {
16 | acc = acc.concat([
17 | `${DIVIDER}--group-${idx}-start`,
18 | ...val,
19 | `${DIVIDER}--group-${idx}-end`,
20 | ]);
21 | }
22 | return acc;
23 | }, []);
24 |
25 | const StandardMenu = props => {
26 | const options = flattenWithDividers(props.options);
27 | const hasOptions = Array.isArray(options);
28 | return (
29 |
34 | {hasOptions && options.length > 0 ? (
35 | options.map(option => {
36 | if (typeof option === 'string' && option.includes(DIVIDER)) {
37 | return (
38 |
39 | );
40 | }
41 | return (
42 |
50 | );
51 | })
52 | ) : (
53 |
61 | )}
62 |
63 | );
64 | };
65 |
66 | StandardMenu.defaultProps = {
67 | value: [],
68 | };
69 |
70 | export const standardMenuProps = {
71 | value: PropTypes.arrayOf(PropTypes.string),
72 | mouseEnterItem: PropTypes.func,
73 | className: PropTypes.string,
74 | direction: PropTypes.oneOf(['up', 'down', 'left', 'right']),
75 | options: PropTypes.any,
76 | isVisible: PropTypes.bool,
77 | };
78 |
79 | StandardMenu.propTypes = standardMenuProps;
80 |
81 | export default StandardMenu;
82 |
--------------------------------------------------------------------------------
/src/_scss/w98/_icons.scss:
--------------------------------------------------------------------------------
1 | @import "./var/colors";
2 |
3 | @mixin icon($classname: ".icon") {
4 | position: relative;
5 | display: block;
6 | outline: none;
7 | background: none;
8 | border: none;
9 |
10 | color: initial;
11 | text-decoration: none;
12 |
13 | padding: 1px 7px 2px;
14 | .icon__icon {
15 | display: block;
16 | background-size: contain;
17 | background-position: center;
18 | background-repeat: no-repeat;
19 | }
20 |
21 | &:focus,
22 | &:active,
23 | &:active:focus,
24 | &.is-active {
25 | outline: none;
26 |
27 | .icon__icon {
28 | filter: hue-rotate(70deg) contrast(0.3) saturate(2);
29 | }
30 |
31 | .icon__text {
32 | background-color: $blue;
33 | color: #ffffff;
34 | outline: 1px dotted #ffffff;
35 | outline-offset: -1px;
36 | }
37 | }
38 | }
39 |
40 | @mixin icon-explorer($classname: ".icon--explorer") {
41 | @include icon($classname);
42 |
43 | padding: initial;
44 | margin: 3px;
45 | width: 52px;
46 | height: 58px;
47 | text-align: center;
48 | display: flex;
49 | flex-direction: column;
50 | align-items: center;
51 |
52 | .icon__icon {
53 | width: 32px;
54 | height: 32px;
55 | margin: 0 3px;
56 | }
57 |
58 | .icon__text {
59 | margin: 2px;
60 | position: absolute;
61 | top: 34px;
62 | padding: 2px;
63 | max-height: 22px;
64 | max-width: 100%;
65 | overflow: hidden;
66 | display: inline-block;
67 | }
68 |
69 | &:focus,
70 | &:active,
71 | &:active:focus,
72 | &.active,
73 | &.clicked {
74 | .icon__text {
75 | padding: 2px 3px;
76 | max-height: initial;
77 | z-index: 1;
78 | }
79 | }
80 | }
81 |
82 | @mixin icon-list($classname: ".icon--list") {
83 | @include icon($classname);
84 | height: 18px;
85 | margin: 2px;
86 | text-align: left;
87 | display: flex;
88 | align-items: center;
89 |
90 | .icon__icon {
91 | display: inline-block;
92 | width: 16px;
93 | height: 16px;
94 | margin-right: 2px;
95 | }
96 |
97 | .icon__text {
98 | position: relative;
99 | padding: 2px;
100 | display: inline-block;
101 | overflow: hidden;
102 | white-space: nowrap;
103 | text-overflow: ellipsis;
104 | width: calc(100% - 20px);
105 | padding-bottom: 3px;
106 | }
107 |
108 | &:focus,
109 | &:active,
110 | &:active:focus,
111 | &.active,
112 | &.clicked {
113 | .icon__text {
114 | max-height: initial;
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/components/WindowAction/WindowAction.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import WindowAbstract from '../Window';
4 | import FakeSelect from '../FormFakeSelect';
5 | import IconListIcon from '../IconListIcon';
6 | import ButtonIconSmall from '../ButtonIconSmall';
7 | import InputText from '../FormInputText';
8 | import ButtonForm from '../ButtonForm';
9 | import img1 from './assets/1.png';
10 | import img2 from './assets/2.png';
11 | import img3 from './assets/3.png';
12 | import img4 from './assets/4.png';
13 | import img5 from './assets/5.png';
14 |
15 | import './_styles.scss';
16 | import SelectBox from '../FormSelectBox';
17 |
18 | const noop = () => {};
19 |
20 | const WindowAction = props => (
21 |
28 |
29 |
{props.action} in
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {Array.isArray(props.content) && (
38 |
45 | )}
46 |
47 |
48 |
49 | File name:
50 |
51 |
52 |
53 | Type:
54 |
55 |
56 |
57 | {props.action}
58 | Cancel
59 |
60 |
61 |
62 | );
63 |
64 | WindowAction.defaultProps = {
65 | onAction: noop,
66 | onCancel: noop,
67 | onHelp: noop,
68 | action: 'Save??',
69 | location: 'Desktop',
70 | content: null,
71 | name: '',
72 | };
73 |
74 | export default WindowAction;
75 |
--------------------------------------------------------------------------------
/src/components/FormSelectBoxSimple/SelectMultipleSimple.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import './_SelectMultipleSimple.scss';
5 |
6 | class SelectMultipleSimple extends Component {
7 | static defaultProps = {
8 | onChange: () => {},
9 | };
10 |
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | value: this.props.multiple ? [] : this.props.value || '',
15 | };
16 | }
17 |
18 | updateValue = value => {
19 | this.setState({ value });
20 | this.props.onChange(value);
21 | };
22 |
23 | handleChange = event => {
24 | if (this.props.multiple) {
25 | const selectedIndex = this.state.value.findIndex(
26 | val => val === event.target.value
27 | );
28 | const isSelected = selectedIndex !== -1;
29 | if (!isSelected && this.props.selectMultiple) {
30 | this.updateValue([...this.state.value, event.target.value]);
31 | return;
32 | }
33 | if (!isSelected) {
34 | this.updateValue([event.target.value]);
35 | return;
36 | }
37 | if (isSelected) {
38 | this.updateValue([
39 | ...this.state.value.slice(0, selectedIndex),
40 | ...this.state.value.slice(selectedIndex + 1),
41 | ]);
42 | return;
43 | }
44 | } else {
45 | this.updateValue(event.target.value);
46 | }
47 | };
48 |
49 | render() {
50 | const { props } = this;
51 | return (
52 |
53 |
72 |
73 | );
74 | }
75 | }
76 |
77 | SelectMultipleSimple.propTypes = {
78 | multiple: PropTypes.bool,
79 | onChange: PropTypes.func,
80 | value: PropTypes.any,
81 | isDisabled: PropTypes.bool,
82 | options: PropTypes.arrayOf(
83 | PropTypes.shape({
84 | name: PropTypes.string,
85 | value: PropTypes.any,
86 | isDisabled: PropTypes.bool,
87 | })
88 | ),
89 | };
90 |
91 | export default SelectMultipleSimple;
92 |
--------------------------------------------------------------------------------
/.storybook/stories/taskbar.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Notifications from '../../src/components/TaskBar/Notifications';
3 | import TaskBar from '../../src/components/TaskBar';
4 | import img from './directory_closed.png';
5 |
6 | const noop = () => {
7 | console.log('run');
8 | };
9 |
10 | const optionsSample = [
11 | {
12 | onClick: noop,
13 | title: 'Windows Update',
14 | icon: img,
15 | },
16 | [
17 | {
18 | onClick: noop,
19 | title: 'Programs',
20 | icon: img,
21 | options: [
22 | {
23 | onClick: noop,
24 | title: 'Accessories',
25 | icon: img,
26 | options: [
27 | {
28 | onClick: noop,
29 | title: 'Notepad?',
30 | icon: img,
31 | },
32 | {
33 | onClick: noop,
34 | title: 'fly it',
35 | icon: img,
36 | },
37 | {
38 | onClick: noop,
39 | title: 'Minesweeper',
40 | icon: img,
41 | },
42 | ],
43 | },
44 | {
45 | onClick: noop,
46 | title: 'open file?',
47 | icon: img,
48 | },
49 | ],
50 | },
51 | {
52 | onClick: noop,
53 | title: 'Control Panel',
54 | icon: img,
55 | },
56 | ],
57 | [
58 | {
59 | onClick: noop,
60 | title: 'Shut Down',
61 | icon: img,
62 | },
63 | ],
64 | ];
65 |
66 | export const TaskBarStory = {
67 | render: () => (
68 |
103 | ),
104 | name: 'TaskBar'
105 | }
106 | export const NotificationStory = {
107 | render: () => ,
108 | name: 'Notifications',
109 | }
110 |
111 | const meta = {
112 | component: TaskBarStory,
113 | }
114 | export default meta;
--------------------------------------------------------------------------------
/.storybook/stories/contextMenu.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ContextMenu from '../../src/components/StandardMenu';
3 | import withMenuWrapper from '../../src/components/StandardMenuHOC';
4 | //import ContextMenuWrapper from '../../src/components/ContextMenu/ContextMenuWrapper';
5 |
6 | const noop = () => {};
7 |
8 | const optionsSample = [
9 | {
10 | onClick: noop,
11 | title: 'New',
12 | },
13 | {
14 | onClick: noop,
15 | title: 'Disabled',
16 | isDisabled: true,
17 | },
18 | [
19 | {
20 | onClick: noop,
21 | title: 'Open',
22 | options: [
23 | {
24 | onClick: noop,
25 | title: 'open file?',
26 | },
27 | {
28 | onClick: noop,
29 | title: 'open drv file?',
30 | },
31 | {
32 | onClick: noop,
33 | title: 'spin it',
34 | options: [
35 | {
36 | onClick: noop,
37 | title: 'twist it?',
38 | },
39 | {
40 | onClick: noop,
41 | title: 'fly it',
42 | },
43 | ],
44 | },
45 | ],
46 | },
47 | ],
48 | {
49 | onClick: noop,
50 | title: 'quit',
51 | },
52 | ];
53 |
54 | const MenuWithLogic = withMenuWrapper();
55 |
56 | const notes =
57 | 'Hover styling currently controlled by javascript (no styling on CSS only version)';
58 |
59 | export const SingleField = {
60 | render: () => (
61 |
90 | ),
91 | }
92 |
93 | export const WithChildren = {
94 | render:
95 | () => (
96 |
97 |
Children display handled via CSS :hover
98 |
99 |
Children display handled via JavaScript wrapper component
100 |
101 |
102 | ),
103 | }
104 |
105 | const meta = {
106 | component: SingleField,
107 | }
108 | export default meta;
--------------------------------------------------------------------------------
/src/components/Button/Button.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 |
5 | import './_AbstractButton.scss';
6 |
7 | class AbstractButton extends Component {
8 | state = {
9 | mouseDown: false,
10 | };
11 |
12 | handleMouse = (func, mouseDown) => {
13 | this.setState({ mouseDown });
14 | if (func) {
15 | func();
16 | }
17 | };
18 |
19 | handleClick = e => {
20 | this.button.focus();
21 | if (this.props.onClick) {
22 | this.props.onClick(e);
23 | }
24 | };
25 |
26 | handleBlur = e => {
27 | if (this.props.onBlur) {
28 | this.props.onBlur(e);
29 | }
30 | };
31 |
32 | handleContextMenu = e => {
33 | e.preventDefault();
34 | e.stopPropagation();
35 | this.button.focus();
36 | if (this.props.onContextMenu) {
37 | this.props.onContextMenu(e);
38 | }
39 | };
40 |
41 | handleDoubleClick = e => {
42 | if (this.props.onDoubleClick) {
43 | this.props.onDoubleClick(e);
44 | }
45 | };
46 |
47 | render() {
48 | const { props } = this;
49 |
50 | return (
51 |
74 | );
75 | }
76 | }
77 |
78 | export const commonButtonPropTypes = {
79 | children: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
80 |
81 | title: PropTypes.string,
82 | className: PropTypes.string,
83 | isActive: PropTypes.bool,
84 | isDisabled: PropTypes.bool,
85 |
86 | onBlur: PropTypes.func,
87 | onClick: PropTypes.func,
88 | };
89 |
90 | AbstractButton.propTypes = {
91 | ...commonButtonPropTypes,
92 | onDoubleClick: PropTypes.func,
93 | onContextMenu: PropTypes.func,
94 | onMouseDown: PropTypes.func,
95 | onMouseUp: PropTypes.func,
96 | style: PropTypes.shape(), // Todo: Needs custom prop
97 | };
98 |
99 | export default AbstractButton;
100 |
--------------------------------------------------------------------------------
/src/components/ResizableIconsList/ResizableIconsList.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 | import ButtonIconLarge from '../ButtonIconLarge';
5 | import StandardMenu from '../StandardMenu';
6 | import './_options-list.scss';
7 | import { flattenWithDividers } from '../StandardMenu/StandardMenu';
8 |
9 | class OptionsListDropdown extends Component {
10 | openList = () => {
11 | this.dropdownButton.focus();
12 | };
13 | render() {
14 | return (
15 |
16 |
28 | );
29 | }
30 | }
31 |
32 | class OptionsList extends Component {
33 | static propTypes = {
34 | options: PropTypes.arrayOf(PropTypes.shape(ButtonIconLarge.propTypes)),
35 | className: PropTypes.string,
36 | };
37 | state = {
38 | entriesInView: 8,
39 | };
40 |
41 | ref = React.createRef();
42 |
43 | checkWidth = () => {
44 | if (!this.ref.current) {
45 | return;
46 | }
47 | const width = this.ref.current.offsetWidth || 200;
48 | const entriesInView = (width - 20) / 50;
49 | if (this.state.entriesInView !== entriesInView) {
50 | this.setState({ entriesInView });
51 | }
52 | };
53 |
54 | render() {
55 | const { props, state } = this;
56 | const options = flattenWithDividers(props.options);
57 | return (
58 |
85 | );
86 | }
87 | }
88 |
89 | export default OptionsList;
90 |
--------------------------------------------------------------------------------
/src/components/Button/Button.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import AbstractButton from './Button';
4 |
5 | describe('AbstractButton', () => {
6 | it('renders', () => {
7 | const wrapper = shallow();
8 | expect(wrapper.find('button').length).toBeTruthy();
9 | });
10 |
11 | it('renders children', () => {
12 | const wrapper = shallow(
13 |
14 |
15 |
16 | );
17 | expect(wrapper.find('#test').length).toBeTruthy();
18 | });
19 |
20 | it('accepts classes passed in', () => {
21 | const wrapper = shallow(
22 |
23 | ).render();
24 | expect(wrapper.hasClass('test')).toBe(true);
25 | expect(wrapper.hasClass('btn--active')).toBe(true);
26 | expect(wrapper.hasClass('btn--disabled')).toBe(true);
27 | });
28 |
29 | it('onclick focuses and fires prop', () => {
30 | const clickFunc = jest.fn();
31 | const testString = 'TestString';
32 | const wrapper = mount(
33 | {testString}
34 | );
35 | wrapper.simulate('click');
36 | expect(document.activeElement.innerHTML).toBe(testString);
37 | expect(clickFunc).toHaveBeenCalled();
38 | });
39 |
40 | it('onMouseDown onMouseUp', () => {
41 | const wrapper = shallow();
42 | wrapper.simulate('mousedown');
43 | expect(wrapper.state().mouseDown).toBe(true);
44 | wrapper.simulate('mouseup');
45 | expect(wrapper.state().mouseDown).toBe(false);
46 | });
47 |
48 | it('onDoubleClick if passed in as prop', () => {
49 | const dblClck = jest.fn();
50 | const wrapper = shallow();
51 | wrapper.simulate('doubleclick');
52 | wrapper.setProps({ onDoubleClick: dblClck });
53 | wrapper.simulate('doubleclick');
54 | expect(dblClck).toHaveBeenCalled();
55 | });
56 |
57 | it('onBlur if passed in as prop', () => {
58 | const onBlur = jest.fn();
59 | const wrapper = shallow();
60 | wrapper.simulate('blur');
61 | wrapper.setProps({ onBlur });
62 | wrapper.simulate('blur');
63 | expect(onBlur).toHaveBeenCalled();
64 | });
65 |
66 | it('onContextMenu on works if passed in as prop', () => {
67 | const mockEvent = {
68 | preventDefault: jest.fn(),
69 | stopPropagation: jest.fn(),
70 | };
71 | const onContextMenu = jest.fn();
72 | const wrapper = mount();
73 | wrapper.simulate('contextmenu');
74 | expect(onContextMenu).not.toHaveBeenCalled();
75 |
76 | wrapper.setProps({ onContextMenu });
77 | wrapper.simulate('contextmenu', mockEvent);
78 | expect(onContextMenu).toHaveBeenCalled();
79 | expect(mockEvent.preventDefault).toHaveBeenCalled();
80 | expect(mockEvent.stopPropagation).toHaveBeenCalled();
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/src/components/Icon/Icon.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 |
5 | class AbstractIcon extends Component {
6 | state = {
7 | doubleReady: false,
8 | };
9 |
10 | disableAction = () => {
11 | this.setState({ doubleReady: false });
12 | };
13 |
14 | checkDoubleClick = () => {
15 | this.handleClick();
16 |
17 | if (!this.props.onDoubleClick) {
18 | return;
19 | }
20 |
21 | if (this.state.doubleReady) {
22 | this.props.onDoubleClick();
23 | this.disableAction();
24 | } else {
25 | this.setState({ doubleReady: true });
26 | setTimeout(this.disableAction, 700);
27 | }
28 | };
29 |
30 | handleClick = e => {
31 | this.icon.focus();
32 | if (this.props.onClick) {
33 | this.props.onClick(e);
34 | }
35 | };
36 |
37 | handleContextMenu = e => {
38 | e.preventDefault();
39 | this.icon.focus();
40 | if (this.props.onContextMenu) {
41 | this.props.onContextMenu(e);
42 | }
43 | //return false;
44 | };
45 |
46 | render() {
47 | const { props } = this;
48 | const Comp = props.href ? 'a' : 'button';
49 |
50 | const iconProps = {
51 | onClick: this.checkDoubleClick,
52 | onContextMenu: this.props.onContextMenu && this.handleContextMenu,
53 | onTouchEnd: this.props.onDoubleClick || this.props.onClick,
54 | className: cx('icon', props.className),
55 | title: props.alt,
56 | value: props.value,
57 | ref: icon => {
58 | this.icon = icon;
59 | },
60 | href: props.href,
61 | };
62 |
63 | const contents = (
64 | <>
65 |
69 | {props.title}
70 | >
71 | );
72 |
73 | if (this.props.onClick || this.props.onDoubleClick) {
74 | return (
75 | {
77 | this.icon = icon;
78 | }}
79 | target={props.external && Comp === 'a' && '_blank'}
80 | rel={props.external && Comp === 'a' && 'noopener noreferrer'}
81 | {...iconProps}
82 | >
83 | {contents}
84 |
85 | );
86 | }
87 | return {contents}
;
88 | }
89 | }
90 |
91 | export const iconProps = {
92 | title: PropTypes.string,
93 | icon: PropTypes.string,
94 | children: PropTypes.node,
95 | className: PropTypes.string,
96 | alt: PropTypes.string,
97 | value: PropTypes.any,
98 | onClick: PropTypes.func,
99 | onDoubleClick: PropTypes.func,
100 | onContextMenu: PropTypes.func,
101 | href: PropTypes.string,
102 | external: PropTypes.bool,
103 | };
104 |
105 | AbstractIcon.propTypes = iconProps;
106 |
107 | export default AbstractIcon;
108 |
--------------------------------------------------------------------------------
/src/components/WindowExplorer/_WindowExplorer.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/var/colors";
2 | @import "../../_scss/w98/functions/box-shadows";
3 | @import "../../_scss/w98/mixins/box-shadows";
4 | @import "../../_scss/w98/var/var";
5 |
6 | @function nav-box-shadow() {
7 | @return inset 0px -1px 0px $darkgrey, inset -1px 0px 0px $darkgrey,
8 | inset 0px 0px 0px 1px #ffffff, -1px 0px 0px $darkgrey, 1px 0px 0px #ffffff,
9 | -1px 1px 0px 0px #ffffff, 1px 1px 0px 0px #ffffff;
10 | }
11 |
12 | .w98 .WindowExplorer {
13 | display: inline-flex;
14 | flex-direction: column;
15 | &__view {
16 | min-height: 20px;
17 | margin: 2px 0px;
18 | flex-grow: 1;
19 | background-color: #ffffff;
20 | @include shadow-input;
21 | }
22 | &__details {
23 | display: flex;
24 | &__section {
25 | box-shadow: inset -1px -1px 0px #ffffff, inset 1px 1px 0px $darkgrey;
26 | flex-grow: 1;
27 | margin-top: 2px;
28 | height: 16px;
29 |
30 | &:not(:last-child) {
31 | margin: 2px;
32 | }
33 | }
34 | }
35 |
36 | .window__menu {
37 | padding: 2px 2px 2px 12px;
38 | }
39 | > div + menu {
40 | margin-top: 2px;
41 | box-shadow: 0px 2px 0px -1px #ffffff, -1px 2px 0px -1px #ffffff,
42 | -1px 1px 0px $darkgrey, 0px 1px 0px $darkgrey, nav-box-shadow(),
43 | -1px -1px 0px $darkgrey, 0px -1px 0px $darkgrey, inset 0px 1px 0px #ffffff,
44 | 1px -1px 0px #ffffff;
45 | }
46 | > menu {
47 | position: relative;
48 | min-height: 22px;
49 | padding-left: 12px;
50 | margin: 0px 1px;
51 | box-shadow: nav-box-shadow();
52 | &:before {
53 | position: absolute;
54 | top: 3px;
55 | left: 5px;
56 | height: calc(100% - 6px);
57 | width: 3px;
58 | background-color: $grey;
59 | content: "";
60 | box-shadow: dualShadow(#ffffff, $darkgrey);
61 | }
62 | &.OptionsList {
63 | min-height: 40px;
64 | display: block;
65 | }
66 | }
67 | > footer {
68 | display: flex;
69 | > div {
70 | white-space: nowrap;
71 | text-overflow: ellipsis;
72 | overflow: hidden;
73 | min-width: 0px; // hack to prevent overflow
74 | flex-grow: 1;
75 | padding: 2px;
76 | height: 12px;
77 | box-shadow: dualShadow($black, #ffffff);
78 | &:not(:last-child) {
79 | margin-right: 2px;
80 | }
81 | &:last-child {
82 | padding-right: 12px;
83 | }
84 | }
85 | }
86 | &__address {
87 | display: flex;
88 | height: 26px;
89 | overflow-y: visible;
90 | user-select: none;
91 | &__title {
92 | align-self: center;
93 | margin-right: 4px;
94 | }
95 | .FakeSelect {
96 | flex-grow: 1;
97 | z-index: 5;
98 | margin-right: 4px;
99 | }
100 | }
101 | &__options {
102 | display: flex;
103 | padding: 2px 8px 2px 12px;
104 | }
105 | > div:last-child {
106 | margin-top: 2px;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | # Todo Lists
2 |
3 | # Critical
4 |
5 | - [x] get rid of node-sass
6 | - [x] fix storybook build issues
7 | - [x] fix css file build issues
8 | - [x] mass upgrade of key dependencies
9 | - [ ] either fix tests or delete them
10 | - [ ] remove unused things such as playroom and arguably stylelint
11 |
12 | # Initial UI Todo List
13 |
14 | Abstract
15 |
16 | - [x] Standard colors
17 | - [x] Borders
18 | - [x] Button
19 | - [x] Actions list
20 | - [x] Nested actions list
21 | - [x] Icons in action list
22 | - [x] Cursor
23 |
24 | Forms/etc
25 |
26 | - [x] Form button
27 | - [x] Section dividers
28 | - [x] Radio buttons
29 | - [x] Checkbox
30 | - [x] Text input
31 | - [x] Select (using react-select)
32 | - [x] Select multiple
33 | - [x] Disabled states for inputs
34 | - [ ] Tabs
35 | - [ ] Alert/dialog text handling
36 |
37 | W98 Toolbar
38 |
39 | - [x] Start Button
40 | - [x] Notifications + time
41 | - [x] Bar and dividers
42 | - [x] Quick Launch
43 | - [x] Active windows
44 | - [x] Start menu
45 |
46 | Explorer/Program
47 |
48 | - [x] Heading
49 | - [x] Standard view icons
50 | - [x] close/minimize/restore/help
51 | - [x] File/Edit/etc toolbar
52 | - [x] Options (needs sections, resize handling and see more icons)
53 | - [x] Explorer input (cosmetic only)
54 | - [ ] Explorer input (working)
55 | - [ ] Status footer (partially done, logic not figured out)
56 | - [ ] Explorer views (?)
57 |
58 | ## Interactive
59 |
60 | General
61 |
62 | - [ ] Right click actions
63 | - [ ] State sharing
64 | - [ ] Loading
65 | - [x] ~Shut down~
66 | - [x] ~Font substitution~
67 | - [x] ~Scaling display size~
68 | - [ ] Make CSS pseudoclass driven design choices optional
69 |
70 | W98 Toolbar
71 |
72 | - [x] Interactive start menu, validated inputs
73 | - [x] ~Only one active window~
74 | - [ ] Network notifications icon
75 |
76 | ## Performance
77 |
78 | - [ ] Tests
79 | - [ ] CI
80 | - [ ] Coverage
81 | - [x] Linting
82 | - [x] PropTypes
83 |
84 | # Necessary Refactorings
85 |
86 | - [ ] Decouple CSS build from React build (i.e. style everything within mixins, include where appropriate)
87 | - [x] ~Reimplement CSS Modules~
88 | - [x] Make dummy Select component for places where aesthetically needed
89 | - [x] Group options collections in explorer
90 | - [ ] Pixel near perfect (leave to last)
91 | - [ ] Switch to react-testing-library
92 | - [ ] Sematic HTML improvements
93 |
94 | ## To fix later
95 |
96 | - [ ] Use svg filter for icon highlighting (checkered blue pixel)
97 | - [ ] Refactor various list option groups
98 | - [ ] Blue selected areas grey when parent not active
99 | - [x] Radio and checkbox for Menus (via classes)
100 | - [x] Custom font
101 | - [ ] Start menu animation on iOS (absolute transitions issue, considering disabling on mobile)
102 | - [ ] Selected sections grey when not focus
103 | - [ ] Filter overhauls
104 | - [ ] Redundant CSS clearout
105 | - [x] StandardMenuWrapper as HOC?
106 | - [ ] Gradient improvements
107 | - [ ] Add empty taskbar to core css
108 | - [ ] Disabled buttons
109 | - [x] ~Single width font~
110 | - [ ] Firefox outlines issue
111 | - [ ] PERFORMANCE IMPROVMENTS
112 |
--------------------------------------------------------------------------------
/src/components/Window/Window.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import cx from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import WindowFrame from '../Frame';
5 | import Button from '../ButtonNav';
6 |
7 | import './_WindowAbstract.scss';
8 |
9 | class WindowAbstract extends Component {
10 | static defaultProps = {
11 | title: '...',
12 | resizable: true,
13 | };
14 |
15 | state = {
16 | maximized: this.props.maximizeOnOpen,
17 | };
18 |
19 | handleMaximize = e => {
20 | this.setState({ maximized: true });
21 |
22 | if (this.props.onMaximize) {
23 | this.props.onMaximize(e);
24 | }
25 | };
26 |
27 | handleRestore = e => {
28 | this.setState({ maximized: false });
29 | if (this.props.onRestore) {
30 | this.props.onRestore(e);
31 | }
32 | };
33 |
34 | render() {
35 | const { props } = this;
36 | return (
37 |
45 |
46 | {props.icon && (
47 |
51 | )}
52 |
{props.title}
53 | {props.onHelp && (
54 |
55 | )}
56 | {props.onMinimize && (
57 |
58 | )}
59 | {this.state.maximized && this.props.resizable && (
60 |
61 | )}
62 | {!this.state.maximized && this.props.resizable && (
63 |
67 | )}
68 | {(props.onClose ||
69 | props.onMaximize ||
70 | props.onRestore ||
71 | props.onMinimize ||
72 | props.onHelp) && (
73 |
78 | )}
79 |
80 | {props.children}
81 |
82 | );
83 | }
84 | }
85 |
86 | export const windowProps = {
87 | children: PropTypes.node,
88 | title: PropTypes.string,
89 | className: PropTypes.string,
90 | isActive: PropTypes.bool,
91 | icon: PropTypes.string,
92 |
93 | onClose: PropTypes.func,
94 | onMinimize: PropTypes.func,
95 | onMaximize: PropTypes.func,
96 | onRestore: PropTypes.func,
97 | maximizeOnOpen: PropTypes.bool,
98 |
99 | changingState: PropTypes.bool,
100 | };
101 |
102 | WindowAbstract.propTypes = windowProps;
103 |
104 | export default WindowAbstract;
105 |
--------------------------------------------------------------------------------
/src/components/FormSelectDISABLED/_Select.scss:
--------------------------------------------------------------------------------
1 | @import "../../_scss/w98/var/colors";
2 | @import "../../_scss/w98/mixins/box-shadows";
3 | @import "../../_scss/w98/functions/box-shadows";
4 | @import "../../_scss/w98/var/var";
5 | @import "../../_scss/w98/var/uris";
6 |
7 | @mixin select-hover {
8 | outline: 1px dotted #ffffff;
9 | outline-offset: -1px;
10 | background-color: $blue;
11 | color: #ffffff;
12 | }
13 |
14 | .w98 {
15 | // .SelectMultiple {
16 | // display: inline-block;
17 | // padding: 2px;
18 | // @include shadow-input;
19 | // >select[multiple] {
20 | // padding: 0px;
21 | // box-shadow: none;
22 | // }
23 | // ::-webkit-scrollbar {
24 | // display: none;
25 | // }
26 | // }
27 |
28 | $select-height: 16px;
29 |
30 | .Select-arrow-zone {
31 | position: absolute;
32 | box-shadow: dualShadow($grey, $black), dualShadow(#ffffff, $darkgrey, 2);
33 | height: $select-height;
34 | width: $select-height;
35 | left: calc(100% - 18px);
36 | top: 2px;
37 | background-color: $grey;
38 | background-repeat: no-repeat;
39 | background-position: center;
40 | background-image: url($arrow-down);
41 | }
42 | /* stylelint-disable */
43 | .Select {
44 | position: relative;
45 | .Select-control {
46 | width: 100%;
47 | .Select-multi-value-wrapper {
48 | .Select-input,
49 | .Select-placeholder,
50 | .Select-value {
51 | width: calc(100% - 4px);
52 | }
53 | .Select-input {
54 | display: none !important;
55 | }
56 | .Select-value,
57 | .Select-placeholder {
58 | height: $select-height;
59 | background-color: #ffffff;
60 | border: none;
61 | @include shadow-input;
62 | padding: 2px;
63 | .Select-value-label > div {
64 | margin: 1px;
65 | margin-right: $select-height + 1px;
66 | padding-left: 1px;
67 | outline: 1px dotted rgba(0, 0, 0, 0);
68 | }
69 | &:active,
70 | &:focus {
71 | .Select-value-label > div {
72 | @include select-hover();
73 | }
74 | }
75 | }
76 | .Select-placeholder {
77 | display: flex;
78 | align-items: center;
79 | padding: 2px 0px 2px 4px;
80 | }
81 | }
82 | .Select-clear-zone {
83 | display: none;
84 | }
85 | }
86 | .Select-menu-outer {
87 | border: 1px solid $black;
88 | background-color: #ffffff;
89 | .Select-menu {
90 | .Select-option {
91 | padding: 1px;
92 | &:hover {
93 | @include select-hover();
94 | }
95 | }
96 | }
97 | }
98 | &.is-disabled {
99 | pointer-events: none;
100 | .Select-control {
101 | .Select-multi-value-wrapper {
102 | .Select-value,
103 | .Select-placeholder {
104 | background-color: $grey;
105 | }
106 | }
107 | .Select-arrow-zone {
108 | &:after {
109 | background-image: url($arrow-down-disabled);
110 | }
111 | }
112 | }
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/_scss/w98/_inputs.scss:
--------------------------------------------------------------------------------
1 | @import "./var/colors";
2 | @import "./mixins/box-shadows";
3 | @import "./var/var";
4 | @import "./var/uris";
5 |
6 | @mixin input-text {
7 | position: relative;
8 | padding: 3px 3px 6px;
9 | font-size: $base-font-size;
10 | border: none;
11 | @include shadow-input;
12 |
13 | &:active,
14 | &:focus,
15 | &:active:focus,
16 | &.clicked {
17 | outline: none;
18 | }
19 | &:disabled,
20 | &.disabled {
21 | background-color: $grey;
22 | }
23 | }
24 |
25 | @mixin select-multiple-simple {
26 | position: relative;
27 | border: none;
28 | background-color: #ffffff;
29 | border-radius: 0px;
30 | outline: none;
31 | padding: 2px;
32 | @include shadow-input;
33 |
34 | &:active,
35 | &:focus,
36 | &:active:focus,
37 | &.active,
38 | &.clicked {
39 | outline: none;
40 | }
41 | option {
42 | &:active,
43 | &:focus,
44 | &:checked,
45 | &.checked {
46 | outline: 1px dotted #ffffff;
47 | outline-offset: -1px;
48 | background-color: $blue;
49 | color: #ffffff;
50 | }
51 | }
52 | }
53 |
54 | @mixin select-hover {
55 | outline: 1px dotted #ffffff;
56 | outline-offset: -1px;
57 | background-color: $blue;
58 | color: #ffffff;
59 | }
60 |
61 | @mixin toggle {
62 | opacity: 0;
63 | display: none;
64 | cursor: pointer;
65 | + label {
66 | position: relative;
67 | padding: 1px 0px;
68 | padding-left: 16px;
69 | > span,
70 | > div {
71 | display: inline-block;
72 | border: 1px solid rgba(0, 0, 0, 0);
73 | }
74 | &:before {
75 | content: "";
76 | position: absolute;
77 | left: 0px;
78 | top: 1px;
79 | width: 20px;
80 | height: 12px;
81 | background-repeat: no-repeat;
82 | }
83 | }
84 | &:checked {
85 | + label {
86 | border-bottom-left-radius: 2px;
87 | border-bottom-right-radius: 2px;
88 | }
89 | &:active,
90 | &:focus,
91 | &:active:focus,
92 | &.active,
93 | &.clicked {
94 | + label {
95 | > span,
96 | > div {
97 | border: 1px dotted $black;
98 | }
99 | }
100 | }
101 | }
102 | &:disabled,
103 | &.disabled {
104 | + label {
105 | color: $darkgrey;
106 | }
107 | }
108 | }
109 |
110 | @mixin checkbox {
111 | @include toggle();
112 | + label:before {
113 | width: 13px;
114 | height: 13px;
115 | background-color: #ffffff;
116 | @include shadow-input;
117 | }
118 | &:checked + label:before {
119 | background-image: url($menu-checked);
120 | background-position: center;
121 | background-size: 8px;
122 | }
123 | &:disabled,
124 | &.disabled {
125 | + label:before {
126 | background-color: $grey;
127 | }
128 | &:checked + label:before {
129 | background-image: url($menu-checked-disabled);
130 | }
131 | }
132 | }
133 |
134 | @mixin radio {
135 | @include toggle();
136 | + label:before {
137 | background-image: url($radio-off);
138 | }
139 | &:checked {
140 | + label {
141 | &:before {
142 | background-image: url($radio-on);
143 | }
144 | }
145 | }
146 | &:disabled,
147 | &.disabled {
148 | + label:before {
149 | background-image: url($radio-off-disabled);
150 | }
151 | &:checked {
152 | + label {
153 | &:before {
154 | background-image: url($radio-on-disabled);
155 | }
156 | }
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "packard-belle",
3 | "version": "0.4.0",
4 | "description": "Library of components to mimic old desktop",
5 | "main": "build/pb.js",
6 | "module": "build/pb.module.js",
7 | "repository": "https://github.com/padraigfl/packard-belle",
8 | "scripts": {
9 | "build:module": "rollup -c rollup.config.babel.js",
10 | "watch": "rollup -c rollup.config.babel.js -w",
11 | "storybook": "storybook dev -p 9001 -c .storybook",
12 | "build": "storybook build -c .storybook -o .out",
13 | "build:css": "sass src/_scss/w98_simple.scss:css/w98_simple.css",
14 | "test": "jest --env=jsdom",
15 | "test:watch": "jest --env=jsdom --no-cache --watch",
16 | "coverage": "jest --env=jsdom --coverage --coverageReporters=text-lcov ",
17 | "coveralls": "npm run coverage | coveralls",
18 | "lint": "eslint -c .eslintrc.js --quiet --ext .js './src/**/*.js'",
19 | "lint:css": "stylelint 'src/**/*.scss'",
20 | "precommit-build": "npm run build:module && git add build && git add css",
21 | "index": "node ./.scripts/autoIndex/",
22 | "playroom:start": "playroom start",
23 | "playroom:build": "playroom build"
24 | },
25 | "keywords": [
26 | "react",
27 | "component",
28 | "library",
29 | "windows",
30 | "desktop",
31 | "98"
32 | ],
33 | "author": "padraigf (https://github.com/padraigfl)",
34 | "license": "ISC",
35 | "dependencies": {
36 | "classnames": "^2.2.6",
37 | "clone": "^2.1.2",
38 | "prop-types": "^15.6.2"
39 | },
40 | "devDependencies": {
41 | "@babel/core": "^7.1.5",
42 | "@babel/plugin-proposal-class-properties": "^7.1.0",
43 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
44 | "@babel/preset-env": "^7.1.5",
45 | "@babel/preset-react": "^7.0.0",
46 | "@storybook/addon-storysource": "^7.0.21",
47 | "@storybook/builder-vite": "^7.0.21",
48 | "@storybook/builder-webpack5": "^7.0.21",
49 | "@storybook/react": "^7.0.21",
50 | "@storybook/react-webpack5": "^7.0.21",
51 | "@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
52 | "ajv": "^6.12.6",
53 | "babel-cli": "^6.26.0",
54 | "babel-core": "^7.0.0-bridge.0",
55 | "babel-eslint": "^8.2.6",
56 | "babel-jest": "^29.5.0",
57 | "babel-loader": "^8.0.4",
58 | "coveralls": "^3.0.2",
59 | "css-loader": "^5.2.7",
60 | "enzyme": "^3.3.0",
61 | "eslint": "^8.42.0",
62 | "eslint-plugin-jest": "^21.18.0",
63 | "eslint-plugin-react": "^7.33.2",
64 | "eslint-plugin-storybook": "^0.6.15",
65 | "jest": "^29.5.0",
66 | "playroom": "^0.31.0",
67 | "pre-commit": "^1.2.2",
68 | "raf": "^3.4.0",
69 | "react": "^17.0.0",
70 | "react-dom": "^17.0.0",
71 | "react-select": "^5.0.0",
72 | "react-test-renderer": "~17.0.0",
73 | "regenerator-runtime": "^0.12.1",
74 | "rollup": "^1.32.1",
75 | "rollup-plugin-auto-external": "^2.0.0",
76 | "rollup-plugin-babel": "^4.0.3",
77 | "rollup-plugin-commonjs": "^9.1.4",
78 | "rollup-plugin-img": "^1.1.0",
79 | "rollup-plugin-node-resolve": "^3.3.0",
80 | "rollup-plugin-postcss": "^4.0.2",
81 | "rollup-plugin-replace": "^2.0.0",
82 | "sass": "^1.63.4",
83 | "sass-loader": "^10.4.1",
84 | "storybook": "^7.0.21",
85 | "style-loader": "^2.0.0",
86 | "stylelint": "^15.7.0",
87 | "stylelint-scss": "^5.0.0"
88 | },
89 | "peerDependencies": {
90 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
91 | "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
92 | },
93 | "pre-commit": [
94 | "lint",
95 | "build:css",
96 | "precommit-build"
97 | ]
98 | }
99 |
--------------------------------------------------------------------------------
/src/_scss/w98/_window.scss:
--------------------------------------------------------------------------------
1 | @import "./var/colors";
2 | @import "./functions/box-shadows";
3 | @import "./var/var";
4 | @import "./var/uris";
5 | @import "./_menu";
6 |
7 | @mixin window-basic {
8 | position: relative;
9 | background-color: $grey;
10 | padding: 3px;
11 | box-shadow: dualShadow($grey, $black), dualShadow(#ffffff, $darkgrey, 2);
12 | display: inline-block;
13 | }
14 |
15 | @mixin window-drag {
16 | background-color: rgba(0, 0, 0, 0);
17 | box-shadow: dualShadow($darkgrey, $darkgrey, 3);
18 | > *,
19 | &:after {
20 | filter: opacity(0.1%);
21 | }
22 | }
23 |
24 | @mixin window-maximized {
25 | width: 100%;
26 | height: 100%;
27 | }
28 |
29 | @mixin window-heading {
30 | display: flex;
31 | background: linear-gradient(to right, $blue, #126fc2);
32 | font-weight: bold;
33 | color: #ffffff;
34 | margin-bottom: 1px;
35 | padding: 0px 1px 0px 3px;
36 | align-items: center;
37 | letter-spacing: 1px;
38 |
39 | button {
40 | padding: 0px;
41 | min-width: initial;
42 | width: 16px;
43 | height: 14px;
44 | margin-left: 1px;
45 | image-rendering: pixelated;
46 | display: flex;
47 | align-items: center;
48 | flex-shrink: 0;
49 | background-repeat: no-repeat;
50 | background-position: 1px 1px;
51 |
52 | &:focus,
53 | &.clicked {
54 | outline: none;
55 | border: none;
56 | }
57 |
58 | &:active:focus,
59 | &.clicked {
60 | padding: 2px 8px 1px 4px;
61 | background-position: 2px 2px;
62 | }
63 | }
64 | }
65 |
66 | @mixin window-icon {
67 | padding: 8px;
68 | display: flex;
69 | background-size: 14px;
70 | background-repeat: no-repeat;
71 | background-position: center;
72 | margin-right: 4px;
73 | }
74 |
75 | @mixin window-title {
76 | white-space: nowrap;
77 | overflow: hidden;
78 | text-overflow: ellipsis;
79 | flex-grow: 1;
80 | min-width: 0px;
81 | user-select: none;
82 | }
83 |
84 | @mixin window-menu($classname: "div") {
85 | display: flex;
86 | padding: 0px;
87 | font-size: 1rem;
88 | position: relative;
89 | overflow-y: visible;
90 | z-index: 20;
91 |
92 | > div {
93 | position: relative;
94 |
95 | > button {
96 | padding: 0px 4px;
97 | outline: none;
98 | border: none;
99 | user-select: none;
100 | color: $black;
101 | display: inline-block;
102 | background-color: rgba(0, 0, 0, 0);
103 | width: 100%;
104 | overflow: hidden;
105 | white-space: nowrap;
106 | text-overflow: ellipsis;
107 | text-align: left;
108 | padding: 3px 6px;
109 | text-transform: capitalize;
110 |
111 | + div,
112 | + #{$classname} {
113 | z-index: 20;
114 | visibility: hidden;
115 | position: absolute;
116 | max-height: 0px;
117 | top: 100%;
118 | left: 0px;
119 | @media (min-height: 720px) and (min-width: 960px) {
120 | transition: max-height linear 750ms;
121 | }
122 | }
123 |
124 | &:hover {
125 | box-shadow: dualShadow(#ffffff, $darkgrey);
126 | }
127 |
128 | &:active,
129 | &:focus,
130 | &:active:focus,
131 | &.active,
132 | &.clicked {
133 | box-shadow: dualShadow($darkgrey, #ffffff);
134 | padding: 4px 5px 2px 7px;
135 |
136 | + div,
137 | + #{$classname} {
138 | visibility: visible;
139 | max-height: 480px;
140 | }
141 | }
142 | }
143 | }
144 | }
145 |
146 | @mixin window-settings-section() {
147 | position: relative;
148 | border: 1px solid #ffffff;
149 | outline: 1px solid $darkgrey;
150 | padding: 5px;
151 | margin: 16px 8px 8px;
152 |
153 | &__title {
154 | position: absolute;
155 | top: -10px;
156 | padding: 2px 4px;
157 | background-color: $grey;
158 | }
159 | }
160 |
161 | @mixin window-alert() {
162 | display: inline-flex;
163 | flex-direction: column;
164 | max-width: 250px;
165 | &__message {
166 | display: flex;
167 | align-items: center;
168 | user-select: none;
169 | &.has-icon {
170 | background-size: 28px 28px;
171 | background-repeat: no-repeat;
172 | background-position: 6px 6px;
173 | padding: 6px 4px 8px 40px;
174 | }
175 | min-height: 28px;
176 | padding: 10px 2px 6px;
177 | }
178 | &__actions {
179 | width: 100%;
180 | display: flex;
181 | justify-content: center;
182 | .btn {
183 | margin: 0px 4px 8px;
184 | }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/src/_scss/w98/var/uris.scss:
--------------------------------------------------------------------------------
1 | $arrow-up: "data:image/gif;base64,R0lGODlhBwAEAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAHAAQAAAIHlGEJq8sOCwA7";
2 | $arrow-down: "data:image/gif;base64,R0lGODlhBwAEAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAHAAQAAAIIhA+CKWoNmSgAOw==";
3 | $arrow-left: "data:image/gif;base64,R0lGODlhBAAHAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAEAAcAAAIIlHEIy7ppBCgAOw==";
4 | $arrow-right: "data:image/gif;base64,R0lGODlhBAAHAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAEAAcAAAIIhA4maeyrlCgAOw==";
5 | $arrow-right-inverted: "data:image/gif;base64,R0lGODlhBAAHAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAEAAcAAAIIjB4maeyrlCgAOw==";
6 | $arrow-down-disabled: "data:image/gif;base64,R0lGODlhCAAFAJEAAAAAAP///5mZmf///yH5BAEAAAMALAAAAAAIAAUAAAIMlC8zKBF6nIJyqqcKADs=";
7 | $menu-checked: "data:image/gif;base64,R0lGODlhBwAHAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAHAAcAAAIMlA9nwMj9xGuLIlUAADs=";
8 | $menu-checked-disabled: "data:image/gif;base64,R0lGODlhBwAHAJEAAAAAAP///5mZmf///yH5BAEAAAMALAAAAAAHAAcAAAIMnC9nwsj9xmuLIlUAADs=";
9 | $menu-radio: "data:image/gif;base64,R0lGODlhBgAGAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAGAAYAAAIIFA6Gy816RAEAOw==";
10 | $radio-off: "data:image/gif;base64,R0lGODlhDAAMAKIAAAAAAP///8zMzJmZmf///wAAAAAAAAAAACH5BAEAAAQALAAAAAAMAAwAAAMqSErTs6uBCVqcIQesBtCaEDAfGAaeeaZqILKqyLQyI4hhTWT3nUEKECQBADs=";
11 | $radio-on: "data:image/gif;base64,R0lGODlhDAAMAKIAAAAAAP///8zMzJmZmf///wAAAAAAAAAAACH5BAEAAAQALAAAAAAMAAwAAAMtSErTs6uBCVqcIQesBtCaEDBfhmWiZ1JooG5skJZwOA6g3QliKC4oXg+iAEESADs=";
12 | $radio-off-disabled: "data:image/gif;base64,R0lGODlhDAAMAKIAAAAAAP///8zMzJmZmf///wAAAAAAAAAAACH5BAEAAAQALAAAAAAMAAwAAAMpSErTs6uBCVqccAY1AFTC1n1LOJKE6aEqmorsxggCRMtEENA3vug6SAIAOw==";
13 | $radio-on-disabled: "data:image/gif;base64,R0lGODlhDAAMAKIAAAAAAP///8zMzJmZmf///wAAAAAAAAAAACH5BAEAAAQALAAAAAAMAAwAAAMtSErTs6uBCVqccAY1AFTC1i0YGIwE5REhqppourLiZ3KCAOWbEgQ5Xg/y+0ESADs=";
14 |
15 | $more-options: "data:image/gif;base64,R0lGODlhCAAFAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAIAAUAAAIKBCSGebzqoJKtAAA7";
16 |
17 | $grey-checkered: "data:image/gif;base64,R0lGODlhAgACAJEAAAAAAP///8zMzP///yH5BAEAAAMALAAAAAACAAIAAAID1CYFADs=";
18 | $blue-checkered: "data:image/gif;base64,R0lGODlhAgACAJEAAAAAAP///wAAt////yH5BAEAAAMALAAAAAACAAIAAAID1CYFADs=";
19 | $blue-checkered-lighter: "data:image/gif;base64,R0lGODlhAgACAJEAAAAAAP///0ZGtP///yH5BAEAAAMALAAAAAACAAIAAAID1CYFADs=";
20 |
21 | $cursor: "data:image/gif;base64,R0lGODlhCwATAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAALABMAAAIrhI4JlhrcBAgvSlrbxPBs7mAU9IlMaV7mwo6jY2zk+Xphh8iWint1tDgUAAA7";
22 | $cursorX2: "data:image/gif;base64,R0lGODlhFgAmAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAAWACYAAAJzBISpu8b/jINUHgpNCBMrzV1eAm6dV4YjkppiBWyyisazfDIt/ur2zcv8gDQf8ZYT7IDJJfHkZL6izwtVyhpKLVwtssudpZJZ8ZCstE3GvbSrHGxIPue2hW72CfOkNvy9wrbiFjcoGFhnmIjIp4iGcZdQAAA7";
23 |
24 | $close: "data:image/gif;base64,R0lGODlhDQALAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAANAAsAAAIUlI+pKwDoVGxvucmwvblqo33MqBQAOw==";
25 | $restore: "data:image/gif;base64,R0lGODlhDQALAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAANAAsAAAIZlI9pwK3SnAKI1kjtwTlpyHjV830b9qRHAQA7";
26 | $minimize: "data:image/gif;base64,R0lGODlhDQALAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAANAAsAAAIOlI+py+0PozSg2mXvFAUAOw==";
27 | $maximize: "data:image/gif;base64,R0lGODlhDQALAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAANAAsAAAIXlI8Jy4wNXzJAznqwsjtPoYFfCDXfWQAAOw==";
28 | $whelp: "data:image/gif;base64,R0lGODlhDQALAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAANAAsAAAIUlI9pwKDrBHtTxmcxvJTrn30VqBQAOw==";
29 | $resize: "data:image/gif;base64,R0lGODlhDAAMAJEAAAAAAP///5mZmf///yH5BAEAAAMALAAAAAAMAAwAAAIbnI8TmSF83IMSKvFWw3dnHnFV+GVGhZZXmaoFADs=";
30 |
31 | $action-menu5: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOAgMAAABbQXQZAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAJUExURQAAq////wAAADZqBOUAAAArSURBVAjXY1gFBAwNDAxMDGDW1NDQMIapUbOmQVhTGBjEoCyEGJwFJkDaAGAFGP4kmmZ0AAAAAElFTkSuQmCC";
32 | $action-menu4: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOAgMAAABbQXQZAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURQAAq////wAAAIeIj/NEB9oAAAAoSURBVAjXY1gFBAwNDAxMDGDW1NDQMIY5mYXLGCaEOobh4AYic0HaAHnVGXeUN3O0AAAAAElFTkSuQmCC";
33 | $action-menu3: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAkUExURUdwTAAA/1Ooqf///wAAAIeIj6ipUf//AKoAVwAAqwD///8AABIbKHYAAAABdFJOUwBA5thmAAAAY0lEQVQY012PURKAIAgFHwimdf/7xkMza/92hxkA2GgNH+rFoMRJPaq5QU8VlIAuEiEd6R0M6WMenWFzoBucXupwOAObFdgKxiXYJ6JMZ1Da43MtXldeKusZyYk41QeiPN1+3GTUApb2Wc7EAAAAAElFTkSuQmCC";
34 | $action-menu2: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAPAgMAAACQHae8AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAJUExURUdwTAAAAP//ADvXwAwAAAABdFJOUwBA5thmAAAAOUlEQVQI12NgYGBkgAEwy4GFgTVAUIBBooXFgSE0NESQwaOj04EhSUlRAMRiAbEYQCwGJFZoaCgDADXDC1L2Qw0aAAAAAElFTkSuQmCC";
35 | $action-menu1: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAANCAMAAABBwMRzAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAJUExURUdwTP///4eIj4zp4tUAAAABdFJOUwBA5thmAAAAOElEQVQI112OQQ4AIAzCuv7/0WpMcBNO0CwM8IhIa9sXu8AaRmmW283KFOHVOYnh44Q+VmPw+88FZDYBB0ED8bYAAAAASUVORK5CYII=";
36 |
--------------------------------------------------------------------------------
/src/_scss/w98/_task-bar.scss:
--------------------------------------------------------------------------------
1 | @import "./var/colors";
2 | @import "./functions/box-shadows";
3 |
4 | $w98-text: "data:image/gif;base64,R0lGODlhDgBkALMAAAAAAP///wIAsZKSmZKTmpGSmZKTmcjOz8fNzsfOz8fOzv///wAAAAAAAAAAAAAAACH5BAEAAAsALAAAAAAOAGQAAAT/cMk5SUo06CO179wSGEowgEOQBcRUEuqkUaIRd/cCwyvFzyJNS3JQ2Tyt0QLBklgwEqZGQasShr4DQhuilDxgRCWAINgIAkIxFoB2DDJWbmGb2Oq0nJx2dqoCXUEuKl8GMCZRSjpgWAdYEydVkhMJQlVkQR8UTFRgQDhiHkc9QRyfRwRSV5+ZH1KbnodzjEGPCAYFcBIJj5mOk61IkgZSnpKVxpSeYCuegTjCw8Uev1bLPkfXccuY29SSGgmRky2p4b2Jnm5+3LrQ3CsY5Wuk9ZlwcJrv2uzLvWthJgH0cWVAKkMGBjhKws1YQ4cPP1wxUETclUPuBOXRY4mOvmDJafaFFMmKwoEDCspIgnGSC0pYDZvB88YvE7Bd3YABrBlRJs+HN73MiPgq4heQYJAhlYiOhqyUwLhVo7TTWcYlyEZOmAbEYM+I4hape4b0Cg0tDXlVyapVR9UY5h7KaogAg9R1c82ubEohAgA7";
5 |
6 | @mixin task-bar($classname: "task-bar") {
7 | position: fixed;
8 | background-color: $grey;
9 | bottom: 0px;
10 | left: 0px;
11 | width: 100%;
12 | max-width: 100%;
13 | z-index: 10;
14 | box-shadow: 0px -1px 0px #ffffff;
15 | padding: 2px 0px;
16 | display: flex;
17 |
18 | > div,
19 | > button {
20 | position: relative;
21 | height: 22px;
22 | margin: 0px 2px;
23 | }
24 |
25 | > div:not(:last-child) {
26 | padding: 0px 6px;
27 | &:first-child {
28 | padding: 0px 3px 0px 0px;
29 | }
30 | &:after {
31 | position: absolute;
32 | top: 1px;
33 | right: 0px;
34 | height: calc(100% - 2px);
35 | width: 1px;
36 | background-color: $darkgrey;
37 | content: "";
38 | box-shadow: 1px 0px 0px #ffffff;
39 | }
40 | &:before {
41 | position: absolute;
42 | top: 3px;
43 | right: -6px;
44 | height: calc(100% - 6px);
45 | width: 3px;
46 | background-color: $grey;
47 | content: "";
48 | box-shadow: dualShadow(#ffffff, $darkgrey);
49 | }
50 | }
51 |
52 | &__programs {
53 | display: flex;
54 | flex-grow: 1;
55 | flex-shrink: 1;
56 | flex-wrap: nowrap;
57 | margin-right: 4px;
58 | min-width: 42px;
59 |
60 | &:before {
61 | display: none;
62 | }
63 | }
64 | &__start {
65 | position: relative;
66 | > button {
67 | + div {
68 | @media (min-height: 720px) and (min-width: 960px) {
69 | transition: max-height linear 200ms;
70 | }
71 | position: fixed;
72 | bottom: 25px;
73 | left: 2px;
74 | visibility: hidden;
75 | max-height: 0px;
76 | padding-left: 22px;
77 | > .divider:empty,
78 | > div:empty {
79 | margin-left: 24px;
80 | width: calc(100% - 26px);
81 | }
82 | &:after {
83 | content: "";
84 | display: block;
85 | position: absolute;
86 | left: 3px;
87 | top: 3px;
88 | height: calc(100% - 6px);
89 | width: 20px;
90 | background: $blue;
91 | background: linear-gradient($blue, #126fc2);
92 | background: url($w98-text) no-repeat bottom 3px center,
93 | linear-gradient($blue, #126fc2);
94 | }
95 | > div {
96 | display: flex;
97 | align-items: center;
98 | margin-left: 20px;
99 | > button {
100 | height: 32px;
101 | padding-left: 32px;
102 | background-size: 22px;
103 | background-position: 4px center;
104 | }
105 | }
106 | }
107 |
108 | &.active,
109 | &.clicked {
110 | background-position: 3px 2px;
111 | outline: 1px dotted $black;
112 | outline-offset: -4px;
113 |
114 | > div {
115 | visibility: visible;
116 | max-height: 100vh;
117 | padding: 3px;
118 | div {
119 | display: flex;
120 | }
121 | }
122 | }
123 | }
124 |
125 | &.active {
126 | > div {
127 | visibility: visible;
128 | max-height: 100vh;
129 | padding: 3px;
130 | div {
131 | display: flex;
132 | }
133 | }
134 | }
135 | }
136 |
137 | &__notifications {
138 | background-color: $grey;
139 | display: flex;
140 | flex: none;
141 | margin-left: auto;
142 | align-items: center;
143 | height: 22px;
144 | padding: 0px 8px 0px 4px;
145 | box-shadow: dualShadow($darkgrey, #ffffff);
146 |
147 | &__time {
148 | margin-left: 4px;
149 | }
150 |
151 | &__notifier {
152 | height: 16px;
153 | width: 16px;
154 | background-color: $grey;
155 | background-size: contain;
156 | background-position: center;
157 | background-repeat: no-repeat;
158 | border: none;
159 | &:active,
160 | &:focus,
161 | &:active:focus,
162 | &.active,
163 | &.clicked {
164 | outline: none;
165 | border: none;
166 | }
167 | }
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/.storybook/stories/windows.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import WindowFrame from '../../src/components/Frame';
3 | import AbstractWindow from '../../src/components/Window';
4 | import ExplorerWindow from '../../src/components/WindowExplorer';
5 | import AlertWindow from '../../src/components/WindowAlert';
6 | import WindowProgram from '../../src/components/WindowProgram';
7 | import DetailsSection from '../../src/components/DetailsSection';
8 |
9 | import img from './directory_closed.png';
10 | import WindowAction from '../../src/components/WindowAction';
11 |
12 | const noop = () => {};
13 |
14 | const optionsSample = [
15 | {
16 | onClick: noop,
17 | title: 'New',
18 | },
19 | {
20 | onClick: noop,
21 | title: 'Disabled',
22 | disabled: true,
23 | },
24 | [
25 | {
26 | onClick: noop,
27 | title: 'Open',
28 | options: [
29 | {
30 | onClick: noop,
31 | title: 'open file?',
32 | },
33 | {
34 | onClick: noop,
35 | title: 'open drv file?',
36 | },
37 | {
38 | onClick: noop,
39 | title: 'spin it',
40 | options: [
41 | {
42 | onClick: noop,
43 | title: 'twist it?',
44 | },
45 | {
46 | onClick: noop,
47 | title: 'fly it',
48 | },
49 | ],
50 | },
51 | ],
52 | },
53 | ],
54 | {
55 | onClick: noop,
56 | title: 'quit',
57 | },
58 | ];
59 |
60 | export const GenericWindow = () => Window
61 | export const StaticWindow = () =>
68 | Windows
69 |
70 | export const WindowWithDetailsSection = () => (
71 |
78 |
79 | Here's a load of stuff
80 |
81 |
82 | Here's a load of stuff
83 |
84 |
85 | )
86 | export const WindowForProgram = () => (
87 |
105 | Windows
106 |
107 | )
108 | export const ExplorerWindowStory = {
109 | render: () => (
110 | Test, Oh
]}
161 | resizable
162 | />
163 | ),
164 | notes: 'WIP'
165 | }
166 | export const AlertWindowStory = {
167 | render: () => (
168 |
175 | This is an error message.
176 |
177 | )
178 | }
179 | export const WindowActionStory = () => (
180 |
188 | );
189 |
190 | const meta = {
191 | component: GenericWindow,
192 | }
193 | export default meta;
--------------------------------------------------------------------------------
/src/components/StandardMenuHOC/StandardMenuHOC.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'classnames';
4 | import clone from 'clone';
5 | import StandardMenu, { standardMenuProps } from '../StandardMenu';
6 |
7 | const withContextLogic = ContextButton => {
8 | return class StandardMenuSimple extends Component {
9 | static defaultProps = {
10 | value: [],
11 | };
12 | static propTypes = {
13 | ...standardMenuProps,
14 | onClick: PropTypes.func,
15 | onBlur: PropTypes.func,
16 | onContextMenu: PropTypes.func,
17 | };
18 |
19 | static getDerivedStateFromProps(nextProps, prevState) {
20 | if (nextProps.isActive !== prevState.isActive) {
21 | return {
22 | options: nextProps.options,
23 | isActive: nextProps.isActive,
24 | };
25 | } else return null;
26 | }
27 |
28 | state = {
29 | options: this.props.options,
30 | isActive: this.props.isActive,
31 | isOpen: false,
32 | };
33 |
34 | updateActive(activeFields, newOptions, idx = 0) {
35 | if (activeFields.length <= idx) {
36 | return newOptions;
37 | }
38 | const changeIdx = newOptions.findIndex((option, optIdx) => {
39 | if (Array.isArray(option)) {
40 | const subIdx = option.findIndex(
41 | opt => opt.title === activeFields[idx]
42 | );
43 | if (subIdx !== -1) {
44 | newOptions[optIdx][subIdx].isActive = true;
45 | if (newOptions[optIdx][subIdx].options) {
46 | newOptions[optIdx][subIdx].options = this.updateActive(
47 | activeFields,
48 | newOptions[optIdx][subIdx].options,
49 | idx + 1
50 | );
51 | }
52 | return;
53 | }
54 | }
55 | return option.title === activeFields[idx];
56 | });
57 | if (changeIdx !== -1) {
58 | newOptions[changeIdx].isActive = true;
59 | newOptions[changeIdx].options = this.updateActive(
60 | activeFields,
61 | newOptions[changeIdx].options,
62 | idx + 1
63 | );
64 | }
65 | return newOptions;
66 | }
67 |
68 | mouseEnterItem = e => {
69 | if (e.target.value) {
70 | const newOptions = this.updateActive(
71 | e.target.value.split(','),
72 | clone(this.props.options),
73 | 0
74 | );
75 | this.setState({ options: newOptions });
76 | }
77 | };
78 | addBlurListener = () => {
79 | document.body.addEventListener('click', this.handleBlur);
80 | document.body.addEventListener('mousedown', this.handleBlur);
81 | };
82 | removeBlurListener = () => {
83 | document.body.removeEventListener('click', this.handleBlur);
84 | document.body.removeEventListener('mousedown', this.handleBlur);
85 | };
86 |
87 | buttonClick = () => {
88 | if (this.state.isOpen) {
89 | this.removeBlurListener();
90 | this.setState({ isOpen: false, options: this.props.options });
91 | } else {
92 | this.addBlurListener();
93 | this.setState({ isOpen: true, options: this.props.options });
94 | }
95 | };
96 | handleEvent = newState => onEvent => e => {
97 | if (onEvent) {
98 | onEvent(e);
99 | }
100 | if (newState) {
101 | this.setState(newState);
102 | }
103 | };
104 | handleContextMenu = e =>
105 | this.handleEvent({ isOpen: true })(this.props.onContextMenu)(e);
106 | handleBlur = e => {
107 | if (this.el && !this.el.contains(e.target)) {
108 | this.handleEvent({ isOpen: false, options: this.props.options })(
109 | this.props.onBlur
110 | )(e);
111 | }
112 | };
113 | handleSelectionClose = this.handleEvent({
114 | isOpen: false,
115 | options: this.props.options,
116 | });
117 |
118 | render() {
119 | const renderedMenu = (
120 | this.mouseEnterItem(e)}
124 | closeOnClick={this.handleSelectionClose}
125 | />
126 | );
127 |
128 | if (ContextButton) {
129 | const { className, ...props } = this.props;
130 | return (
131 | {
133 | this.el = el;
134 | }}
135 | className={cx('StandardMenuWrapper', className, {
136 | active: this.state.isOpen,
137 | })}
138 | >
139 | this.handleContextMenu(e))
145 | }
146 | >
147 | {props.children}
148 |
149 | {renderedMenu}
150 |
151 | );
152 | }
153 | return renderedMenu;
154 | }
155 | };
156 | };
157 |
158 | export default withContextLogic;
159 |
--------------------------------------------------------------------------------