refactor: remove advanced-checklist editor (moved to community plugins) (#1740)
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@types-styled-components-npm-5.1.26-aabda06611-84f53b3101.zip
vendored
Normal file
BIN
.yarn/cache/@types-styled-components-npm-5.1.26-aabda06611-84f53b3101.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5
packages/components/dist/Components.js
vendored
5
packages/components/dist/Components.js
vendored
@@ -13,11 +13,6 @@ exports.Components = void 0;
|
||||
var BaseEditorStaticFiles = ['index.html', 'dist', 'package.json'];
|
||||
var BaseThemeStaticFiles = ['dist', 'package.json'];
|
||||
var Editors = [
|
||||
{
|
||||
identifier: 'org.standardnotes.advanced-checklist',
|
||||
path: 'Editors/org.standardnotes.advanced-checklist',
|
||||
static_files: __spreadArray(__spreadArray([], BaseEditorStaticFiles, true), ['build'], false),
|
||||
},
|
||||
{
|
||||
identifier: 'org.standardnotes.code-editor',
|
||||
path: 'Editors/org.standardnotes.code-editor',
|
||||
|
||||
5
packages/components/dist/zips/checksums.json
vendored
5
packages/components/dist/zips/checksums.json
vendored
@@ -89,11 +89,6 @@
|
||||
"base64": "0ae54799c28a336447a63ddabfe43d0c6e18f8c2cd29cfdb1c0174ab96fde248",
|
||||
"binary": "54a01414a21e46c67fa59c69189a3d34a6548de7539699f2cf0248f9c5059d37"
|
||||
},
|
||||
"org.standardnotes.advanced-checklist": {
|
||||
"version": "0.2.5",
|
||||
"base64": "077a56ef75958105bc9dc16376bcea2e89aee57b512b43f9e1b33e1d32ea8358",
|
||||
"binary": "e903a5c592bd03ca28d205d09dcbc7e4329c7cd57fb74b4e0e5fa724c2321a61"
|
||||
},
|
||||
"org.standardnotes.file-safe": {
|
||||
"version": "2.0.16",
|
||||
"base64": "2bb641344613fc86c340fb29636a68678a13e453d04fb36228af6045e06664db",
|
||||
|
||||
@@ -2,11 +2,6 @@ const BaseEditorStaticFiles = ['index.html', 'dist', 'package.json']
|
||||
const BaseThemeStaticFiles = ['dist', 'package.json']
|
||||
|
||||
const Editors = [
|
||||
{
|
||||
identifier: 'org.standardnotes.advanced-checklist',
|
||||
path: 'Editors/org.standardnotes.advanced-checklist',
|
||||
static_files: [...BaseEditorStaticFiles, 'build'],
|
||||
},
|
||||
{
|
||||
identifier: 'org.standardnotes.code-editor',
|
||||
path: 'Editors/org.standardnotes.code-editor',
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
#### Copied from Create React App ####
|
||||
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
/public/ext.json
|
||||
@@ -1,115 +0,0 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.2.5](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.2.4...@standardnotes/advanced-checklist@0.2.5) (2022-09-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
|
||||
## [0.2.4](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.2.3...@standardnotes/advanced-checklist@0.2.4) (2022-08-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
|
||||
## [0.2.3](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.2.2...@standardnotes/advanced-checklist@0.2.3) (2022-07-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* upgrade jest with types to latest version ([09e08ca](https://github.com/standardnotes/app/commit/09e08ca899ba8694cf43292e918c4c204c0d2cb9))
|
||||
* upgrade ts-jest in packages ([71e792d](https://github.com/standardnotes/app/commit/71e792da354ff90335b92758e196075a0f88d060))
|
||||
|
||||
## [0.2.2](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.2.1...@standardnotes/advanced-checklist@0.2.2) (2022-07-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **advanced checklist:** improve editor styles ([#1221](https://github.com/standardnotes/app/issues/1221)) ([f7ba658](https://github.com/standardnotes/app/commit/f7ba6588a7d062e3ec82e6413042ce5d8cd075f7))
|
||||
|
||||
## [0.2.1](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.2.0...@standardnotes/advanced-checklist@0.2.1) (2022-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
|
||||
# [0.2.0](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.1.6...@standardnotes/advanced-checklist@0.2.0) (2022-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* add utils package ([aef4ceb](https://github.com/standardnotes/app/commit/aef4ceb7f85948f1f08b8b09a4db5d187daa371b))
|
||||
|
||||
## [0.1.6](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.1.5...@standardnotes/advanced-checklist@0.1.6) (2022-07-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **advanced checklist:** remove test for reorder icon ([#1209](https://github.com/standardnotes/app/issues/1209)) ([296aa9a](https://github.com/standardnotes/app/commit/296aa9aab2072d4aad68485c31c000ad2c3bf013))
|
||||
|
||||
## [0.1.5](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.1.4...@standardnotes/advanced-checklist@0.1.5) (2022-07-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **advanced checklist:** UI changes ([#1208](https://github.com/standardnotes/app/issues/1208)) ([1ce4cb3](https://github.com/standardnotes/app/commit/1ce4cb3c5c8f4e590fd67fdbd684b36ac6383bd9))
|
||||
|
||||
## [0.1.4](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.1.3...@standardnotes/advanced-checklist@0.1.4) (2022-07-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **advanced checklist:** animations and error handling ([#1200](https://github.com/standardnotes/app/issues/1200)) ([a0205a5](https://github.com/standardnotes/app/commit/a0205a5c7dd72184cb575195ced3132091072239))
|
||||
|
||||
## [0.1.3](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.1.2...@standardnotes/advanced-checklist@0.1.3) (2022-06-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* debounce saving task description/draft ([#1187](https://github.com/standardnotes/app/issues/1187)) ([47a0551](https://github.com/standardnotes/app/commit/47a0551967ca420a957e2123d56bd7f0c8a95c53))
|
||||
|
||||
## [0.1.2](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.1.1...@standardnotes/advanced-checklist@0.1.2) (2022-06-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **advanced checklist:** style and layout improvements ([#1182](https://github.com/standardnotes/app/issues/1182)) ([6c19adb](https://github.com/standardnotes/app/commit/6c19adba1902ef054f501d57f6e284fbf44ca28b))
|
||||
|
||||
## [0.1.1](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.1.0...@standardnotes/advanced-checklist@0.1.1) (2022-06-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
|
||||
# [0.1.0](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.0.5...@standardnotes/advanced-checklist@0.1.0) (2022-06-28)
|
||||
|
||||
### Features
|
||||
|
||||
* **advanced checklist:** collapsible group sections ([#1167](https://github.com/standardnotes/app/issues/1167)) ([59e5324](https://github.com/standardnotes/app/commit/59e5324a29029d024811bf2bb63e08ae42d3b62b))
|
||||
* deprecated editors ([#1166](https://github.com/standardnotes/app/issues/1166)) ([60ca415](https://github.com/standardnotes/app/commit/60ca4150446f9a14bb6a31416686c6d07a7d0cd9))
|
||||
* **web:** tailwind css ([#1147](https://github.com/standardnotes/app/issues/1147)) ([b80038f](https://github.com/standardnotes/app/commit/b80038f607d7411912fa99366abf559a44874ef3))
|
||||
|
||||
## [0.0.5](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.0.5-alpha.0...@standardnotes/advanced-checklist@0.0.5) (2022-06-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
|
||||
## [0.0.5-alpha.0](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.0.4...@standardnotes/advanced-checklist@0.0.5-alpha.0) (2022-06-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* components scripts ([#1136](https://github.com/standardnotes/app/issues/1136)) ([e80b4d0](https://github.com/standardnotes/app/commit/e80b4d0ffad495c758b593c30e1c4c754dda9b7e))
|
||||
|
||||
## 0.0.4 (2022-06-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
|
||||
## 0.0.3 (2022-06-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
|
||||
## [0.0.2](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.0.2-alpha.3...@standardnotes/advanced-checklist@0.0.2) (2022-06-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
|
||||
## [0.0.2-alpha.3](https://github.com/standardnotes/app/compare/@standardnotes/advanced-checklist@0.0.2-alpha.2...@standardnotes/advanced-checklist@0.0.2-alpha.3) (2022-06-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
|
||||
## 0.0.2-alpha.2 (2022-06-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
|
||||
## 0.0.2-alpha.1 (2022-06-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
|
||||
## 0.0.2-alpha.0 (2022-06-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/advanced-checklist
|
||||
@@ -1,80 +0,0 @@
|
||||
# advanced-checklist
|
||||
|
||||
A great way to manage short-term and long-term to-do's. You can mark tasks as completed, change their order, and edit the text naturally in place.
|
||||
|
||||
## Development
|
||||
|
||||
**Prerequisites:** Install [Node.js](https://nodejs.org/en/), [Yarn](https://classic.yarnpkg.com/en/docs/install/), and [Git](https://github.com/git-guides/install-git) on your computer.
|
||||
|
||||
The general instructions setting up an environment to develop Standard Notes extensions can be found [here](https://docs.standardnotes.org/extensions/local-setup). You can also follow these instructions:
|
||||
|
||||
1. Fork the [repository](https://github.com/standardnotes/advanced-checklist) on GitHub.
|
||||
1. [Clone](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository) your fork of the repository.
|
||||
1. Run `cd advanced-checklist` to enter the `advanced-checklist` directory.
|
||||
1. Run `yarn install` to install the dependencies on your machine as they are described in `yarn.lock`.
|
||||
|
||||
### Testing in the browser
|
||||
|
||||
1. To run the app in development mode, run `yarn start` and visit http://localhost:8001. Press `ctrl/cmd + C` to exit development mode.
|
||||
|
||||
### Testing in the Standard Notes app
|
||||
|
||||
1. Create an `ext.json` in the `public` directory. You have three options:
|
||||
1. Use `sample.ext.json`.
|
||||
1. Create `ext.json` as a copy of `sample.ext.json`.
|
||||
1. Follow the instructions [here](https://docs.standardnotes.org/extensions/local-setup) with `url: "http://localhost:3000/index.html"`.
|
||||
1. Install http-server using `sudo npm install -g http-server` then run `yarn server` to serve the `./build` directory at http://localhost:3000.
|
||||
1. To build the app, run `yarn build`.
|
||||
1. Install the editor into the [web](https://app.standardnotes.org) or [desktop](https://standardnotes.org/download) app with `http://localhost:3000/sample.ext.json` or with your custom `ext.json`. Press `ctrl/cmd + C` to shut down the server.
|
||||
|
||||
### Deployment
|
||||
|
||||
1. To make the source code prettier, run `yarn pretty`.
|
||||
1. To the deploy the build into the `gh-pages` branch of your repository on GitHub, run `yarn deploy-stable`.
|
||||
1. To deploy the build into to the `dev` branch for testing, run `yarn deploy-dev`.
|
||||
1. To deploy the built into the `build` branch for distributing, run `yarn deploy-build` for distributing builds.
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
### Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
#### `yarn start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:8001](http://localhost:8001) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.\
|
||||
You will also see any lint errors in the console.
|
||||
|
||||
#### `yarn test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
#### `yarn build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
#### `yarn eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
|
||||
### Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
@@ -1,2 +0,0 @@
|
||||
const override = require('../cra-app.override')
|
||||
module.exports = override
|
||||
@@ -1,127 +0,0 @@
|
||||
{
|
||||
"name": "@standardnotes/advanced-checklist",
|
||||
"version": "0.2.5",
|
||||
"description": "A task editor with grouping functionality.",
|
||||
"author": "Standard Notes.",
|
||||
"keywords": [
|
||||
"Standard Notes",
|
||||
"Standard Notes Extensions"
|
||||
],
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"sn": {
|
||||
"main": "build/index.html"
|
||||
},
|
||||
"homepage": ".",
|
||||
"scripts": {
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"start": "react-app-rewired start",
|
||||
"test:coverage": "npm run test -- --coverage --watchAll --no-silent",
|
||||
"eject": "react-scripts eject",
|
||||
"components:compile": "react-app-rewired build",
|
||||
"test": "react-app-rewired test --watchAll=false --silent",
|
||||
"format": "prettier --write 'src/**/*.{html,css,scss,js,jsx,ts,tsx,json}' README.md"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"lint-staged": {
|
||||
"README.md": [
|
||||
"prettier --write"
|
||||
],
|
||||
"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.{js,jsx,ts,tsx}",
|
||||
"!<rootDir>/node_modules/"
|
||||
],
|
||||
"coveragePathIgnorePatterns": [
|
||||
"<rootDir>/src/mockData.ts",
|
||||
"<rootDir>/src/app/hooks.ts",
|
||||
"<rootDir>/src/app/store.ts",
|
||||
"<rootDir>/src/app/listenerMiddleware.ts"
|
||||
],
|
||||
"coverageReporters": [
|
||||
"text",
|
||||
"html"
|
||||
],
|
||||
"coverageThreshold": {
|
||||
"global": {
|
||||
"branches": 90,
|
||||
"functions": 100,
|
||||
"lines": 100,
|
||||
"statements": 100
|
||||
}
|
||||
},
|
||||
"transform": {
|
||||
"^.+\\.(ts|tsx)$": "ts-jest"
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
|
||||
"^.+\\.module\\.(css|sass|scss)$"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/utils": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@reach/alert-dialog": "0.16.2",
|
||||
"@reach/menu-button": "0.16.2",
|
||||
"@reach/visually-hidden": "0.16.0",
|
||||
"@react-hook/resize-observer": "^1.2.5",
|
||||
"@reduxjs/toolkit": "1.8.0",
|
||||
"@standardnotes/editor-kit": "2.2.5",
|
||||
"@standardnotes/stylekit": "5.23.0",
|
||||
"@testing-library/dom": "8.11.3",
|
||||
"@testing-library/jest-dom": "5.16.2",
|
||||
"@testing-library/react": "12.1.4",
|
||||
"@testing-library/user-event": "13.5.0",
|
||||
"@types/jest": "^28.1.5",
|
||||
"@types/lodash": "4.14.179",
|
||||
"@types/node": "17.0.21",
|
||||
"@types/react": "17.0.40",
|
||||
"@types/react-beautiful-dnd": "13.1.2",
|
||||
"@types/react-dom": "17.0.13",
|
||||
"@types/react-redux": "7.1.23",
|
||||
"@types/react-transition-group": "4.4.4",
|
||||
"@types/redux-mock-store": "1.0.3",
|
||||
"@types/styled-components": "5.1.24",
|
||||
"@types/uuid": "8.3.4",
|
||||
"gh-pages": "3.2.3",
|
||||
"lint-staged": "12.3.5",
|
||||
"node-sass": "*",
|
||||
"prettier": "*",
|
||||
"react": "17.0.2",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"react-beautiful-dnd": "13.1.0",
|
||||
"react-dom": "17.0.2",
|
||||
"react-redux": "7.2.8",
|
||||
"react-scripts": "5.0.0",
|
||||
"react-transition-group": "4.4.2",
|
||||
"redux": "4.1.2",
|
||||
"redux-mock-store": "1.5.4",
|
||||
"source-map-explorer": "2.5.2",
|
||||
"styled-components": "5.3.5",
|
||||
"ts-jest": "^28.0.5",
|
||||
"typescript": "4.6.2",
|
||||
"uuid": "8.3.2"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,44 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="A great way to manage short-term and long-term to-do's. You can mark tasks as completed, change their order, and edit the text naturally in place."
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Advanced Checklist</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div class="sn-component" id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.4 KiB |
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"short_name": "Advanced Checklist",
|
||||
"name": "Advanced Checklist",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"identifier": "org.standardnotes.advanced-checklist-dev",
|
||||
"name": "Advanced Checklist - Dev",
|
||||
"content_type": "SN|Component",
|
||||
"area": "editor-editor",
|
||||
"version": "0.1.0",
|
||||
"description": "A task editor with grouping functionality.",
|
||||
"url": "http://localhost:3000/index.html",
|
||||
"download_url": "",
|
||||
"latest_url": "",
|
||||
"thumbnail_url": ""
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import useResizeObserver from '@react-hook/resize-observer'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
|
||||
import type { AppDispatch, RootState } from './store'
|
||||
|
||||
export const useAppDispatch = () => useDispatch<AppDispatch>()
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
||||
|
||||
export const useDidMount = (effect: React.EffectCallback, deps?: React.DependencyList) => {
|
||||
const [didMount, setDidMount] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (didMount) {
|
||||
effect()
|
||||
} else {
|
||||
setDidMount(true)
|
||||
}
|
||||
}, [deps, didMount, effect])
|
||||
}
|
||||
|
||||
export const useResize = (ref: React.RefObject<HTMLElement>, effect: (target: HTMLElement) => void) => {
|
||||
const [size, setSize] = useState<DOMRect>()
|
||||
|
||||
function isDeepEqual(prevSize?: DOMRect, nextSize?: DOMRect) {
|
||||
return JSON.stringify(prevSize) === JSON.stringify(nextSize)
|
||||
}
|
||||
|
||||
useResizeObserver(ref, ({ contentRect, target }) => {
|
||||
if (!isDeepEqual(size, contentRect)) {
|
||||
setSize(contentRect)
|
||||
effect(target as HTMLElement)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const useDebouncedCallback = (callback: () => void, waitMs: number = 500) => {
|
||||
const timeout = useRef<any>()
|
||||
|
||||
clearTimeout(timeout.current)
|
||||
|
||||
timeout.current = setTimeout(() => {
|
||||
callback()
|
||||
}, waitMs)
|
||||
}
|
||||
|
||||
export const usePrevious = (value: any) => {
|
||||
const ref = useRef<typeof value>()
|
||||
|
||||
useEffect(() => {
|
||||
ref.current = value
|
||||
}, [value])
|
||||
|
||||
return ref.current
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { createListenerMiddleware, isAnyOf } from '@reduxjs/toolkit'
|
||||
import {
|
||||
deleteAllCompleted,
|
||||
openAllCompleted,
|
||||
taskAdded,
|
||||
taskDeleted,
|
||||
taskModified,
|
||||
tasksGroupAdded,
|
||||
tasksGroupCollapsed,
|
||||
tasksGroupDeleted,
|
||||
tasksGroupLastActive,
|
||||
tasksGroupMerged,
|
||||
tasksReordered,
|
||||
taskToggled,
|
||||
} from '../features/tasks/tasks-slice'
|
||||
|
||||
const listenerMiddleware = createListenerMiddleware()
|
||||
|
||||
/**
|
||||
* A list of actions that we want to listen to.
|
||||
* The groupName is obtained from the payload, and we use it to
|
||||
* dispatch the tasksGroupLastActive action.
|
||||
*/
|
||||
const actionsWithGroup = isAnyOf(
|
||||
taskAdded,
|
||||
taskModified,
|
||||
taskToggled,
|
||||
taskDeleted,
|
||||
openAllCompleted,
|
||||
deleteAllCompleted,
|
||||
tasksReordered,
|
||||
tasksGroupAdded,
|
||||
tasksGroupDeleted,
|
||||
tasksGroupMerged,
|
||||
tasksGroupCollapsed,
|
||||
)
|
||||
|
||||
listenerMiddleware.startListening({
|
||||
matcher: actionsWithGroup,
|
||||
effect: ({ payload }, listenerApi) => {
|
||||
const { groupName } = payload
|
||||
listenerApi.dispatch(tasksGroupLastActive({ groupName }))
|
||||
},
|
||||
})
|
||||
|
||||
export default listenerMiddleware.middleware
|
||||
@@ -1,16 +0,0 @@
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
|
||||
import settingsReducer from '../features/settings/settings-slice'
|
||||
import tasksReducer from '../features/tasks/tasks-slice'
|
||||
import listenerMiddleware from './listenerMiddleware'
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
tasks: tasksReducer,
|
||||
settings: settingsReducer,
|
||||
},
|
||||
middleware: (getDefaultMiddleware) => getDefaultMiddleware().prepend(listenerMiddleware),
|
||||
})
|
||||
|
||||
export type AppDispatch = typeof store.dispatch
|
||||
export type RootState = ReturnType<typeof store.getState>
|
||||
@@ -1,38 +0,0 @@
|
||||
import { ChangeEvent, forwardRef, MouseEvent } from 'react'
|
||||
|
||||
type CheckBoxInputProps = {
|
||||
checked?: boolean
|
||||
disabled?: boolean
|
||||
testId?: string
|
||||
onChange?: (event: ChangeEvent<HTMLInputElement>) => void
|
||||
onClick?: (event: MouseEvent<SVGElement>) => void
|
||||
}
|
||||
|
||||
export const CheckBoxInput = forwardRef<HTMLInputElement, CheckBoxInputProps>(
|
||||
({ checked, disabled, testId, onChange, onClick }, ref) => {
|
||||
return (
|
||||
<label className="checkbox-container">
|
||||
<input
|
||||
className="checkbox-state"
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
data-testid={testId}
|
||||
disabled={disabled}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
/>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="3 2 22 20"
|
||||
className="checkbox-button"
|
||||
onClick={onClick}
|
||||
>
|
||||
<use xlinkHref="#checkbox-square" className="checkbox-square"></use>
|
||||
<use xlinkHref="#checkbox-mark" className="checkbox-mark"></use>
|
||||
<use xlinkHref="#checkbox-circle" className="checkbox-circle"></use>
|
||||
</svg>
|
||||
</label>
|
||||
)
|
||||
},
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
const ProgressBarBackground = styled.circle`
|
||||
fill: none;
|
||||
stroke: var(--sn-stylekit-neutral-color);
|
||||
`
|
||||
|
||||
const ProgressBarStroke = styled.circle`
|
||||
fill: none;
|
||||
stroke: var(--sn-stylekit-info-color);
|
||||
transition: all 0.5s;
|
||||
`
|
||||
|
||||
type CircularProgressBarProps = {
|
||||
size: number
|
||||
percentage: number
|
||||
}
|
||||
|
||||
export const CircularProgressBar: React.FC<CircularProgressBarProps> = ({ size, percentage }) => {
|
||||
const [progress, setProgress] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
setProgress(percentage)
|
||||
}, [percentage])
|
||||
|
||||
const viewBox = `0 0 ${size} ${size}`
|
||||
const strokeWidth = size * (0.1 / 100) * 100 + 1
|
||||
const radius = (size - strokeWidth) / 2
|
||||
const circumference = radius * Math.PI * 2
|
||||
const dash = (progress * circumference) / 100
|
||||
|
||||
return (
|
||||
<svg height={size} viewBox={viewBox} width={size} data-testid="circular-progress-bar">
|
||||
<ProgressBarBackground cx={size / 2} cy={size / 2} r={radius} strokeWidth={strokeWidth} />
|
||||
<ProgressBarStroke
|
||||
cx={size / 2}
|
||||
cy={size / 2}
|
||||
r={radius}
|
||||
strokeWidth={strokeWidth}
|
||||
transform={`rotate(-90 ${size / 2} ${size / 2})`}
|
||||
strokeDasharray={`${dash} ${circumference - dash}`}
|
||||
strokeLinecap="round"
|
||||
style={{ transition: 'all 0.5s' }}
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
import '@reach/dialog/styles.css'
|
||||
|
||||
import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog'
|
||||
import React, { useRef } from 'react'
|
||||
|
||||
import { sanitizeHtmlString } from '@standardnotes/utils'
|
||||
|
||||
type ConfirmDialogProps = {
|
||||
testId?: string
|
||||
title?: string
|
||||
confirmButtonText?: string
|
||||
confirmButtonStyle?: 'danger' | 'info'
|
||||
confirmButtonCb: () => void
|
||||
cancelButtonText?: string
|
||||
cancelButtonCb: () => void
|
||||
}
|
||||
|
||||
export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
|
||||
testId,
|
||||
title = '',
|
||||
confirmButtonText = 'Confirm',
|
||||
confirmButtonStyle = 'info',
|
||||
confirmButtonCb,
|
||||
cancelButtonText = 'Cancel',
|
||||
cancelButtonCb,
|
||||
children,
|
||||
}) => {
|
||||
const cancelRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
return (
|
||||
<AlertDialog data-testid={testId} onDismiss={cancelButtonCb} leastDestructiveRef={cancelRef}>
|
||||
<div className="sk-modal-content">
|
||||
<div className="sn-component">
|
||||
<div className="sk-panel">
|
||||
<div className="sk-panel-content">
|
||||
<div className="sk-panel-section">
|
||||
<AlertDialogLabel
|
||||
className="sk-h3 sk-panel-section-title"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: sanitizeHtmlString(title),
|
||||
}}
|
||||
/>
|
||||
|
||||
<AlertDialogDescription className="sk-panel-row">
|
||||
<p className="color-foreground">{children}</p>
|
||||
</AlertDialogDescription>
|
||||
|
||||
<div className="flex my-1">
|
||||
<button
|
||||
data-testid="cancel-dialog-button"
|
||||
className="sn-button small neutral"
|
||||
onClick={cancelButtonCb}
|
||||
ref={cancelRef}
|
||||
>
|
||||
{cancelButtonText}
|
||||
</button>
|
||||
<button
|
||||
data-testid="confirm-dialog-button"
|
||||
className={`sn-button small ml-2 ${confirmButtonStyle}`}
|
||||
onClick={confirmButtonCb}
|
||||
>
|
||||
{confirmButtonText}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const GenericInlineText = styled.span`
|
||||
color: var(--sn-stylekit-paragraph-text-color);
|
||||
font-size: var(--sn-stylekit-font-size-p);
|
||||
margin: 0 10px;
|
||||
`
|
||||
@@ -1,26 +0,0 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
type Header1Props = {
|
||||
crossed: boolean
|
||||
}
|
||||
|
||||
const Header1 = styled.h1<Header1Props>`
|
||||
color: var(--sn-stylekit-editor-foreground-color);
|
||||
display: inline;
|
||||
font-size: 1.125rem !important;
|
||||
margin-right: 10px !important;
|
||||
text-decoration: ${({ crossed }) => (crossed ? 'line-through' : 'none')};
|
||||
`
|
||||
|
||||
type MainTitleProps = {
|
||||
highlight?: boolean
|
||||
crossed?: boolean
|
||||
}
|
||||
|
||||
export const MainTitle: React.FC<MainTitleProps> = ({ children, highlight = false, crossed = false, ...props }) => {
|
||||
return (
|
||||
<Header1 className={`sk-h1 ${highlight ? 'info' : ''}`} crossed={crossed} {...props}>
|
||||
{children}
|
||||
</Header1>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
type RoundButtonProps = {
|
||||
testId?: string
|
||||
onClick: () => void
|
||||
size?: 'normal' | 'small'
|
||||
}
|
||||
|
||||
export const RoundButton: React.FC<RoundButtonProps> = ({ testId, onClick, children, size = 'normal' }) => {
|
||||
return (
|
||||
<button data-testid={testId} className={`sn-icon-button ${size}`} onClick={onClick}>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const SubTitle = styled.h3`
|
||||
color: var(--sn-stylekit-foreground-color);
|
||||
cursor: pointer;
|
||||
font-size: var(--sn-stylekit-font-size-h3);
|
||||
font-weight: 500;
|
||||
margin: 10px 0px;
|
||||
opacity: 0.55;
|
||||
|
||||
&::first-letter {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
`
|
||||
@@ -1,46 +0,0 @@
|
||||
import { ChangeEvent, forwardRef, KeyboardEvent } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
const StyledTextArea = styled.textarea`
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: inherit;
|
||||
font-size: 0.98rem;
|
||||
font-weight: 400;
|
||||
margin-left: 2px;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
type TextAreaInputProps = {
|
||||
value: string
|
||||
className?: string
|
||||
dir?: 'ltr' | 'rtl' | 'auto'
|
||||
disabled?: boolean
|
||||
spellCheck?: boolean
|
||||
testId?: string
|
||||
onChange?: (event: ChangeEvent<HTMLTextAreaElement>) => void
|
||||
onKeyPress?: (event: KeyboardEvent<HTMLTextAreaElement>) => void
|
||||
onKeyUp?: (event: KeyboardEvent<HTMLTextAreaElement>) => void
|
||||
}
|
||||
|
||||
export const TextAreaInput = forwardRef<HTMLTextAreaElement, TextAreaInputProps>(
|
||||
({ value, className, dir = 'auto', disabled, spellCheck, testId, onChange, onKeyPress, onKeyUp }, ref) => {
|
||||
return (
|
||||
<StyledTextArea
|
||||
className={className}
|
||||
data-testid={testId}
|
||||
dir={dir}
|
||||
disabled={disabled}
|
||||
onChange={onChange}
|
||||
onKeyPress={onKeyPress}
|
||||
onKeyUp={onKeyUp}
|
||||
ref={ref}
|
||||
spellCheck={spellCheck}
|
||||
value={value}
|
||||
/>
|
||||
)
|
||||
},
|
||||
)
|
||||
@@ -1,21 +0,0 @@
|
||||
$transition-duration: 750ms;
|
||||
|
||||
@keyframes strike {
|
||||
from {
|
||||
text-decoration: line-through 1px solid transparent;
|
||||
}
|
||||
to {
|
||||
text-decoration: line-through 1px solid var(--sn-stylekit-info-color);
|
||||
}
|
||||
}
|
||||
|
||||
.cross-out {
|
||||
animation-duration: $transition-duration;
|
||||
animation-fill-mode: forwards;
|
||||
animation-name: strike;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.no-text-decoration {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import './TextInput.scss'
|
||||
|
||||
import { ChangeEvent, FocusEvent, forwardRef, KeyboardEvent } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
type StyledInputProps = {
|
||||
textSize: 'normal' | 'big'
|
||||
}
|
||||
|
||||
const StyledInput = styled.input<StyledInputProps>`
|
||||
background-color: unset;
|
||||
border: none;
|
||||
color: var(--sn-stylekit-foreground-color);
|
||||
font-size: ${({ textSize }) => (textSize === 'big' ? '1.125rem' : 'var(--sn-stylekit-font-size-h3)')};
|
||||
font-weight: ${({ textSize }) => (textSize === 'big' ? '500' : '400')};
|
||||
height: auto;
|
||||
margin: 6px 0 6px 0;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
|
||||
/* Remove default shadow for iOS mobile */
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
|
||||
::placeholder {
|
||||
color: var(--sn-stylekit-input-placeholder-color);
|
||||
}
|
||||
`
|
||||
|
||||
type TextInputProps = {
|
||||
value: string
|
||||
autoFocus?: boolean
|
||||
dir?: 'ltr' | 'rtl' | 'auto'
|
||||
disabled?: boolean
|
||||
enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
|
||||
placeholder?: string
|
||||
spellCheck?: boolean
|
||||
testId?: string
|
||||
textSize?: 'normal' | 'big'
|
||||
onBlur?: (event: FocusEvent<HTMLInputElement>) => void
|
||||
onChange?: (event: ChangeEvent<HTMLInputElement>) => void
|
||||
onKeyPress?: (event: KeyboardEvent<HTMLInputElement>) => void
|
||||
}
|
||||
|
||||
export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
(
|
||||
{
|
||||
value,
|
||||
autoFocus,
|
||||
dir = 'auto',
|
||||
disabled,
|
||||
enterKeyHint,
|
||||
placeholder,
|
||||
spellCheck,
|
||||
testId,
|
||||
textSize = 'normal',
|
||||
onBlur,
|
||||
onChange,
|
||||
onKeyPress,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
return (
|
||||
<StyledInput
|
||||
type="text"
|
||||
autoFocus={autoFocus}
|
||||
data-testid={testId}
|
||||
dir={dir}
|
||||
disabled={disabled}
|
||||
enterKeyHint={enterKeyHint}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
onKeyPress={onKeyPress}
|
||||
placeholder={placeholder}
|
||||
ref={ref}
|
||||
spellCheck={spellCheck}
|
||||
textSize={textSize}
|
||||
value={value}
|
||||
/>
|
||||
)
|
||||
},
|
||||
)
|
||||
@@ -1,15 +0,0 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const WideButton = styled.button`
|
||||
align-items: center;
|
||||
background-color: var(--sn-stylekit-background-color);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--sn-stylekit-border-color);
|
||||
box-sizing: border-box;
|
||||
color: var(--sn-stylekit-paragraph-text-color);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 36px;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
`
|
||||
@@ -1,14 +0,0 @@
|
||||
export const AddIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
className="sn-icon small block"
|
||||
fill="none"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
width="14"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M14 7C14 7.55228 13.5523 8 13 8H8V13C8 13.5523 7.55228 14 7 14C6.44772 14 6 13.5523 6 13V8H1C0.447715 8 0 7.55228 0 7C0 6.44772 0.447715 6 1 6H6V1C6 0.447715 6.44772 0 7 0C7.55228 0 8 0.447715 8 1V6H13C13.5523 6 14 6.44772 14 7Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export const ChevronDownIcon = () => {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className="sn-icon block">
|
||||
<path d="M6.17622 7.15015L10.0012 10.9751L13.8262 7.15015L15.0012 8.33348L10.0012 13.3335L5.00122 8.33348L6.17622 7.15015Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export const ChevronUpIcon = () => {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className="sn-icon block">
|
||||
<path d="M13.826 13.3335L10.001 9.5085L6.17597 13.3335L5.00097 12.1502L10.001 7.15017L15.001 12.1502L13.826 13.3335Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
export const DottedCircleIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
className="sn-icon no-fill stroke-neutral-color block"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect x="0.5" y="0.5" width="19" height="19" rx="9.5" strokeDasharray="2 2" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export const MergeIcon = () => {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className="sn-icon block">
|
||||
<path d="M8 17L12 13H15.2C15.6 14.2 16.7 15 18 15C19.7 15 21 13.7 21 12C21 10.3 19.7 9 18 9C16.7 9 15.6 9.8 15.2 11H12L8 7V3H3V8H6L10.2 12L6 16H3V21H8V17Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
const Ellipsis = ({ ...props }) => (
|
||||
<span aria-hidden {...props}>
|
||||
…
|
||||
</span>
|
||||
)
|
||||
|
||||
/**
|
||||
* SVG icons don't work well with the MenuButton components.
|
||||
* So we create a text-based replacement for it.
|
||||
*/
|
||||
export const MoreIcon = styled(Ellipsis)`
|
||||
font-weight: 800;
|
||||
height: 18px;
|
||||
line-height: 10px;
|
||||
`
|
||||
@@ -1,7 +0,0 @@
|
||||
export const RenameIcon = () => {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className="sn-icon block">
|
||||
<path d="M11.7167 7.5L12.5 8.28333L4.93333 15.8333H4.16667V15.0667L11.7167 7.5ZM14.7167 2.5C14.5083 2.5 14.2917 2.58333 14.1333 2.74167L12.6083 4.26667L15.7333 7.39167L17.2583 5.86667C17.5833 5.54167 17.5833 5 17.2583 4.69167L15.3083 2.74167C15.1417 2.575 14.9333 2.5 14.7167 2.5ZM11.7167 5.15833L2.5 14.375V17.5H5.625L14.8417 8.28333L11.7167 5.15833Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
type ReorderIconProps = {
|
||||
highlight?: boolean
|
||||
}
|
||||
|
||||
export const ReorderIcon: React.FC<ReorderIconProps> = ({ highlight = false }) => {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`sn-icon block ${highlight ? 'info' : 'neutral'}`}
|
||||
data-testid="reorder-icon"
|
||||
>
|
||||
<path d="M17 5V6.66667H3V5H17ZM3 15H17V13.3333H3V15ZM3 10.8333H17V9.16667H3V10.8333Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export const TrashIcon = () => {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className="sn-icon block">
|
||||
<path d="M7.49992 2.5V3.33333H3.33325V5H4.16659V15.8333C4.16659 16.2754 4.34218 16.6993 4.65474 17.0118C4.9673 17.3244 5.39122 17.5 5.83325 17.5H14.1666C14.6086 17.5 15.0325 17.3244 15.3451 17.0118C15.6577 16.6993 15.8333 16.2754 15.8333 15.8333V5H16.6666V3.33333H12.4999V2.5H7.49992ZM5.83325 5H14.1666V15.8333H5.83325V5ZM7.49992 6.66667V14.1667H9.16658V6.66667H7.49992ZM10.8333 6.66667V14.1667H12.4999V6.66667H10.8333Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
export { AddIcon } from './AddIcon'
|
||||
export { ChevronDownIcon } from './ChevronDownIcon'
|
||||
export { ChevronUpIcon } from './ChevronUpIcon'
|
||||
export { DottedCircleIcon } from './DottedCircleIcon'
|
||||
export { MergeIcon } from './MergeIcon'
|
||||
export { MoreIcon } from './MoreIcon'
|
||||
export { RenameIcon } from './RenameIcon'
|
||||
export { ReorderIcon } from './ReorderIcon'
|
||||
export { TrashIcon } from './TrashIcon'
|
||||
@@ -1,10 +0,0 @@
|
||||
export * from './CheckBoxInput'
|
||||
export * from './CircularProgressBar'
|
||||
export * from './ConfirmDialog'
|
||||
export * from './GenericInlineText'
|
||||
export * from './MainTitle'
|
||||
export * from './RoundButton'
|
||||
export * from './SubTitle'
|
||||
export * from './TextAreaInput'
|
||||
export * from './TextInput'
|
||||
export * from './WideButton'
|
||||
@@ -1,114 +0,0 @@
|
||||
$transition-duration: 750ms;
|
||||
|
||||
.checkbox-container {
|
||||
display: block;
|
||||
padding-left: 22px;
|
||||
position: relative;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-state {
|
||||
height: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.checkbox-button {
|
||||
bottom: 0;
|
||||
cursor: pointer;
|
||||
fill: var(--sn-stylekit-contrast-background-color);
|
||||
height: 18px;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 1;
|
||||
top: 0;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
.align-baseline {
|
||||
.checkbox-button {
|
||||
top: -10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-square,
|
||||
.checkbox-mark {
|
||||
cursor: pointer;
|
||||
transition: stroke-dashoffset $transition-duration cubic-bezier(0.9, 0, 0.5, 1);
|
||||
}
|
||||
|
||||
.checkbox-circle {
|
||||
animation-delay: 1s;
|
||||
animation: none $transition-duration linear;
|
||||
stroke-dasharray: 1 6;
|
||||
stroke-width: 0;
|
||||
stroke: var(--sn-stylekit-neutral-color);
|
||||
transform-origin: 13.5px 12.5px;
|
||||
transform: scale(0.4) rotate(0deg);
|
||||
}
|
||||
|
||||
.checkbox-square {
|
||||
stroke-dasharray: 56.1053, 56.1053;
|
||||
stroke-dashoffset: 0;
|
||||
stroke: var(--sn-stylekit-shadow-color);
|
||||
transition-delay: $transition-duration * 0.2;
|
||||
fill: var(--sn-stylekit-contrast-background-color);
|
||||
}
|
||||
|
||||
.checkbox-mark {
|
||||
stroke-dasharray: 9.8995, 9.8995;
|
||||
stroke-dashoffset: 9.8995;
|
||||
stroke: var(--sn-stylekit-neutral-color);
|
||||
transition-duration: $transition-duration * 0.4;
|
||||
}
|
||||
|
||||
.checkbox-circle {
|
||||
animation-delay: $transition-duration * 0.7;
|
||||
animation-duration: $transition-duration * 0.7;
|
||||
}
|
||||
|
||||
.checkbox-state:checked {
|
||||
~ .checkbox-button .checkbox-square {
|
||||
stroke-dashoffset: 56.1053;
|
||||
stroke: var(--sn-stylekit-info-color);
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
~ .checkbox-button .checkbox-mark {
|
||||
stroke-dashoffset: 0;
|
||||
stroke: var(--sn-stylekit-info-color);
|
||||
transition-delay: $transition-duration * 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes explode {
|
||||
30% {
|
||||
stroke: var(--sn-stylekit-info-color);
|
||||
stroke-opacity: 1;
|
||||
stroke-width: 3;
|
||||
transform: scale(0.8) rotate(40deg);
|
||||
}
|
||||
100% {
|
||||
stroke: var(--sn-stylekit-neutral-color);
|
||||
stroke-opacity: 0;
|
||||
stroke-width: 0;
|
||||
transform: scale(1.1) rotate(60deg);
|
||||
}
|
||||
}
|
||||
|
||||
.explode {
|
||||
.checkbox-circle {
|
||||
animation: explode ease $transition-duration;
|
||||
animation-delay: $transition-duration;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import './CheckBoxElementsDefs.scss'
|
||||
|
||||
export const CheckBoxElementsDefs = () => {
|
||||
return (
|
||||
<svg viewBox="0 0 0 0" style={{ position: 'absolute', zIndex: -1, opacity: 0 }}>
|
||||
<defs>
|
||||
<path
|
||||
id="checkbox-square"
|
||||
d="M21 12.7v5c0 1.3-1 2.3-2.3 2.3H8.3C7 20 6 19 6 17.7V7.3C6 6 7 5 8.3 5h10.4C20 5 21 6 21 7.3v5.4"
|
||||
></path>
|
||||
<path id="checkbox-mark" d="M10 13l2 2 5-5"></path>
|
||||
<circle id="checkbox-circle" cx="13.5" cy="12.5" r="10"></circle>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from './CheckBoxElementsDefs'
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
export { ArrowVector } from './ArrowVector'
|
||||
@@ -1,296 +0,0 @@
|
||||
import { DEFAULT_SECTIONS, GroupModel, TaskModel } from '../features/tasks/tasks-slice'
|
||||
import {
|
||||
arrayDefault,
|
||||
arrayMoveImmutable,
|
||||
arrayMoveMutable,
|
||||
getPercentage,
|
||||
getPlainPreview,
|
||||
getTaskArrayFromGroupedTasks,
|
||||
groupTasksByCompletedStatus,
|
||||
parseMarkdownTasks,
|
||||
truncateText,
|
||||
} from './utils'
|
||||
|
||||
describe('arrayMoveMutable', () => {
|
||||
it('should not mutate array if there are no elements', () => {
|
||||
const theArray: any[] = []
|
||||
arrayMoveMutable(theArray, 0, 1)
|
||||
|
||||
expect(theArray).toHaveLength(0)
|
||||
})
|
||||
|
||||
test('passing a negative number to fromIndex should use 0 instead', () => {
|
||||
const theArray = ['test', 'another test']
|
||||
arrayMoveMutable(theArray, -1, 1)
|
||||
|
||||
expect(theArray).toHaveLength(2)
|
||||
expect(theArray[0]).toBe('test')
|
||||
expect(theArray[1]).toBe('another test')
|
||||
})
|
||||
|
||||
test('passing a negative number to toIndex should use 0 instead', () => {
|
||||
const theArray = ['test', 'another test']
|
||||
arrayMoveMutable(theArray, 1, -1)
|
||||
|
||||
expect(theArray).toHaveLength(2)
|
||||
expect(theArray[0]).toBe('test')
|
||||
expect(theArray[1]).toBe('another test')
|
||||
})
|
||||
})
|
||||
|
||||
describe('arrayMoveImmutable', () => {
|
||||
it('should move the element to the desired position', () => {
|
||||
const theArray = ['test', 'testing']
|
||||
const newArray = arrayMoveImmutable(theArray, 0, 1)
|
||||
|
||||
expect(theArray).toHaveLength(2)
|
||||
expect(theArray[0]).toBe('test')
|
||||
expect(theArray[1]).toBe('testing')
|
||||
|
||||
expect(newArray).toHaveLength(2)
|
||||
expect(newArray[0]).toBe('testing')
|
||||
expect(newArray[1]).toBe('test')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getPercentage', () => {
|
||||
it('should return 0 if the first number is 0', () => {
|
||||
const percentage = getPercentage(0, 1)
|
||||
expect(percentage).toBe(0)
|
||||
})
|
||||
|
||||
it('should return 0 if the second number is 0', () => {
|
||||
const percentage = getPercentage(1, 0)
|
||||
expect(percentage).toBe(0)
|
||||
})
|
||||
|
||||
it('should swap first number with second number, if the later is greater', () => {
|
||||
const percentage = getPercentage(10, 1)
|
||||
expect(percentage).toBe(10)
|
||||
})
|
||||
|
||||
it('should trucate numbers up to two places', () => {
|
||||
expect(getPercentage(38.2, 125)).toBe(30.56)
|
||||
expect(getPercentage(67.55, 125)).toBe(54.04)
|
||||
expect(getPercentage(86.65, 125)).toBe(69.32)
|
||||
expect(getPercentage(98.85, 125)).toBe(79.08)
|
||||
})
|
||||
|
||||
it('should return the percentage of two numbers', () => {
|
||||
expect(getPercentage(4, 20)).toBe(20)
|
||||
expect(getPercentage(10, 10)).toBe(100)
|
||||
expect(getPercentage(10, 100)).toBe(10)
|
||||
expect(getPercentage(10, 40)).toBe(25)
|
||||
expect(getPercentage(15, 30)).toBe(50)
|
||||
})
|
||||
})
|
||||
|
||||
describe('groupTasksByCompletedStatus', () => {
|
||||
it('should return open tasks and completed tasks', () => {
|
||||
const tasks: TaskModel[] = [
|
||||
{
|
||||
id: 'test-1',
|
||||
description: 'Testing #1',
|
||||
completed: false,
|
||||
createdAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'test-2',
|
||||
description: 'Testing #2',
|
||||
createdAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'test-3',
|
||||
description: 'Testing #3',
|
||||
completed: true,
|
||||
createdAt: new Date(),
|
||||
},
|
||||
]
|
||||
|
||||
const { openTasks, completedTasks } = groupTasksByCompletedStatus(tasks)
|
||||
|
||||
expect(openTasks).toHaveLength(2)
|
||||
expect(openTasks[0]).toBe(tasks[0])
|
||||
expect(openTasks[1]).toBe(tasks[1])
|
||||
|
||||
expect(completedTasks).toHaveLength(1)
|
||||
expect(completedTasks[0]).toBe(tasks[2])
|
||||
})
|
||||
})
|
||||
|
||||
describe('getTaskArrayFromGroupedTasks', () => {
|
||||
it('should return an array of tasks', () => {
|
||||
const workTasks = [
|
||||
{
|
||||
id: 'test-b-1',
|
||||
description: 'Test #1',
|
||||
createdAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'test-b-2',
|
||||
description: 'Test #2',
|
||||
completed: true,
|
||||
createdAt: new Date(),
|
||||
},
|
||||
]
|
||||
|
||||
const personalTasks = [
|
||||
{
|
||||
id: 'test-c-1',
|
||||
description: 'Test #3',
|
||||
createdAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'test-c-2',
|
||||
description: 'Test #4',
|
||||
completed: true,
|
||||
createdAt: new Date(),
|
||||
},
|
||||
]
|
||||
|
||||
const groupedTasks: GroupModel[] = [
|
||||
{
|
||||
name: 'Work',
|
||||
sections: DEFAULT_SECTIONS,
|
||||
tasks: workTasks,
|
||||
},
|
||||
{
|
||||
name: 'Personal',
|
||||
sections: DEFAULT_SECTIONS,
|
||||
tasks: personalTasks,
|
||||
},
|
||||
]
|
||||
|
||||
const taskArray = getTaskArrayFromGroupedTasks(groupedTasks)
|
||||
|
||||
expect(taskArray).toHaveLength(workTasks.length + personalTasks.length)
|
||||
expect(taskArray).toStrictEqual([...workTasks, ...personalTasks])
|
||||
})
|
||||
})
|
||||
|
||||
describe('truncateText', () => {
|
||||
it('should return the text as-is', () => {
|
||||
const text = 'This is a simple text. It should not be truncated.'
|
||||
|
||||
expect(truncateText(text, 100)).toBe(text)
|
||||
})
|
||||
|
||||
it('should return the truncated text', () => {
|
||||
const text = 'This is a simple text. It should not be truncated.'
|
||||
const truncated = truncateText(text, 10)
|
||||
|
||||
expect(truncated).toHaveLength(13) // Includes ellipsis
|
||||
expect(truncated).toBe('This is a ...')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getPlainPreview', () => {
|
||||
it('should return a text preview in the format: {open tasks}/{all tasks}', () => {
|
||||
const workTasks = [
|
||||
{
|
||||
id: 'test-b-1',
|
||||
description: 'Test #1',
|
||||
createdAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'test-b-2',
|
||||
description: 'Test #2',
|
||||
completed: true,
|
||||
createdAt: new Date(),
|
||||
},
|
||||
]
|
||||
|
||||
const personalTasks = [
|
||||
{
|
||||
id: 'test-c-1',
|
||||
description: 'Test #3',
|
||||
createdAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'test-c-2',
|
||||
description: 'Test #4',
|
||||
completed: true,
|
||||
createdAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'test-c-3',
|
||||
description: 'Test #5',
|
||||
createdAt: new Date(),
|
||||
},
|
||||
]
|
||||
|
||||
const groupedTasks: GroupModel[] = [
|
||||
{
|
||||
name: 'Work',
|
||||
sections: DEFAULT_SECTIONS,
|
||||
tasks: workTasks,
|
||||
},
|
||||
{
|
||||
name: 'Personal',
|
||||
sections: DEFAULT_SECTIONS,
|
||||
tasks: personalTasks,
|
||||
},
|
||||
]
|
||||
|
||||
expect(getPlainPreview(groupedTasks)).toBe('2/5 tasks completed')
|
||||
expect(getPlainPreview([])).toBe('0/0 tasks completed')
|
||||
expect(getPlainPreview([{ name: 'Test', tasks: [], sections: [] }])).toBe('0/0 tasks completed')
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseMarkdownTasks', () => {
|
||||
it('should not return tasks if payload is not in correct format', () => {
|
||||
expect(parseMarkdownTasks('')).toBeUndefined()
|
||||
expect(parseMarkdownTasks(' ')).toBeUndefined()
|
||||
expect(parseMarkdownTasks('this is just a piece of text')).toBeUndefined()
|
||||
expect(parseMarkdownTasks(undefined)).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should not return tasks without descriptions', () => {
|
||||
const payload = '- [ ] '
|
||||
expect(parseMarkdownTasks(payload)).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should return tasks from a payload with correct format', () => {
|
||||
const payload = `- [ ] Foo
|
||||
- [x] Bar
|
||||
- [ ] Foobar`
|
||||
|
||||
expect(parseMarkdownTasks(payload)).toMatchObject<GroupModel>({
|
||||
name: 'Checklist',
|
||||
tasks: [
|
||||
{
|
||||
id: expect.any(String),
|
||||
description: 'Foo',
|
||||
completed: false,
|
||||
createdAt: expect.any(Date),
|
||||
},
|
||||
{
|
||||
id: expect.any(String),
|
||||
description: 'Bar',
|
||||
completed: true,
|
||||
createdAt: expect.any(Date),
|
||||
},
|
||||
{
|
||||
id: expect.any(String),
|
||||
description: 'Foobar',
|
||||
completed: false,
|
||||
createdAt: expect.any(Date),
|
||||
},
|
||||
],
|
||||
sections: DEFAULT_SECTIONS,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('arrayDefault', () => {
|
||||
it('should fallback to default value', () => {
|
||||
expect(arrayDefault({ defaultValue: [] })).toEqual([])
|
||||
expect(arrayDefault({ value: undefined, defaultValue: [] })).toEqual([])
|
||||
expect(arrayDefault({ value: [], defaultValue: ['test'] })).toEqual(['test'])
|
||||
})
|
||||
|
||||
it('should return value', () => {
|
||||
expect(arrayDefault({ value: ['test'], defaultValue: [] })).toEqual(['test'])
|
||||
})
|
||||
})
|
||||
@@ -1,144 +0,0 @@
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { DEFAULT_SECTIONS, GroupModel, TaskModel } from '../features/tasks/tasks-slice'
|
||||
|
||||
export function arrayMoveMutable(array: any[], fromIndex: number, toIndex: number) {
|
||||
const startIndex = fromIndex < 0 ? array.length + fromIndex : fromIndex
|
||||
if (startIndex >= 0 && startIndex < array.length) {
|
||||
const endIndex = toIndex < 0 ? array.length + toIndex : toIndex
|
||||
const [item] = array.splice(fromIndex, 1)
|
||||
array.splice(endIndex, 0, item)
|
||||
}
|
||||
}
|
||||
|
||||
export function arrayMoveImmutable(array: any[], fromIndex: number, toIndex: number) {
|
||||
array = [...array]
|
||||
arrayMoveMutable(array, fromIndex, toIndex)
|
||||
return array
|
||||
}
|
||||
|
||||
export function getPercentage(numberA: number, numberB: number): number {
|
||||
if (numberA === 0 || numberB === 0) {
|
||||
return 0
|
||||
}
|
||||
const min = Math.min(numberA, numberB)
|
||||
const max = Math.max(numberA, numberB)
|
||||
const percentage = (min / max) * 100
|
||||
return Number(percentage.toFixed(2))
|
||||
}
|
||||
|
||||
export function groupTasksByCompletedStatus(tasks: TaskModel[]) {
|
||||
const openTasks = tasks.filter((task) => !task.completed)
|
||||
const completedTasks = tasks.filter((task) => task.completed)
|
||||
return {
|
||||
openTasks,
|
||||
completedTasks,
|
||||
}
|
||||
}
|
||||
|
||||
export function getTaskArrayFromGroupedTasks(groupedTasks: GroupModel[]): TaskModel[] {
|
||||
let taskArray: TaskModel[] = []
|
||||
|
||||
groupedTasks.forEach((group) => {
|
||||
taskArray = taskArray.concat(group.tasks)
|
||||
})
|
||||
|
||||
return taskArray
|
||||
}
|
||||
|
||||
export function truncateText(text: string, limit: number = 50) {
|
||||
if (text.length <= limit) {
|
||||
return text
|
||||
}
|
||||
return text.substring(0, limit) + '...'
|
||||
}
|
||||
|
||||
export function getPlainPreview(groupedTasks: GroupModel[]) {
|
||||
const allTasks = getTaskArrayFromGroupedTasks(groupedTasks)
|
||||
const { completedTasks } = groupTasksByCompletedStatus(allTasks)
|
||||
|
||||
return `${completedTasks.length}/${allTasks.length} tasks completed`
|
||||
}
|
||||
|
||||
function createTaskFromLine(rawTask: string): TaskModel | undefined {
|
||||
const IS_COMPLETED = /^- \[x\] /i
|
||||
const OPEN_PREFIX = '- [ ] '
|
||||
|
||||
const description = rawTask.replace(OPEN_PREFIX, '').replace(IS_COMPLETED, '')
|
||||
|
||||
if (description.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
id: uuidv4(),
|
||||
description,
|
||||
completed: IS_COMPLETED.test(rawTask),
|
||||
createdAt: new Date(),
|
||||
}
|
||||
}
|
||||
|
||||
export function parseMarkdownTasks(payload?: string): GroupModel | undefined {
|
||||
if (!payload) {
|
||||
return
|
||||
}
|
||||
|
||||
const IS_LEGACY_FORMAT = /^- \[[x ]\] .*/gim
|
||||
if (!IS_LEGACY_FORMAT.test(payload)) {
|
||||
return
|
||||
}
|
||||
|
||||
const lines = payload.split('\n')
|
||||
const tasks: TaskModel[] = []
|
||||
|
||||
lines
|
||||
.filter((line) => line.replace(/ /g, '').length > 0)
|
||||
.map((line) => createTaskFromLine(line))
|
||||
.forEach((item) => item && tasks.push(item))
|
||||
|
||||
if (tasks.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'Checklist',
|
||||
tasks,
|
||||
sections: DEFAULT_SECTIONS,
|
||||
}
|
||||
}
|
||||
|
||||
export function isJsonString(rawString: string) {
|
||||
try {
|
||||
JSON.parse(rawString)
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export function isLastActiveGroup(allGroups: GroupModel[], groupName: string): boolean {
|
||||
if (allGroups.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
const lastActiveGroup = allGroups.reduce((prev, current) => {
|
||||
if (!prev.lastActive) {
|
||||
return current
|
||||
}
|
||||
if (!current.lastActive) {
|
||||
return prev
|
||||
}
|
||||
return prev.lastActive > current.lastActive ? prev : current
|
||||
})
|
||||
|
||||
return lastActiveGroup.name === groupName
|
||||
}
|
||||
|
||||
export function arrayDefault({ value, defaultValue }: { value?: any[]; defaultValue: any[] }) {
|
||||
if (!value) {
|
||||
return defaultValue
|
||||
}
|
||||
if (value.length === 0) {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import type { SettingsState } from './settings-slice'
|
||||
import reducer, { setCanEdit, setIsRunningOnMobile, setSpellCheckerEnabled } from './settings-slice'
|
||||
|
||||
it('should return the initial state', () => {
|
||||
return expect(
|
||||
reducer(undefined, {
|
||||
type: undefined,
|
||||
}),
|
||||
).toEqual({
|
||||
canEdit: true,
|
||||
isRunningOnMobile: false,
|
||||
spellCheckerEnabled: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle setting canEdit property', () => {
|
||||
const previousState: SettingsState = {
|
||||
canEdit: false,
|
||||
isRunningOnMobile: false,
|
||||
spellCheckerEnabled: false,
|
||||
}
|
||||
|
||||
expect(reducer(previousState, setCanEdit(true))).toEqual({
|
||||
...previousState,
|
||||
canEdit: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle setting isRunningOnMobile property', () => {
|
||||
const previousState: SettingsState = {
|
||||
canEdit: false,
|
||||
isRunningOnMobile: false,
|
||||
spellCheckerEnabled: false,
|
||||
}
|
||||
|
||||
expect(reducer(previousState, setIsRunningOnMobile(true))).toEqual({
|
||||
...previousState,
|
||||
isRunningOnMobile: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle setting spellCheckerEnabled property', () => {
|
||||
const previousState: SettingsState = {
|
||||
canEdit: false,
|
||||
isRunningOnMobile: false,
|
||||
spellCheckerEnabled: false,
|
||||
}
|
||||
|
||||
expect(reducer(previousState, setSpellCheckerEnabled(true))).toEqual({
|
||||
...previousState,
|
||||
spellCheckerEnabled: true,
|
||||
})
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user