├── .editorconfig ├── .eslintrc ├── .github └── workflows │ ├── alpha.yml │ └── release.yml ├── .gitignore ├── .storybook ├── main.js └── preview.js ├── .vscode └── extensions.json ├── README.MD ├── browserslist ├── cypress.config.ts ├── cypress ├── component │ ├── app.tsx │ ├── container.tsx │ ├── localization.ts │ └── test.cy.tsx ├── fixtures │ └── example.json └── support │ ├── commands.ts │ ├── component-index.html │ └── component.ts ├── package-lock.json ├── package.json ├── prepare-package.mjs ├── src ├── lib │ ├── cron-base-props.type.ts │ ├── cron-base-tab-props.type.ts │ ├── cron-localization.ts │ ├── cron-props.type.ts │ ├── cron.ts │ ├── helpers.ts │ ├── index.ts │ ├── quartz │ │ ├── index.ts │ │ ├── quartz.tsx │ │ └── tabs │ │ │ ├── day │ │ │ └── day.tsx │ │ │ ├── hour │ │ │ └── hour.tsx │ │ │ ├── index.ts │ │ │ ├── minute │ │ │ └── minute.tsx │ │ │ ├── month │ │ │ └── month.tsx │ │ │ ├── second │ │ │ └── second.tsx │ │ │ ├── shared │ │ │ ├── increment.tsx │ │ │ ├── index.ts │ │ │ └── tab-props.type.ts │ │ │ └── year │ │ │ └── year.tsx │ ├── shared │ │ ├── and.tsx │ │ ├── every.tsx │ │ ├── index.ts │ │ ├── props.type.ts │ │ └── range.tsx │ └── unix │ │ ├── index.ts │ │ ├── tabs │ │ ├── day │ │ │ └── day.tsx │ │ ├── hour │ │ │ └── hour.tsx │ │ ├── index.ts │ │ ├── minute │ │ │ └── minute.tsx │ │ ├── month │ │ │ └── month.tsx │ │ └── shared │ │ │ ├── increment.tsx │ │ │ ├── index.ts │ │ │ └── tab-props.type.ts │ │ └── unix.tsx └── stories │ ├── Introduction.stories.mdx │ ├── assets │ ├── code-brackets.svg │ ├── colors.svg │ ├── comments.svg │ ├── direction.svg │ ├── flow.svg │ ├── plugin.svg │ ├── repo.svg │ └── stackalt.svg │ ├── quartz-cron.stories.tsx │ └── unix-cron.stories.tsx ├── tsconfig.base.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint" 6 | ], 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/eslint-recommended", 10 | "plugin:@typescript-eslint/recommended" 11 | ], 12 | "rules": { 13 | "indent": ["error", 2] 14 | }, 15 | "ignorePatterns": [ 16 | "dist", 17 | "coverage", 18 | "node_modules" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/alpha.yml: -------------------------------------------------------------------------------- 1 | name: Alpha 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'feature/**' 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [16.16.0] 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - name: Get npm cache directory 24 | id: npm-cache-dir 25 | run: | 26 | echo "::set-output name=dir::$(npm config get cache)" 27 | - name: Cache node modules 28 | uses: actions/cache@v3 29 | id: npm-cache 30 | with: 31 | path: ${{ steps.npm-cache-dir.outputs.dir }} 32 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 33 | restore-keys: | 34 | ${{ runner.os }}-node- 35 | - name: Install Dependencies 36 | run: npm ci 37 | env: 38 | CI: true 39 | - name: Linting 40 | run: npm run lint 41 | env: 42 | CI: true 43 | - name: Units Testing 44 | run: npm run e2e-run 45 | env: 46 | CI: true 47 | - name: Publish Test Report 48 | uses: mikepenz/action-junit-report@v3 49 | if: always() # always run even if the previous step fails 50 | with: 51 | report_paths: 'e2e-results/**/*.xml' 52 | - name: Build Dist 53 | run: npm run build-alpha 54 | env: 55 | CI: true 56 | - name: Npm Configuration 57 | run: npm config set //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN 58 | env: 59 | CI: true 60 | NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 61 | - name: Publish Cron Core 62 | run: npm run publish-alpha 63 | env: 64 | CI: true 65 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | main 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [16.16.0] 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - name: Get npm cache directory 24 | id: npm-cache-dir 25 | run: | 26 | echo "::set-output name=dir::$(npm config get cache)" 27 | - name: Cache node modules 28 | uses: actions/cache@v3 29 | id: npm-cache 30 | with: 31 | path: ${{ steps.npm-cache-dir.outputs.dir }} 32 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 33 | restore-keys: | 34 | ${{ runner.os }}-node- 35 | - name: Install Dependencies 36 | run: npm ci 37 | env: 38 | CI: true 39 | - name: Linting 40 | run: npm run lint 41 | env: 42 | CI: true 43 | - name: Units Testing 44 | run: npm run e2e-run 45 | env: 46 | CI: true 47 | - name: Publish Test Report 48 | uses: mikepenz/action-junit-report@v3 49 | if: always() # always run even if the previous step fails 50 | with: 51 | report_paths: 'e2e-results/**/*.xml' 52 | - name: Build Dist 53 | run: npm run build 54 | env: 55 | CI: true 56 | - name: Npm Configuration 57 | run: npm config set //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN 58 | env: 59 | CI: true 60 | NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 61 | - name: Publish Cron Core 62 | run: npm run publish 63 | env: 64 | CI: true 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | storybook-static 4 | e2e-results 5 | cypress/videos 6 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | core: { 3 | builder: 'webpack5', 4 | }, 5 | "stories": [ 6 | "../src/stories/**/*.stories.mdx", 7 | "../src/stories/**/*.stories.@(js|jsx|ts|tsx)" 8 | ], 9 | "addons": [ 10 | "@storybook/addon-links", 11 | "@storybook/addon-essentials", 12 | "@storybook/addon-interactions", 13 | { 14 | "name": "storybook-addon-sass-postcss", 15 | "options": { 16 | sassLoaderOptions: { 17 | implementation: require('sass'), 18 | } 19 | } 20 | } 21 | ], 22 | "framework": "@storybook/react" 23 | } 24 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import '!style-loader!css-loader!sass-loader!bootstrap/scss/bootstrap.scss' 2 | 3 | export const parameters = { 4 | actions: { argTypesRegex: "^on[A-Z].*" }, 5 | controls: { 6 | matchers: { 7 | color: /(background|color)$/i, 8 | date: /Date$/, 9 | }, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "nrwl.angular-console", 4 | "esbenp.prettier-vscode", 5 | "firsttris.vscode-jest-runner", 6 | "dbaeumer.vscode-eslint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct-single.svg)](https://vshymanskyy.github.io/StandWithUkraine) 2 | 3 | # Quartz/Unix Cron Component - React 4 | 5 | [React](https://reactjs.org/) cron widget built from the ground up using only [Bootstrap 4](https://getbootstrap.com/) CSS. 6 | 7 | Please check our [demo & documentation](https://recron.emptyui.com) and the list of 8 | [issues](https://github.com/ua-cron/react/issues) to see all the things we are working on. Feel free to make comments there. 9 | 10 |

11 | 12 | Quartz/Unix Cron Component - React 16 | 17 |

18 | 19 |

20 | 21 | npm version 24 | 25 | 26 | npm downloads 29 | 30 |

31 | 32 | ## Getting Started 33 | 34 | This is an open source project that builds a cron builder component for React applications. 35 | It supports Quartz/Unix cron string formats for both input and output. 36 | Inspired by this [non-react](https://www.freeformatter.com/cron-expression-generator-quartz.html) implementation. 37 | 38 | ## Installation 39 | You can use either the npm or yarn command-line tool to install packages. 40 | ``` 41 | npm install --save @sbzen/re-cron 42 | ``` 43 | 44 | ## Display the cron component 45 | Import and add the cron component into your jsx/tsx. 46 | 47 | ```tsx 48 | import React, { useState } from 'react'; 49 | import { ReQuartzCron } from '@sbzen/re-cron'; 50 | 51 | const App = () => { 52 | const [value, setValue] = useState('2,0,4,3,1 0/1 3/2 ? * 4/5 *'); 53 | 54 | return ( 55 | 58 | ); 59 | }; 60 | export default App; 61 | ``` 62 | 63 | ## Usage & Demo 64 | Main source of API documentation and usage scenarios available here: https://recron.emptyui.com. 65 | 66 | 67 | ## Compatibility 68 | The only two required dependencies are React and cron-core. 69 | The Bootstrap CSS is optional as you can use this component with your own styling. 70 | Here is the versions compatibility list: 71 | 72 | | Re Cron | React | Bootstrap CSS | 73 | | ------------- | ------------- | -------------- | 74 | | 0.0.1 | 16.x.x | 4.x.x | 75 | | 2.0.0 | 17.x.x | 4.x.x | 76 | -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. 13 | -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | fileServerFolder: './', 5 | component: { 6 | numTestsKeptInMemory: 0, 7 | devServer: { 8 | framework: "create-react-app", 9 | bundler: "webpack", 10 | }, 11 | reporter: 'junit', 12 | reporterOptions: { 13 | mochaFile: 'e2e-results/test-result-[hash].xml', 14 | includePending: true, 15 | attachments: true, 16 | outputs: true 17 | } 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /cypress/component/app.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { ReUnixCron, ReQuartzCron, Tab } from '@sbzen/re-cron'; 3 | 4 | import { TestContainer } from './container'; 5 | 6 | export const App = () => { 7 | return ( 8 | 9 | }/> 14 | 15 | }/> 19 | 20 | ); 21 | }; 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /cypress/component/container.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Tab, CronLocalization } from '@sbzen/re-cron'; 3 | 4 | import { getLocalizationPathes, getLocalization } from './localization'; 5 | 6 | export const TestContainer = ({ 7 | type = '', 8 | render, 9 | initialValue, 10 | initialTabs = [Tab.SECONDS, Tab.MINUTES, Tab.HOURS, Tab.DAY, Tab.MONTH, Tab.YEAR] 11 | }) => { 12 | const [value, setValue] = useState(initialValue); 13 | const [disabled, setDisabled] = useState(false); 14 | const [hideTabs, setHideTabs] = useState(false); 15 | const [cssClassPrefix, setCssClassPrefix] = useState(undefined); 16 | const [activeTab, setActiveTab] = useState(undefined); 17 | const [localization, setLocalization] = useState(undefined); 18 | const [tabs, setTabs] = useState(undefined); 19 | 20 | const getLocalizationValue = (path: string, loc = getLocalization()) => { 21 | const paths = path.split('.'); 22 | const lastProp = paths[paths.length - 1]; 23 | const store = paths.slice(0, paths.length - 1).reduce((acc, prop) => acc[prop], loc); 24 | return store[lastProp]; 25 | } 26 | 27 | const updateLocalizationField = (path: string, localizationValue: string) => { 28 | if (!localization) { 29 | return; 30 | } 31 | const paths = path.split('.'); 32 | const lastProp = paths[paths.length - 1]; 33 | const store = paths.slice(0, paths.length - 1).reduce((acc, prop) => acc[prop], localization); 34 | store[lastProp] = localizationValue; 35 | setLocalization({ ...localization }); 36 | }; 37 | 38 | const addedTab = (tab: Tab) => (tabs || []).includes(tab); 39 | 40 | const toggleTab = (tab: Tab) => { 41 | const tabsToUse = tabs || []; 42 | if (addedTab(tab)) { 43 | setTabs(tabsToUse.filter(t => t !== tab)); 44 | return; 45 | } 46 | setTabs([ 47 | ...tabsToUse, 48 | tab 49 | ]); 50 | }; 51 | 52 | return ( 53 |
56 | 57 |

{type}

58 | 59 | setValue(e.target.value)}/> 63 | 64 | 70 | 76 | 82 | 88 | 94 | 100 | 106 | 107 |
108 | {getLocalizationPathes().map((path, i) => ( 109 | updateLocalizationField(path, e.target.value)}/> 118 | ))} 119 |
120 | 121 |
122 | 128 | 134 | {initialTabs.map((tab, i) => ( 135 | 144 | ))} 145 |
146 | 147 |
148 | {initialTabs.map((tab, i) => ( 149 | 157 | ))} 158 |
159 | 160 |
161 | {activeTab} 162 |
163 | 164 |
165 | {render({ 166 | value, 167 | cssClassPrefix, 168 | localization, 169 | activeTab, 170 | tabs, 171 | onChange: setValue, 172 | hideTabs, 173 | onTabChange: setActiveTab, 174 | disabled 175 | })} 176 |
177 |
178 | ); 179 | }; 180 | -------------------------------------------------------------------------------- /cypress/component/localization.ts: -------------------------------------------------------------------------------- 1 | import { CronLocalization } from '@sbzen/re-cron'; 2 | 3 | export const getLocalizationPathes = (obj: CronLocalization = getLocalization(), parentProp = '') => { 4 | return Object.keys(obj).reduce((acc, prop) => { 5 | const propsLine = [parentProp, prop].filter(a => !!a).join('.'); 6 | 7 | const value = obj[prop]; 8 | let values: string[] = []; 9 | 10 | if (typeof value !== 'string') { 11 | values = getLocalizationPathes(value, propsLine); 12 | } else { 13 | values = [propsLine]; 14 | } 15 | 16 | return [ 17 | ...acc, 18 | ...values 19 | ]; 20 | }, [] as string[]); 21 | }; 22 | 23 | export const getLocalization = (): CronLocalization => ({ 24 | common: { 25 | month: { 26 | january: 'January', 27 | february: 'February', 28 | march: 'March', 29 | april: 'April', 30 | may: 'May', 31 | june: 'June', 32 | july: 'July', 33 | august: 'August', 34 | september: 'September', 35 | october: 'October', 36 | november: 'November', 37 | december: 'December' 38 | }, 39 | dayOfWeek: { 40 | sunday: 'Sunday', 41 | monday: 'Monday', 42 | tuesday: 'Tuesday', 43 | wednesday: 'Wednesday', 44 | thursday: 'Thursday', 45 | friday: 'Friday', 46 | saturday: 'Saturday' 47 | }, 48 | dayOfMonth: { 49 | '1st': '1st', 50 | '2nd': '2nd', 51 | '3rd': '3rd', 52 | '4th': '4th', 53 | '5th': '5th', 54 | '6th': '6th', 55 | '7th': '7th', 56 | '8th': '8th', 57 | '9th': '9th', 58 | '10th': '10th', 59 | '11th': '11th', 60 | '12th': '12th', 61 | '13th': '13th', 62 | '14th': '14th', 63 | '15th': '15th', 64 | '16th': '16th', 65 | '17th': '17th', 66 | '18th': '18th', 67 | '19th': '19th', 68 | '20th': '20th', 69 | '21st': '21st', 70 | '22nd': '22nd', 71 | '23rd': '23rd', 72 | '24th': '24th', 73 | '25th': '25th', 74 | '26th': '26th', 75 | '27th': '27th', 76 | '28th': '28th', 77 | '29th': '29th', 78 | '30th': '30th', 79 | '31st': '31st' 80 | } 81 | }, 82 | tabs: { 83 | seconds: 'Seconds', 84 | minutes: 'Minutes', 85 | hours: 'Hours', 86 | day: 'Day', 87 | month: 'Month', 88 | year: 'Year' 89 | }, 90 | quartz: { 91 | day: { 92 | every: { 93 | label: 'Every day' 94 | }, 95 | dayOfWeekIncrement: { 96 | label1: 'Every', 97 | label2: 'day(s) starting on' 98 | }, 99 | dayOfMonthIncrement: { 100 | label1: 'Every', 101 | label2: 'day(s) starting on the', 102 | label3: 'of the month' 103 | }, 104 | dayOfWeekAnd: { 105 | label: 'Specific day of week (choose one or many)' 106 | }, 107 | dayOfWeekRange: { 108 | label1: 'Every day between', 109 | label2: 'and' 110 | }, 111 | dayOfMonthAnd: { 112 | label: 'Specific day of month (choose one or many)' 113 | }, 114 | dayOfMonthLastDay: { 115 | label: 'On the last day of the month' 116 | }, 117 | dayOfMonthLastDayWeek: { 118 | label: 'On the last weekday of the month' 119 | }, 120 | dayOfWeekLastNTHDayWeek: { 121 | label1: 'On the last', 122 | label2: 'of the month' 123 | }, 124 | dayOfMonthDaysBeforeEndMonth: { 125 | label: 'day(s) before the end of the month' 126 | }, 127 | dayOfMonthNearestWeekDayOfMonth: { 128 | label1: 'Nearest weekday (Monday to Friday) to the', 129 | label2: 'of the month' 130 | }, 131 | dayOfWeekNTHWeekDayOfMonth: { 132 | label1: 'On the', 133 | label2: 'of the month' 134 | } 135 | }, 136 | month: { 137 | every: { 138 | label: 'Every month' 139 | }, 140 | increment: { 141 | label1: 'Every', 142 | label2: 'month(s) starting at month', 143 | }, 144 | and: { 145 | label: 'Specific month (choose one or many)' 146 | }, 147 | range: { 148 | label1: 'Every month between month', 149 | label2: 'and month' 150 | } 151 | }, 152 | second: { 153 | every: { 154 | label: 'Every second' 155 | }, 156 | increment: { 157 | label1: 'Every', 158 | label2: 'second(s) starting at second', 159 | }, 160 | and: { 161 | label: 'Specific second (choose one or many)' 162 | }, 163 | range: { 164 | label1: 'Every second between second', 165 | label2: 'and second' 166 | } 167 | }, 168 | minute: { 169 | every: { 170 | label: 'Every minute' 171 | }, 172 | increment: { 173 | label1: 'Every', 174 | label2: 'minute(s) starting at minute', 175 | }, 176 | and: { 177 | label: 'Specific minute (choose one or many)' 178 | }, 179 | range: { 180 | label1: 'Every minute between minute', 181 | label2: 'and minute' 182 | } 183 | }, 184 | hour: { 185 | every: { 186 | label: 'Every hour' 187 | }, 188 | increment: { 189 | label1: 'Every', 190 | label2: 'hour(s) starting at hour', 191 | }, 192 | and: { 193 | label: 'Specific hour (choose one or many)' 194 | }, 195 | range: { 196 | label1: 'Every hour between hour', 197 | label2: 'and hour' 198 | } 199 | }, 200 | year: { 201 | every: { 202 | label: 'Any year' 203 | }, 204 | increment: { 205 | label1: 'Every', 206 | label2: 'year(s) starting at year', 207 | }, 208 | and: { 209 | label: 'Specific year (choose one or many)' 210 | }, 211 | range: { 212 | label1: 'Every year between year', 213 | label2: 'and year' 214 | } 215 | } 216 | }, 217 | unix: { 218 | day: { 219 | every: { 220 | label: 'Every day' 221 | }, 222 | dayOfWeekIncrement: { 223 | label1: 'Every', 224 | label2: 'day(s) of week' 225 | }, 226 | dayOfMonthIncrement: { 227 | label1: 'Every', 228 | label2: 'day(s) of month' 229 | }, 230 | dayOfWeekAnd: { 231 | label: 'Specific day of week (choose one or many)' 232 | }, 233 | dayOfMonthAnd: { 234 | label: 'Specific day of month (choose one or many)' 235 | } 236 | }, 237 | month: { 238 | every: { 239 | label: 'Every month' 240 | }, 241 | increment: { 242 | label1: 'Every', 243 | label2: 'month(s)', 244 | }, 245 | and: { 246 | label: 'Specific month (choose one or many)' 247 | }, 248 | range: { 249 | label1: 'Every month between month', 250 | label2: 'and month' 251 | } 252 | }, 253 | minute: { 254 | every: { 255 | label: 'Every minute' 256 | }, 257 | increment: { 258 | label1: 'Every', 259 | label2: 'minute(s)', 260 | }, 261 | and: { 262 | label: 'Specific minute (choose one or many)' 263 | }, 264 | range: { 265 | label1: 'Every minute between minute', 266 | label2: 'and minute' 267 | } 268 | }, 269 | hour: { 270 | every: { 271 | label: 'Every hour' 272 | }, 273 | increment: { 274 | label1: 'Every', 275 | label2: 'hour(s)', 276 | }, 277 | and: { 278 | label: 'Specific hour (choose one or many)' 279 | }, 280 | range: { 281 | label1: 'Every hour between hour', 282 | label2: 'and hour' 283 | } 284 | } 285 | } 286 | }); 287 | -------------------------------------------------------------------------------- /cypress/component/test.cy.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { mount } from '@cypress/react' 3 | 4 | import { activeTab, cssClasses, localization, disabled, tabChange, tabs, hideTabs, values } from '@sbzen/e2e'; 5 | 6 | import { App } from './app'; 7 | 8 | const beforeEachCb = () => beforeEach(() => mount()); 9 | 10 | activeTab(beforeEachCb); 11 | cssClasses(beforeEachCb, false); 12 | localization(beforeEachCb); 13 | disabled(beforeEachCb); 14 | tabChange(beforeEachCb); 15 | tabs(beforeEachCb); 16 | hideTabs(beforeEachCb); 17 | values(beforeEachCb); 18 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************** 3 | // This example commands.ts shows you how to 4 | // create various custom commands and overwrite 5 | // existing commands. 6 | // 7 | // For more comprehensive examples of custom 8 | // commands please read more here: 9 | // https://on.cypress.io/custom-commands 10 | // *********************************************** 11 | // 12 | // 13 | // -- This is a parent command -- 14 | // Cypress.Commands.add('login', (email, password) => { ... }) 15 | // 16 | // 17 | // -- This is a child command -- 18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 19 | // 20 | // 21 | // -- This is a dual command -- 22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 23 | // 24 | // 25 | // -- This will overwrite an existing command -- 26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 27 | // 28 | // declare global { 29 | // namespace Cypress { 30 | // interface Chainable { 31 | // login(email: string, password: string): Chainable 32 | // drag(subject: string, options?: Partial): Chainable 33 | // dismiss(subject: string, options?: Partial): Chainable 34 | // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable 35 | // } 36 | // } 37 | // } -------------------------------------------------------------------------------- /cypress/support/component-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Components App 8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /cypress/support/component.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/component.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | 22 | import { mount } from 'cypress/react' 23 | 24 | // Augment the Cypress namespace to include type definitions for 25 | // your custom command. 26 | // Alternatively, can be defined in cypress/support/component.d.ts 27 | // with a at the top of your spec. 28 | declare global { 29 | // eslint-disable-next-line 30 | namespace Cypress { 31 | interface Chainable { 32 | mount: typeof mount 33 | } 34 | } 35 | } 36 | 37 | Cypress.Commands.add('mount', mount) 38 | 39 | // Example use: 40 | // cy.mount() 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sbzen/re-cron", 3 | "version": "2.0.7", 4 | "description": "Quartz Cron Component - React", 5 | "author": "Serhii Bzenko ", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+ssh://git@github.com/ua-cron/react.git" 10 | }, 11 | "keywords": [ 12 | "react", 13 | "bootstap", 14 | "cron", 15 | "quartz", 16 | "cron builder", 17 | "cron generator", 18 | "quartz cron" 19 | ], 20 | "homepage": "https://recron.emptyui.com", 21 | "sideEffects": false, 22 | "bugs": { 23 | "url": "https://github.com/ua-cron/react/issues" 24 | }, 25 | "scripts": { 26 | "copy-rm": "cp README.MD dist/README.MD", 27 | "build": "npm run clean && tsc && node prepare-package.mjs && npm run copy-rm", 28 | "build-alpha": "ALPHA=1 npm run build", 29 | "clean": "rm -rf dist", 30 | "test-debug": "node --inspect-brk node_modules/.bin/jest --runInBand", 31 | "test": "jest", 32 | "lint": "eslint . --ext .ts --ext .tsx", 33 | "storybook": "start-storybook -p 6006", 34 | "build-storybook": "build-storybook", 35 | "e2e-inject": "npm install ./dist --force", 36 | "e2e": "npm run build && npm run e2e-inject && npx cypress open --component", 37 | "e2e-run": "npm run build && npm run e2e-inject && npx cypress run --component", 38 | "publish-alpha": "npm publish ./dist --tag alpha --access=public", 39 | "publish": "npm publish ./dist" 40 | }, 41 | "dependencies": { 42 | "@sbzen/cron-core": "2.0.3" 43 | }, 44 | "peerDependencies": { 45 | "react": "*", 46 | "react-dom": "*" 47 | }, 48 | "devDependencies": { 49 | "@babel/core": "^7.18.6", 50 | "@babel/preset-typescript": "^7.18.6", 51 | "@cypress/react": "^6.1.0", 52 | "@sbzen/e2e": "https://github.com/ua-cron/e2e/releases/download/20240602212421/dist.tar.gz", 53 | "@storybook/addon-actions": "^6.5.10", 54 | "@storybook/addon-essentials": "^6.5.10", 55 | "@storybook/addon-interactions": "^6.5.10", 56 | "@storybook/addon-links": "^6.5.10", 57 | "@storybook/builder-webpack5": "^6.5.10", 58 | "@storybook/manager-webpack5": "^6.5.10", 59 | "@storybook/react": "^6.5.10", 60 | "@storybook/testing-library": "0.0.13", 61 | "@types/jest": "^27.5.1", 62 | "@types/lodash": "^4.14.182", 63 | "@types/node": "^17.0.35", 64 | "@types/react": "^17.0.2", 65 | "@typescript-eslint/eslint-plugin": "^5.30.0", 66 | "@typescript-eslint/parser": "^5.30.0", 67 | "babel-loader": "^8.2.5", 68 | "bootstrap": "^4.6.1", 69 | "eslint": "^8.18.0", 70 | "jest": "^28.1.0", 71 | "lodash": "^4.17.21", 72 | "react-scripts": "^5.0.1", 73 | "sass": "^1.53.0", 74 | "storybook-addon-sass-postcss": "^0.1.3", 75 | "ts-jest": "^28.0.2", 76 | "ts-node": "^10.8.0", 77 | "typescript": "4.6.4" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /prepare-package.mjs: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from 'fs'; 2 | import { resolve, dirname } from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | import { env } from 'node:process'; 5 | 6 | const isAlpha = env.ALPHA; 7 | const __dirname = dirname(fileURLToPath(import.meta.url)); 8 | const distLibPath = resolve(__dirname, './dist'); 9 | const packagePath = resolve(__dirname, './package.json'); 10 | 11 | const packageString = readFileSync(packagePath); 12 | const { 13 | name, 14 | version, 15 | author, 16 | homepage, 17 | keywords, 18 | license, 19 | repository, 20 | sideEffects, 21 | dependencies, 22 | peerDependencies, 23 | bugs 24 | } = JSON.parse(packageString); 25 | 26 | const nextVersion = isAlpha ? `${version}-alpha.${Date.now()}` : version; 27 | const nextDependencies = Object.keys(dependencies).reduce((acc, prop) => { 28 | if (prop === name) { 29 | return acc; 30 | } 31 | return { 32 | ...acc, 33 | [prop]: dependencies[prop] 34 | } 35 | }, {}); 36 | 37 | const libPackage = JSON.stringify({ 38 | name, 39 | version: nextVersion, 40 | author, 41 | license, 42 | keywords, 43 | repository, 44 | homepage, 45 | dependencies: nextDependencies, 46 | peerDependencies, 47 | sideEffects, 48 | bugs, 49 | main: 'index.js', 50 | types: 'index.d.ts' 51 | }, null, ' '); 52 | 53 | writeFileSync(resolve(distLibPath, './package.json'), libPackage); 54 | -------------------------------------------------------------------------------- /src/lib/cron-base-props.type.ts: -------------------------------------------------------------------------------- 1 | export type CronBaseProps = { 2 | cssClassPrefix?: string 3 | }; 4 | -------------------------------------------------------------------------------- /src/lib/cron-base-tab-props.type.ts: -------------------------------------------------------------------------------- 1 | import { CronUnixUIService, CronQuartzUIService } from '@sbzen/cron-core'; 2 | 3 | import { CronBaseProps } from './cron-base-props.type'; 4 | import { localization } from './cron-localization'; 5 | 6 | export type CronBaseTabProps = { 7 | localization: typeof localization, 8 | session: string, 9 | service: T 10 | } & CronBaseProps; 11 | -------------------------------------------------------------------------------- /src/lib/cron-localization.ts: -------------------------------------------------------------------------------- 1 | export const localization = { 2 | common: { 3 | month: { 4 | january: 'January', 5 | february: 'February', 6 | march: 'March', 7 | april: 'April', 8 | may: 'May', 9 | june: 'June', 10 | july: 'July', 11 | august: 'August', 12 | september: 'September', 13 | october: 'October', 14 | november: 'November', 15 | december: 'December' 16 | }, 17 | dayOfWeek: { 18 | sunday: 'Sunday', 19 | monday: 'Monday', 20 | tuesday: 'Tuesday', 21 | wednesday: 'Wednesday', 22 | thursday: 'Thursday', 23 | friday: 'Friday', 24 | saturday: 'Saturday' 25 | }, 26 | dayOfMonth: { 27 | '1st': '1st', 28 | '2nd': '2nd', 29 | '3rd': '3rd', 30 | '4th': '4th', 31 | '5th': '5th', 32 | '6th': '6th', 33 | '7th': '7th', 34 | '8th': '8th', 35 | '9th': '9th', 36 | '10th': '10th', 37 | '11th': '11th', 38 | '12th': '12th', 39 | '13th': '13th', 40 | '14th': '14th', 41 | '15th': '15th', 42 | '16th': '16th', 43 | '17th': '17th', 44 | '18th': '18th', 45 | '19th': '19th', 46 | '20th': '20th', 47 | '21st': '21st', 48 | '22nd': '22nd', 49 | '23rd': '23rd', 50 | '24th': '24th', 51 | '25th': '25th', 52 | '26th': '26th', 53 | '27th': '27th', 54 | '28th': '28th', 55 | '29th': '29th', 56 | '30th': '30th', 57 | '31st': '31st' 58 | } 59 | }, 60 | tabs: { 61 | seconds: 'Seconds', 62 | minutes: 'Minutes', 63 | hours: 'Hours', 64 | day: 'Day', 65 | month: 'Month', 66 | year: 'Year' 67 | }, 68 | quartz: { 69 | day: { 70 | every: { 71 | label: 'Every day' 72 | }, 73 | dayOfWeekIncrement: { 74 | label1: 'Every', 75 | label2: 'day(s) starting on' 76 | }, 77 | dayOfMonthIncrement: { 78 | label1: 'Every', 79 | label2: 'day(s) starting on the', 80 | label3: 'of the month' 81 | }, 82 | dayOfWeekAnd: { 83 | label: 'Specific day of week (choose one or many)' 84 | }, 85 | dayOfWeekRange: { 86 | label1: 'Every day between', 87 | label2: 'and' 88 | }, 89 | dayOfMonthAnd: { 90 | label: 'Specific day of month (choose one or many)' 91 | }, 92 | dayOfMonthLastDay: { 93 | label: 'On the last day of the month' 94 | }, 95 | dayOfMonthLastDayWeek: { 96 | label: 'On the last weekday of the month' 97 | }, 98 | dayOfWeekLastNTHDayWeek: { 99 | label1: 'On the last', 100 | label2: 'of the month' 101 | }, 102 | dayOfMonthDaysBeforeEndMonth: { 103 | label: 'day(s) before the end of the month' 104 | }, 105 | dayOfMonthNearestWeekDayOfMonth: { 106 | label1: 'Nearest weekday (Monday to Friday) to the', 107 | label2: 'of the month' 108 | }, 109 | dayOfWeekNTHWeekDayOfMonth: { 110 | label1: 'On the', 111 | label2: 'of the month' 112 | } 113 | }, 114 | month: { 115 | every: { 116 | label: 'Every month' 117 | }, 118 | increment: { 119 | label1: 'Every', 120 | label2: 'month(s) starting at month', 121 | }, 122 | and: { 123 | label: 'Specific month (choose one or many)' 124 | }, 125 | range: { 126 | label1: 'Every month between month', 127 | label2: 'and month' 128 | } 129 | }, 130 | second: { 131 | every: { 132 | label: 'Every second' 133 | }, 134 | increment: { 135 | label1: 'Every', 136 | label2: 'second(s) starting at second', 137 | }, 138 | and: { 139 | label: 'Specific second (choose one or many)' 140 | }, 141 | range: { 142 | label1: 'Every second between second', 143 | label2: 'and second' 144 | } 145 | }, 146 | minute: { 147 | every: { 148 | label: 'Every minute' 149 | }, 150 | increment: { 151 | label1: 'Every', 152 | label2: 'minute(s) starting at minute', 153 | }, 154 | and: { 155 | label: 'Specific minute (choose one or many)' 156 | }, 157 | range: { 158 | label1: 'Every minute between minute', 159 | label2: 'and minute' 160 | } 161 | }, 162 | hour: { 163 | every: { 164 | label: 'Every hour' 165 | }, 166 | increment: { 167 | label1: 'Every', 168 | label2: 'hour(s) starting at hour', 169 | }, 170 | and: { 171 | label: 'Specific hour (choose one or many)' 172 | }, 173 | range: { 174 | label1: 'Every hour between hour', 175 | label2: 'and hour' 176 | } 177 | }, 178 | year: { 179 | every: { 180 | label: 'Any year' 181 | }, 182 | increment: { 183 | label1: 'Every', 184 | label2: 'year(s) starting at year', 185 | }, 186 | and: { 187 | label: 'Specific year (choose one or many)' 188 | }, 189 | range: { 190 | label1: 'Every year between year', 191 | label2: 'and year' 192 | } 193 | } 194 | }, 195 | unix: { 196 | day: { 197 | every: { 198 | label: 'Every day' 199 | }, 200 | dayOfWeekIncrement: { 201 | label1: 'Every', 202 | label2: 'day(s) of week' 203 | }, 204 | dayOfMonthIncrement: { 205 | label1: 'Every', 206 | label2: 'day(s) of month' 207 | }, 208 | dayOfWeekAnd: { 209 | label: 'Specific day of week (choose one or many)' 210 | }, 211 | dayOfMonthAnd: { 212 | label: 'Specific day of month (choose one or many)' 213 | } 214 | }, 215 | month: { 216 | every: { 217 | label: 'Every month' 218 | }, 219 | increment: { 220 | label1: 'Every', 221 | label2: 'month(s)', 222 | }, 223 | and: { 224 | label: 'Specific month (choose one or many)' 225 | }, 226 | range: { 227 | label1: 'Every month between month', 228 | label2: 'and month' 229 | } 230 | }, 231 | minute: { 232 | every: { 233 | label: 'Every minute' 234 | }, 235 | increment: { 236 | label1: 'Every', 237 | label2: 'minute(s)', 238 | }, 239 | and: { 240 | label: 'Specific minute (choose one or many)' 241 | }, 242 | range: { 243 | label1: 'Every minute between minute', 244 | label2: 'and minute' 245 | } 246 | }, 247 | hour: { 248 | every: { 249 | label: 'Every hour' 250 | }, 251 | increment: { 252 | label1: 'Every', 253 | label2: 'hour(s)', 254 | }, 255 | and: { 256 | label: 'Specific hour (choose one or many)' 257 | }, 258 | range: { 259 | label1: 'Every hour between hour', 260 | label2: 'and hour' 261 | } 262 | } 263 | } 264 | }; 265 | 266 | export type DeepPartial = { 267 | [P in keyof T]?: DeepPartial; 268 | }; 269 | 270 | export type CronLocalization = DeepPartial; 271 | -------------------------------------------------------------------------------- /src/lib/cron-props.type.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@sbzen/cron-core'; 2 | 3 | import { CronBaseProps } from './cron-base-props.type'; 4 | import { CronLocalization } from './cron-localization'; 5 | 6 | export type CronHostProps = { 7 | localization?: CronLocalization, 8 | hideTabs?: boolean, 9 | value?: string, 10 | activeTab?: T, 11 | tabs?: T[], 12 | disabled?: boolean, 13 | renderYearsFrom?: number, 14 | renderYearsTo?: number, 15 | onChange?: (cronValue: string) => void, 16 | onTabChange?: (tab: T) => void 17 | } & CronBaseProps; 18 | -------------------------------------------------------------------------------- /src/lib/cron.ts: -------------------------------------------------------------------------------- 1 | export { ReQuartzCron as ReCron } from './quartz'; 2 | -------------------------------------------------------------------------------- /src/lib/helpers.ts: -------------------------------------------------------------------------------- 1 | import { Mode } from '@sbzen/cron-core'; 2 | 3 | import { DeepPartial, localization, CronLocalization } from './cron-localization'; 4 | 5 | export const genSessionId = () => `${Date.now()}_${Math.random()}`; 6 | 7 | export const genClassName = (cssClassPrefix = '', classes: string[], noPrefixClasses: string[] = []) => { 8 | const prefixed = classes.filter(c => !!c).map(c => cssClassPrefix + c); 9 | return prefixed.concat(noPrefixClasses).join(' '); 10 | }; 11 | 12 | export const genId = (mode: Mode, session: string, extra?: string) => { 13 | return `${mode}-${extra || ''}${session}`; 14 | }; 15 | 16 | export const getCssClassPrefix = (cssClassPrefix?: string) => cssClassPrefix || ''; 17 | 18 | export const localizeList = (list: { value: string, label: string }[], localizationStore: { [key: string]: string }) => { 19 | return list.map(v => ({ 20 | ...v, 21 | label: localizeLabel(v.label, localizationStore) 22 | })); 23 | } 24 | 25 | export const localizeLabel = (label: string, localizationStore: { [key: string]: string }) => { 26 | return localizationStore[label.toLowerCase()] 27 | } 28 | 29 | export const getLocalization = (input?: CronLocalization) => { 30 | const args: RawObject[] = [localization]; 31 | if (input) { 32 | args.push(input); 33 | } 34 | return mergeDeep(...args); 35 | } 36 | 37 | type RawObject = DeepPartial<{ 38 | [key: string]: string|RawObject; 39 | }>; 40 | const mergeDeep = (...objects: RawObject[]) => { 41 | return objects.reduce((prev, obj) => { 42 | Object.keys(obj).forEach(key => { 43 | const pVal = prev[key]; 44 | const oVal = obj[key]; 45 | 46 | if (pVal && typeof pVal === 'object' && oVal && typeof oVal === 'object') { 47 | prev[key] = mergeDeep(pVal, oVal); 48 | } else { 49 | prev[key] = oVal; 50 | } 51 | }); 52 | 53 | return prev; 54 | }, {}) as T; 55 | } 56 | -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export { Type as Tab } from '@sbzen/cron-core'; 2 | export { CronLocalization } from './cron-localization'; 3 | export * from './cron'; 4 | export * from './unix'; 5 | export * from './quartz'; 6 | -------------------------------------------------------------------------------- /src/lib/quartz/index.ts: -------------------------------------------------------------------------------- 1 | export * from './quartz'; -------------------------------------------------------------------------------- /src/lib/quartz/quartz.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { 3 | CronQuartzUIService, 4 | QuartzType, 5 | Type, 6 | getSegmentsList, 7 | getTypeSegments, 8 | getQuartzTypes 9 | } from '@sbzen/cron-core'; 10 | 11 | import { getLocalization, genSessionId, genClassName } from './../helpers'; 12 | import { CronHostProps } from './../cron-props.type'; 13 | import { 14 | QuartzCronSecond, 15 | QuartzCronMinute, 16 | QuartzCronHour, 17 | QuartzCronMonth, 18 | QuartzCronYear, 19 | QuartzCronDay 20 | } from './tabs'; 21 | 22 | export type ReQuartzCronProps = CronHostProps; 23 | export const ReQuartzCron = ({ 24 | localization: propLocalization, 25 | hideTabs: propHideTabs, 26 | value = '', 27 | activeTab, 28 | tabs = getQuartzTypes(), 29 | renderYearsFrom, 30 | renderYearsTo, 31 | cssClassPrefix, 32 | disabled, 33 | onTabChange, 34 | onChange 35 | }: ReQuartzCronProps) => { 36 | const [tab, setTab] = useState(activeTab || tabs[0]); 37 | const [service] = useState(new CronQuartzUIService(renderYearsFrom)); 38 | const [renderCount, setRenderCount] = useState(0); 39 | const [session] = useState(genSessionId()); 40 | const localization = getLocalization(propLocalization); 41 | const hasTabs = !propHideTabs && !!tabs.length; 42 | const tabProps = { 43 | cssClassPrefix, 44 | localization, 45 | session, 46 | service 47 | }; 48 | const yearTabProps = { 49 | ...tabProps, 50 | renderYearsFrom, 51 | renderYearsTo 52 | }; 53 | 54 | useEffect(() => { 55 | const shouldUpdate = !!activeTab && activeTab !== tab; 56 | shouldUpdate && setTab(activeTab); 57 | }, [activeTab]); 58 | useEffect(() => () => service.destroy(), [service]); 59 | useEffect(() => listenChangas()); 60 | useEffect(() => service.fillFromExpression(value), [value]); 61 | useEffect(() => service.setDisabled(disabled), [disabled]); 62 | 63 | const listenChangas = () => { 64 | const segments = getSegmentsList(); 65 | return service.listen(segments, (_, segment) => { 66 | const shouldApply = getTypeSegments(tab).includes(segment); 67 | if (shouldApply) { 68 | applyChanges(); 69 | } 70 | }); 71 | }; 72 | 73 | const genContent = () => { 74 | if (tab === Type.SECONDS) { 75 | return ; 76 | } else if (tab === Type.MINUTES) { 77 | return ; 78 | } else if (tab === Type.HOURS) { 79 | return ; 80 | } else if (tab === Type.MONTH) { 81 | return ; 82 | } else if (tab === Type.YEAR) { 83 | return ; 84 | } else { 85 | return ; 86 | } 87 | }; 88 | 89 | const genTabs = (activeTab: QuartzType) => { 90 | const className = genClassName(cssClassPrefix, ['nav', 'nav-tabs', 'mb-2'], ['c-tabs']); 91 | return ( 92 |
    96 | 97 | {tabs.map(t => genTab(t, activeTab))} 98 |
99 | ); 100 | }; 101 | 102 | const genTab = (tab: QuartzType, activeTab: QuartzType) => { 103 | const { tabs: tabsLocalization } = localization; 104 | const isActive = activeTab === tab; 105 | const className = genClassName(cssClassPrefix, ['nav-link', isActive ? 'active': ''], [tab, 'c-tab']); 106 | const tabKey = tab.toLowerCase() as keyof typeof tabsLocalization; 107 | 108 | return ( 109 |
  • 112 | 113 | 123 |
  • 124 | ); 125 | }; 126 | 127 | const changeTab = (tab: QuartzType) => { 128 | setTab(tab); 129 | if (onTabChange) { 130 | onTabChange(tab); 131 | } 132 | }; 133 | 134 | const applyChanges = () => { 135 | const str = service.toString(); 136 | if (str !== value && onChange) { 137 | onChange(str); 138 | } 139 | setRenderCount(renderCount + 1); 140 | }; 141 | 142 | return ( 143 |
    144 | {hasTabs && genTabs(tab)} 145 | 146 |
    152 | {genContent()} 153 |
    154 |
    155 | ); 156 | }; 157 | 158 | export default ReQuartzCron; 159 | -------------------------------------------------------------------------------- /src/lib/quartz/tabs/day/day.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Segment, Mode, Type, getDaysOfWeekCodes, getList } from '@sbzen/cron-core'; 3 | 4 | import { genId, getCssClassPrefix, localizeLabel, genClassName, localizeList } from './../../../helpers'; 5 | import { SimpleRange } from './../../../shared'; 6 | import { CronQuartzTabProps } from './../shared'; 7 | 8 | export const QuartzCronDay = ({ 9 | service, 10 | session, 11 | localization, 12 | cssClassPrefix 13 | }: CronQuartzTabProps) => { 14 | const { common, quartz } = localization; 15 | const { 16 | every, 17 | dayOfWeekIncrement, 18 | dayOfMonthLastDay, 19 | dayOfWeekNTHWeekDayOfMonth, 20 | dayOfMonthNearestWeekDayOfMonth, 21 | dayOfMonthDaysBeforeEndMonth, 22 | dayOfWeekLastNTHDayWeek, 23 | dayOfMonthLastDayWeek, 24 | dayOfMonthIncrement, 25 | dayOfWeekAnd, 26 | dayOfWeekRange, 27 | dayOfMonthAnd 28 | } = quartz.day; 29 | const classPrefix = getCssClassPrefix(cssClassPrefix); 30 | const api = service.getApi(Type.DAY); 31 | const daysOfWeekEvery = getList(Segment.dayOfWeek, true); 32 | const daysOfWeek = getList(Segment.dayOfWeek); 33 | const daysOfWeekCodes = getDaysOfWeekCodes(); 34 | const daysOfMonthEvery = getList(Segment.dayOfMonth, true); 35 | const daysOfMonth = getList(Segment.dayOfMonth); 36 | const limitedDaysOfMonth = daysOfMonthEvery.slice(0, 5); 37 | 38 | const genEvery = () => ( 39 |
    40 |
    41 | api.selectEvery()} /> 49 | 50 | 56 |
    57 |
    58 | ); 59 | 60 | const genDayOfWeekIncrement = () => ( 61 |
    62 |
    63 | api.selectDayOfWeekIncrement()} /> 71 | 72 | 78 |
    79 | 80 | 96 | 97 | 102 | 103 | 119 |
    120 | ); 121 | 122 | const genDayOfMonthIncrement = () => ( 123 |
    124 |
    125 | api.selectDayOfMonthIncrement()} /> 133 | 134 | 139 |
    140 | 141 | 157 | 158 | 163 | 164 | 180 | 181 | 186 |
    187 | ); 188 | 189 | const genDayOfWeekAnd = () => ( 190 |
    191 |
    192 | api.selectDayOfWeekAnd()} /> 200 | 201 | 206 |
    207 | 208 |
    209 | {daysOfWeekCodes.map(item => { 210 | return ( 211 |
    215 | 216 |
    217 | api.selectDayOfWeekAndValue(item.value)} /> 225 | 226 | 231 |
    232 |
    233 | ); 234 | })} 235 |
    236 |
    237 | ); 238 | 239 | const getDayOfWeekRange = () => ( 240 | api.selectDayOfWeekRange()} 246 | disabledControls={api.isDayOfWeekRangeControlsDisabled()} 247 | label1={dayOfWeekRange.label1} 248 | label2={dayOfWeekRange.label2} 249 | primaryOptions={localizeList(daysOfWeekCodes, common.dayOfWeek)} 250 | primaryValue={api.getDayOfWeekRangePrimary()} 251 | onPrimaryValueChange={api.setDayOfWeekRangePrimary} 252 | secondaryOptions={localizeList(daysOfWeekCodes, common.dayOfWeek)} 253 | secondaryValue={api.getDayOfWeekRangeSecondary()} 254 | onSecondaryValueChange={api.setDayOfWeekRangeSecondary}/> 255 | ); 256 | 257 | const genDayOfMonthAnd = () => ( 258 |
    259 |
    260 | api.selectDayOfMonthAnd()} /> 268 | 269 | 274 |
    275 | 276 |
    277 | {daysOfMonth.map(item => { 278 | return ( 279 |
    283 | 284 |
    285 | api.selectDayOfMonthAndValue(item.value)} /> 293 | 294 | 299 |
    300 |
    301 | ); 302 | })} 303 |
    304 |
    305 | ); 306 | 307 | const genDayOfMonthLastDay = () => ( 308 |
    309 |
    310 | api.selectDayOfMonthLastDay()} /> 318 | 319 | 324 |
    325 |
    326 | ); 327 | 328 | const genDayOfMonthLastDayWeek = () => ( 329 |
    330 |
    331 | api.selectDayOfMonthLastDayWeek()} /> 339 | 340 | 345 |
    346 |
    347 | ); 348 | 349 | const genDayOfWeekLastNTHDayWeek = () => ( 350 |
    351 |
    352 | api.selectDayOfWeekLastNTHDayWeek()} /> 360 | 361 | 367 |
    368 | 369 | 385 | 386 | 391 |
    392 | ); 393 | 394 | const genDayOfMonthDaysBeforeEndMonth = () => ( 395 |
    396 |
    397 | api.selectDayOfMonthDaysBeforeEndMonth()} /> 405 |
    406 | 407 | 423 | 424 | 429 |
    430 | ); 431 | 432 | const genDayOfMonthNearestWeekDayOfMonth = () => ( 433 |
    434 |
    435 | api.selectDayOfMonthNearestWeekDayOfMonth()} /> 443 | 444 | 449 |
    450 | 451 | 467 | 468 | 473 |
    474 | ); 475 | 476 | const genDayOfWeekNTHWeekDayOfMonth = () => ( 477 |
    478 |
    479 | api.selectDayOfWeekNTHWeekDayOfMonth()} /> 487 | 488 | 493 |
    494 | 495 | 511 | 512 | 528 | 529 | 534 |
    535 | ); 536 | 537 | return ( 538 |
    539 | {genEvery()} 540 | {genDayOfWeekIncrement()} 541 | {genDayOfMonthIncrement()} 542 | {genDayOfWeekAnd()} 543 | {getDayOfWeekRange()} 544 | {genDayOfMonthAnd()} 545 | {genDayOfMonthLastDay()} 546 | {genDayOfMonthLastDayWeek()} 547 | {genDayOfWeekLastNTHDayWeek()} 548 | {genDayOfMonthDaysBeforeEndMonth()} 549 | {genDayOfMonthNearestWeekDayOfMonth()} 550 | {genDayOfWeekNTHWeekDayOfMonth()} 551 |
    552 | ); 553 | } 554 | -------------------------------------------------------------------------------- /src/lib/quartz/tabs/hour/hour.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Segment, Mode, Type, getList } from '@sbzen/cron-core'; 3 | 4 | import { SimpleEvery, SimpleAnd, SimpleRange } from './../../../shared'; 5 | import { genId, getCssClassPrefix } from './../../../helpers'; 6 | import { SimpleIncrement, CronQuartzTabProps } from './../shared'; 7 | 8 | export const QuartzCronHour = ({ 9 | service, 10 | session, 11 | localization, 12 | cssClassPrefix 13 | }: CronQuartzTabProps) => { 14 | const { every, increment, and, range } = localization.quartz.hour; 15 | const classPrefix = getCssClassPrefix(cssClassPrefix); 16 | const api = service.getApi(Type.HOURS); 17 | const hourCodes = getList(Segment.hours, true); 18 | const hoursList = getList(Segment.hours); 19 | 20 | const genEvery = () => ( 21 | api.selectEvery()} 27 | label={every.label}/> 28 | ); 29 | 30 | const genIncrement = () => ( 31 | api.selectIncrement()} 38 | label1={increment.label1} 39 | label2={increment.label2} 40 | primaryOptions={hourCodes} 41 | primaryValue={api.getIncrementPrimaryValue()} 42 | onPrimaryValueChange={api.setIncrementPrimaryValue} 43 | secondaryOptions={hoursList} 44 | secondaryValue={api.getIncrementSecondaryValue()} 45 | onSecondaryValueChange={api.setIncrementSecondaryValue}/> 46 | ); 47 | 48 | const genAnd = () => ( 49 | api.selectAnd()} 56 | label={and.label} 57 | onValueChange={api.selectAndValue} 58 | isValueSelected={value => api.isSelectedAndValue(value)} 59 | options={hoursList}/> 60 | ); 61 | 62 | const genRange = () => ( 63 | api.selectRange()} 69 | disabledControls={api.isRangeControlsDisabled()} 70 | label1={range.label1} 71 | label2={range.label2} 72 | primaryOptions={hoursList} 73 | primaryValue={api.getRangePrimaryValue()} 74 | onPrimaryValueChange={api.setRangePrimaryValue} 75 | secondaryOptions={hoursList} 76 | secondaryValue={api.getRangeSecondaryValue()} 77 | onSecondaryValueChange={api.setRangeSecondaryValue}/> 78 | ); 79 | 80 | return ( 81 |
    82 | {genEvery()} 83 | {genIncrement()} 84 | {genAnd()} 85 | {genRange()} 86 |
    87 | ); 88 | } 89 | -------------------------------------------------------------------------------- /src/lib/quartz/tabs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './day/day'; 2 | export * from './hour/hour'; 3 | export * from './minute/minute'; 4 | export * from './month/month'; 5 | export * from './second/second'; 6 | export * from './year/year'; -------------------------------------------------------------------------------- /src/lib/quartz/tabs/minute/minute.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Segment, Mode, Type, getList } from '@sbzen/cron-core'; 3 | 4 | import { SimpleEvery, SimpleAnd, SimpleRange } from './../../../shared'; 5 | import { genId, getCssClassPrefix } from './../../../helpers'; 6 | import { SimpleIncrement, CronQuartzTabProps } from './../shared'; 7 | 8 | export const QuartzCronMinute = ({ 9 | service, 10 | session, 11 | localization, 12 | cssClassPrefix 13 | }: CronQuartzTabProps) => { 14 | const { every, increment, and, range } = localization.quartz.minute; 15 | const classPrefix = getCssClassPrefix(cssClassPrefix); 16 | const api = service.getApi(Type.MINUTES); 17 | const minutesList = getList(Segment.minutes); 18 | const minuteCodes = getList(Segment.minutes, true); 19 | 20 | const genEvery = () => ( 21 | api.selectEvery()} 27 | label={every.label}/> 28 | ); 29 | 30 | const genIncrement = () => ( 31 | api.selectIncrement()} 38 | label1={increment.label1} 39 | label2={increment.label2} 40 | primaryOptions={minuteCodes} 41 | primaryValue={api.getIncrementPrimaryValue()} 42 | onPrimaryValueChange={api.setIncrementPrimaryValue} 43 | secondaryOptions={minutesList} 44 | secondaryValue={api.getIncrementSecondaryValue()} 45 | onSecondaryValueChange={api.setIncrementSecondaryValue}/> 46 | ); 47 | 48 | const genAnd = () => ( 49 | api.selectAnd()} 56 | label={and.label} 57 | onValueChange={api.selectAndValue} 58 | isValueSelected={value => api.isSelectedAndValue(value)} 59 | options={minutesList}/> 60 | ); 61 | 62 | const genRange = () => ( 63 | api.selectRange()} 69 | disabledControls={api.isRangeControlsDisabled()} 70 | label1={range.label1} 71 | label2={range.label2} 72 | primaryOptions={minutesList} 73 | primaryValue={api.getRangePrimaryValue()} 74 | onPrimaryValueChange={api.setRangePrimaryValue} 75 | secondaryOptions={minutesList} 76 | secondaryValue={api.getRangeSecondaryValue()} 77 | onSecondaryValueChange={api.setRangeSecondaryValue}/> 78 | ); 79 | 80 | return ( 81 |
    82 | {genEvery()} 83 | {genIncrement()} 84 | {genAnd()} 85 | {genRange()} 86 |
    87 | ); 88 | } 89 | -------------------------------------------------------------------------------- /src/lib/quartz/tabs/month/month.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Segment, Mode, Type, getMonthCodes, getList } from '@sbzen/cron-core'; 3 | 4 | import { genId, getCssClassPrefix, localizeList } from './../../../helpers'; 5 | import { SimpleEvery, SimpleAnd, SimpleRange } from './../../../shared'; 6 | import { SimpleIncrement, CronQuartzTabProps } from './../shared'; 7 | 8 | export const QuartzCronMonth = ({ 9 | service, 10 | localization, 11 | session, 12 | cssClassPrefix 13 | }: CronQuartzTabProps) => { 14 | const { common, quartz } = localization; 15 | const { every, increment, and, range } = quartz.month; 16 | const classPrefix = getCssClassPrefix(cssClassPrefix); 17 | const api = service.getApi(Type.MONTH); 18 | const monthCodes = getMonthCodes(); 19 | const monthes = getList(Segment.month); 20 | 21 | const genEvery = () => ( 22 | api.selectEvery()} 28 | label={every.label}/> 29 | ); 30 | 31 | const genIncrement = () => ( 32 | api.selectIncrement()} 39 | label1={increment.label1} 40 | label2={increment.label2} 41 | primaryOptions={monthes.map(({ value }, i) => ({ value, label: i + 1 }))} 42 | primaryValue={api.getIncrementPrimaryValue()} 43 | onPrimaryValueChange={api.setIncrementPrimaryValue} 44 | secondaryOptions={localizeList(monthes, common.month)} 45 | secondaryValue={api.getIncrementSecondaryValue()} 46 | onSecondaryValueChange={api.setIncrementSecondaryValue}/> 47 | ); 48 | 49 | const genAnd = () => ( 50 | api.selectAnd()} 58 | label={and.label} 59 | onValueChange={api.selectAndValue} 60 | isValueSelected={value => api.isSelectedAndValue(value)} 61 | options={localizeList(monthCodes, common.month)}/> 62 | ); 63 | 64 | const genRange = () => ( 65 | api.selectRange()} 71 | disabledControls={api.isRangeControlsDisabled()} 72 | label1={range.label1} 73 | label2={range.label2} 74 | primaryOptions={localizeList(monthes, common.month)} 75 | primaryValue={api.getRangePrimaryValue()} 76 | onPrimaryValueChange={api.setRangePrimaryValue} 77 | secondaryOptions={localizeList(monthes, common.month)} 78 | secondaryValue={api.getRangeSecondaryValue()} 79 | onSecondaryValueChange={api.setRangeSecondaryValue}/> 80 | ); 81 | 82 | return ( 83 |
    84 | {genEvery()} 85 | {genIncrement()} 86 | {genAnd()} 87 | {genRange()} 88 |
    89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /src/lib/quartz/tabs/second/second.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Segment, Mode, Type, getList } from '@sbzen/cron-core'; 3 | 4 | import { SimpleEvery, SimpleAnd, SimpleRange } from './../../../shared'; 5 | import { genId, getCssClassPrefix } from './../../../helpers'; 6 | import { SimpleIncrement, CronQuartzTabProps } from './../shared'; 7 | 8 | export const QuartzCronSecond = (props: CronQuartzTabProps) => { 9 | const { service, localization, session, cssClassPrefix } = props; 10 | const { every, increment, and, range } = localization.quartz.second; 11 | const classPrefix = getCssClassPrefix(cssClassPrefix); 12 | const api = service.getApi(Type.SECONDS); 13 | const secondCodes = getList(Segment.seconds, true); 14 | const secondsList = getList(Segment.seconds); 15 | 16 | const genEvery = () => ( 17 | api.selectEvery()} 23 | label={every.label}/> 24 | ); 25 | 26 | const genIncrement = () => ( 27 | api.selectIncrement()} 34 | label1={increment.label1} 35 | label2={increment.label2} 36 | primaryOptions={secondCodes} 37 | primaryValue={api.getIncrementPrimaryValue()} 38 | onPrimaryValueChange={api.setIncrementPrimaryValue} 39 | secondaryOptions={secondsList} 40 | secondaryValue={api.getIncrementSecondaryValue()} 41 | onSecondaryValueChange={api.setIncrementSecondaryValue}/> 42 | ); 43 | 44 | const genAnd = () => ( 45 | api.selectAnd()} 52 | label={and.label} 53 | onValueChange={api.selectAndValue} 54 | isValueSelected={value => api.isSelectedAndValue(value)} 55 | options={secondsList}/> 56 | ); 57 | 58 | const genRange = () => ( 59 | api.selectRange()} 65 | disabledControls={api.isRangeControlsDisabled()} 66 | label1={range.label1} 67 | label2={range.label2} 68 | primaryOptions={secondsList} 69 | primaryValue={api.getRangePrimaryValue()} 70 | onPrimaryValueChange={api.setRangePrimaryValue} 71 | secondaryOptions={secondsList} 72 | secondaryValue={api.getRangeSecondaryValue()} 73 | onSecondaryValueChange={api.setRangeSecondaryValue}/> 74 | ); 75 | 76 | return ( 77 |
    78 | {genEvery()} 79 | {genIncrement()} 80 | {genAnd()} 81 | {genRange()} 82 |
    83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /src/lib/quartz/tabs/shared/increment.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Mode } from '@sbzen/cron-core'; 3 | 4 | import { CronBaseProps } from './../../../cron-base-props.type'; 5 | import { genClassName } from '../../../helpers'; 6 | 7 | type Props = { 8 | checked?: boolean, 9 | disabled?: boolean, 10 | disabledControls?: boolean, 11 | onSelect: () => void, 12 | primaryOptions: { 13 | label: string|number, 14 | value: string 15 | }[]; 16 | primaryValue: string, 17 | onPrimaryValueChange: (value: string) => void, 18 | secondaryOptions: { 19 | label: string|number, 20 | value: string 21 | }[], 22 | secondaryValue: string, 23 | onSecondaryValueChange: (value: string) => void, 24 | label1: string, 25 | label2: string, 26 | segmentId: string 27 | } & CronBaseProps; 28 | 29 | export const SimpleIncrement = ({ 30 | cssClassPrefix = '', 31 | checked = false, 32 | disabled = false, 33 | disabledControls = false, 34 | label1, 35 | label2, 36 | onSelect, 37 | primaryOptions, 38 | primaryValue, 39 | onPrimaryValueChange, 40 | secondaryOptions, 41 | secondaryValue, 42 | onSecondaryValueChange, 43 | segmentId 44 | }: Props) => ( 45 |
    46 |
    47 | 55 | 56 | 61 |
    62 | 63 | 79 | 80 | 85 | 86 | 102 |
    103 | ); 104 | -------------------------------------------------------------------------------- /src/lib/quartz/tabs/shared/index.ts: -------------------------------------------------------------------------------- 1 | export * from './increment'; 2 | export * from './tab-props.type'; 3 | -------------------------------------------------------------------------------- /src/lib/quartz/tabs/shared/tab-props.type.ts: -------------------------------------------------------------------------------- 1 | import { CronQuartzUIService } from '@sbzen/cron-core'; 2 | 3 | import { CronBaseTabProps } from './../../../cron-base-tab-props.type'; 4 | 5 | export type CronQuartzTabProps = CronBaseTabProps; 6 | -------------------------------------------------------------------------------- /src/lib/quartz/tabs/year/year.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Segment, Mode, Type, getList } from '@sbzen/cron-core'; 3 | 4 | import { SimpleEvery, SimpleAnd, SimpleRange } from './../../../shared'; 5 | import { genId, getCssClassPrefix } from './../../../helpers'; 6 | import { SimpleIncrement, CronQuartzTabProps } from './../shared'; 7 | 8 | export type CronTabYearProps = { 9 | renderYearsFrom?: number, 10 | renderYearsTo?: number 11 | } & CronQuartzTabProps; 12 | 13 | export const QuartzCronYear = (props: CronTabYearProps) => { 14 | const { service, localization, session, cssClassPrefix, renderYearsFrom, renderYearsTo } = props; 15 | const { every, increment, and, range } = localization.quartz.year; 16 | const classPrefix = getCssClassPrefix(cssClassPrefix); 17 | const api = service.getApi(Type.YEAR); 18 | const yearCodes = getList(Segment.year, true); 19 | const years = getList(Segment.year, false, renderYearsFrom, renderYearsTo); 20 | 21 | const genEvery = () => ( 22 | api.selectEvery()} 28 | label={every.label}/> 29 | ); 30 | 31 | const genIncrement = () => ( 32 | api.selectIncrement()} 39 | label1={increment.label1} 40 | label2={increment.label2} 41 | primaryOptions={yearCodes} 42 | primaryValue={api.getIncrementPrimaryValue()} 43 | onPrimaryValueChange={api.setIncrementPrimaryValue} 44 | secondaryOptions={years} 45 | secondaryValue={api.getIncrementSecondaryValue()} 46 | onSecondaryValueChange={api.setIncrementSecondaryValue}/> 47 | ); 48 | 49 | const genAnd = () => ( 50 | api.selectAnd()} 57 | label={and.label} 58 | onValueChange={api.selectAndValue} 59 | isValueSelected={value => api.isSelectedAndValue(value)} 60 | options={years}/> 61 | ); 62 | 63 | const genRange = () => ( 64 | api.selectRange()} 70 | disabledControls={api.isRangeControlsDisabled()} 71 | label1={range.label1} 72 | label2={range.label2} 73 | primaryOptions={years} 74 | primaryValue={api.getRangePrimaryValue()} 75 | onPrimaryValueChange={api.setRangePrimaryValue} 76 | secondaryOptions={years} 77 | secondaryValue={api.getRangeSecondaryValue()} 78 | onSecondaryValueChange={api.setRangeSecondaryValue}/> 79 | ); 80 | 81 | return ( 82 |
    83 | {genEvery()} 84 | {genIncrement()} 85 | {genAnd()} 86 | {genRange()} 87 |
    88 | ); 89 | } 90 | -------------------------------------------------------------------------------- /src/lib/shared/and.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Mode } from '@sbzen/cron-core'; 3 | 4 | import { genClassName } from './../helpers'; 5 | import { SharedProps } from './props.type'; 6 | 7 | type Props = { 8 | disabledControls?: boolean; 9 | onValueChange: (value: string) => void; 10 | isValueSelected: (value: string) => boolean; 11 | options: { 12 | label: string, 13 | value: string 14 | }[], 15 | label: string; 16 | gridSize?: string[]; 17 | } & SharedProps; 18 | 19 | export const SimpleAnd = ({ 20 | cssClassPrefix = '', 21 | checked = false, 22 | disabled = false, 23 | disabledControls = false, 24 | label, 25 | options, 26 | onSelect, 27 | onValueChange, 28 | isValueSelected, 29 | gridSize = ['col-2', 'col-md-1'], 30 | segmentId 31 | }: Props) => ( 32 |
    33 |
    34 | 42 | 43 | 48 |
    49 | 50 |
    51 | {options.map(item => { 52 | return ( 53 |
    57 | 58 |
    59 | onValueChange(item.value)} /> 67 | 68 | 73 |
    74 |
    75 | ); 76 | })} 77 |
    78 |
    79 | ); 80 | -------------------------------------------------------------------------------- /src/lib/shared/every.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Mode } from '@sbzen/cron-core'; 3 | 4 | import { genClassName } from './../helpers'; 5 | import { SharedProps } from './props.type'; 6 | 7 | type Props = { 8 | label: string; 9 | } & SharedProps; 10 | 11 | export const SimpleEvery = ({ 12 | cssClassPrefix = '', 13 | checked = false, 14 | disabled = false, 15 | label, 16 | onSelect, 17 | segmentId 18 | }: Props) => ( 19 |
    20 |
    21 | 29 | 30 | 35 |
    36 |
    37 | ); 38 | -------------------------------------------------------------------------------- /src/lib/shared/index.ts: -------------------------------------------------------------------------------- 1 | export * from './and'; 2 | export * from './every'; 3 | export * from './range'; -------------------------------------------------------------------------------- /src/lib/shared/props.type.ts: -------------------------------------------------------------------------------- 1 | import { CronBaseProps } from './../cron-base-props.type'; 2 | 3 | export type SharedProps = { 4 | segmentId: string, 5 | disabled?: boolean, 6 | checked?: boolean, 7 | onSelect: () => void 8 | } & CronBaseProps; 9 | -------------------------------------------------------------------------------- /src/lib/shared/range.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Mode } from '@sbzen/cron-core'; 3 | 4 | import { genClassName } from './../helpers'; 5 | import { SharedProps } from './props.type'; 6 | 7 | type Props = { 8 | disabledControls?: boolean; 9 | primaryOptions: { 10 | label: string, 11 | value: string 12 | }[]; 13 | primaryValue: string; 14 | onPrimaryValueChange: (value: string) => void; 15 | secondaryOptions: { 16 | label: string, 17 | value: string 18 | }[]; 19 | secondaryValue: string; 20 | onSecondaryValueChange: (value: string) => void; 21 | label1: string; 22 | label2: string; 23 | } & SharedProps; 24 | 25 | export const SimpleRange = ({ 26 | cssClassPrefix = '', 27 | checked = false, 28 | disabled = false, 29 | disabledControls = false, 30 | label1, 31 | label2, 32 | onSelect, 33 | primaryOptions, 34 | primaryValue, 35 | onPrimaryValueChange, 36 | secondaryOptions, 37 | secondaryValue, 38 | onSecondaryValueChange, 39 | segmentId 40 | }: Props) => ( 41 |
    42 |
    43 | 44 | 52 | 53 | 58 |
    59 | 60 | 76 | 77 | 82 | 83 | 99 |
    100 | ); 101 | -------------------------------------------------------------------------------- /src/lib/unix/index.ts: -------------------------------------------------------------------------------- 1 | export * from './unix'; -------------------------------------------------------------------------------- /src/lib/unix/tabs/day/day.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Segment, getList, getDaysOfWeekCodes, Type, Mode } from '@sbzen/cron-core'; 3 | 4 | import { genId, getCssClassPrefix, localizeLabel, genClassName } from './../../../helpers'; 5 | import { CronUnixTabProps } from './../shared'; 6 | 7 | export const UnixCronDay = ({ 8 | service, 9 | session, 10 | localization, 11 | cssClassPrefix 12 | }: CronUnixTabProps) => { 13 | const { common, unix } = localization; 14 | const { 15 | every, 16 | dayOfWeekAnd, 17 | dayOfMonthAnd, 18 | dayOfWeekIncrement, 19 | dayOfMonthIncrement 20 | } = unix.day; 21 | const classPrefix = getCssClassPrefix(cssClassPrefix); 22 | const api = service.getApi(Type.DAY); 23 | const daysOfWeekEvery = getList(Segment.dayOfWeek, true); 24 | const daysOfWeekCodes = getDaysOfWeekCodes(); 25 | const daysOfMonth = getList(Segment.dayOfMonth); 26 | 27 | const genEvery = () => ( 28 |
    29 |
    30 | api.selectEvery()} /> 38 | 39 | 45 |
    46 |
    47 | ); 48 | 49 | const genDayOfWeekIncrement = () => ( 50 |
    51 |
    52 | api.selectDayOfWeekIncrement()} /> 60 | 61 | 67 |
    68 | 69 | 85 | 86 | 91 |
    92 | ); 93 | 94 | const genDayOfMonthIncrement = () => ( 95 |
    96 |
    97 | api.selectDayOfMonthIncrement()} /> 105 | 106 | 111 |
    112 | 113 | 129 | 130 | 135 |
    136 | ); 137 | 138 | const genDayOfWeekAnd = () => ( 139 |
    140 |
    141 | api.selectDayOfWeekAnd()} /> 149 | 150 | 155 |
    156 | 157 |
    158 | {daysOfWeekCodes.map(item => { 159 | return ( 160 |
    164 | 165 |
    166 | api.selectDayOfWeekAndValue(item.value)} /> 174 | 175 | 180 |
    181 |
    182 | ); 183 | })} 184 |
    185 |
    186 | ); 187 | 188 | const genDayOfMonthAnd = () => ( 189 |
    190 |
    191 | api.selectDayOfMonthAnd()} /> 199 | 200 | 205 |
    206 | 207 |
    208 | {daysOfMonth.map(item => { 209 | return ( 210 |
    214 | 215 |
    216 | api.selectDayOfMonthAndValue(item.value)} /> 224 | 225 | 230 |
    231 |
    232 | ); 233 | })} 234 |
    235 |
    236 | ); 237 | 238 | return ( 239 |
    240 | {genEvery()} 241 | {genDayOfWeekIncrement()} 242 | {genDayOfMonthIncrement()} 243 | {genDayOfWeekAnd()} 244 | {genDayOfMonthAnd()} 245 |
    246 | ); 247 | }; 248 | -------------------------------------------------------------------------------- /src/lib/unix/tabs/hour/hour.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Segment, getList, Mode, Type } from '@sbzen/cron-core'; 3 | 4 | import { SimpleEvery, SimpleAnd, SimpleRange } from './../../../shared'; 5 | import { genId, getCssClassPrefix } from './../../../helpers'; 6 | import { SimpleIncrement, CronUnixTabProps } from './../shared'; 7 | 8 | export const UnixCronHour = ({ 9 | service, 10 | session, 11 | localization, 12 | cssClassPrefix 13 | }: CronUnixTabProps) => { 14 | const { every, increment, and, range } = localization.unix.hour; 15 | const classPrefix = getCssClassPrefix(cssClassPrefix); 16 | const api = service.getApi(Type.HOURS); 17 | const hourCodes = getList(Segment.hours, true); 18 | const hoursList = getList(Segment.hours); 19 | 20 | const genEvery = () => ( 21 | api.selectEvery()} 27 | label={every.label}/> 28 | ); 29 | 30 | const genIncrement = () => ( 31 | api.selectIncrement()} 38 | label1={increment.label1} 39 | label2={increment.label2} 40 | primaryOptions={hourCodes} 41 | primaryValue={api.getIncrementPrimaryValue()} 42 | onPrimaryValueChange={api.setIncrementPrimaryValue}/> 43 | ); 44 | 45 | const genAnd = () => ( 46 | api.selectAnd()} 53 | label={and.label} 54 | onValueChange={api.selectAndValue} 55 | isValueSelected={value => api.isSelectedAndValue(value)} 56 | options={hoursList}/> 57 | ); 58 | 59 | const genRange = () => ( 60 | api.selectRange()} 66 | disabledControls={api.isRangeControlsDisabled()} 67 | label1={range.label1} 68 | label2={range.label2} 69 | primaryOptions={hoursList} 70 | primaryValue={api.getRangePrimaryValue()} 71 | onPrimaryValueChange={api.setRangePrimaryValue} 72 | secondaryOptions={hoursList} 73 | secondaryValue={api.getRangeSecondaryValue()} 74 | onSecondaryValueChange={api.setRangeSecondaryValue}/> 75 | ); 76 | 77 | return ( 78 |
    79 | {genEvery()} 80 | {genIncrement()} 81 | {genAnd()} 82 | {genRange()} 83 |
    84 | ); 85 | }; 86 | -------------------------------------------------------------------------------- /src/lib/unix/tabs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './day/day'; 2 | export * from './hour/hour'; 3 | export * from './minute/minute'; 4 | export * from './month/month'; -------------------------------------------------------------------------------- /src/lib/unix/tabs/minute/minute.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Segment, Mode, Type, getList } from '@sbzen/cron-core'; 3 | 4 | import { SimpleEvery, SimpleAnd, SimpleRange } from './../../../shared'; 5 | import { genId, getCssClassPrefix } from './../../../helpers'; 6 | import { SimpleIncrement, CronUnixTabProps } from './../shared'; 7 | 8 | export const UnixCronMinute = ({ 9 | service, 10 | session, 11 | localization, 12 | cssClassPrefix 13 | }: CronUnixTabProps) => { 14 | const { every, increment, and, range } = localization.unix.minute; 15 | const classPrefix = getCssClassPrefix(cssClassPrefix); 16 | const api = service.getApi(Type.MINUTES); 17 | const minuteCodes = getList(Segment.minutes, true); 18 | const minutesList = getList(Segment.minutes); 19 | 20 | const genEvery = () => ( 21 | api.selectEvery()} 27 | label={every.label}/> 28 | ); 29 | 30 | const genIncrement = () => ( 31 | api.selectIncrement()} 38 | label1={increment.label1} 39 | label2={increment.label2} 40 | primaryOptions={minuteCodes} 41 | primaryValue={api.getIncrementPrimaryValue()} 42 | onPrimaryValueChange={api.setIncrementPrimaryValue}/> 43 | ); 44 | 45 | const genAnd = () => ( 46 | api.selectAnd()} 53 | label={and.label} 54 | onValueChange={api.selectAndValue} 55 | isValueSelected={value => api.isSelectedAndValue(value)} 56 | options={minutesList}/> 57 | ); 58 | 59 | const genRange = () => ( 60 | api.selectRange()} 66 | disabledControls={api.isRangeControlsDisabled()} 67 | label1={range.label1} 68 | label2={range.label2} 69 | primaryOptions={minutesList} 70 | primaryValue={api.getRangePrimaryValue()} 71 | onPrimaryValueChange={api.setRangePrimaryValue} 72 | secondaryOptions={minutesList} 73 | secondaryValue={api.getRangeSecondaryValue()} 74 | onSecondaryValueChange={api.setRangeSecondaryValue}/> 75 | ); 76 | 77 | return ( 78 |
    79 | {genEvery()} 80 | {genIncrement()} 81 | {genAnd()} 82 | {genRange()} 83 |
    84 | ); 85 | }; 86 | -------------------------------------------------------------------------------- /src/lib/unix/tabs/month/month.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Segment, Mode, Type, getMonthCodes, getList } from '@sbzen/cron-core'; 3 | 4 | import { genId, getCssClassPrefix, localizeList } from './../../../helpers'; 5 | import { SimpleEvery, SimpleAnd, SimpleRange } from './../../../shared'; 6 | import { SimpleIncrement, CronUnixTabProps } from './../shared'; 7 | 8 | export const UnixCronMonth = ({ 9 | service, 10 | localization, 11 | session, 12 | cssClassPrefix 13 | }: CronUnixTabProps) => { 14 | const { common, unix } = localization; 15 | const { every, increment, and, range } = unix.month; 16 | const classPrefix = getCssClassPrefix(cssClassPrefix); 17 | const api = service.getApi(Type.MONTH); 18 | const monthCodes = getMonthCodes(); 19 | const monthes = getList(Segment.month); 20 | 21 | const genEvery = () => ( 22 | api.selectEvery()} 28 | label={every.label}/> 29 | ); 30 | 31 | const genIncrement = () => ( 32 | api.selectIncrement()} 39 | label1={increment.label1} 40 | label2={increment.label2} 41 | primaryOptions={monthes.map(({ value }, i) => ({ value, label: i + 1 }))} 42 | primaryValue={api.getIncrementPrimaryValue()} 43 | onPrimaryValueChange={api.setIncrementPrimaryValue}/> 44 | ); 45 | 46 | const genAnd = () => ( 47 | api.selectAnd()} 55 | label={and.label} 56 | onValueChange={api.selectAndValue} 57 | isValueSelected={value => api.isSelectedAndValue(value)} 58 | options={localizeList(monthCodes, common.month)}/> 59 | ); 60 | 61 | const genRange = () => ( 62 | api.selectRange()} 68 | disabledControls={api.isRangeControlsDisabled()} 69 | label1={range.label1} 70 | label2={range.label2} 71 | primaryOptions={localizeList(monthes, common.month)} 72 | primaryValue={api.getRangePrimaryValue()} 73 | onPrimaryValueChange={api.setRangePrimaryValue} 74 | secondaryOptions={localizeList(monthes, common.month)} 75 | secondaryValue={api.getRangeSecondaryValue()} 76 | onSecondaryValueChange={api.setRangeSecondaryValue}/> 77 | ); 78 | 79 | return ( 80 |
    81 | {genEvery()} 82 | {genIncrement()} 83 | {genAnd()} 84 | {genRange()} 85 |
    86 | ); 87 | }; 88 | -------------------------------------------------------------------------------- /src/lib/unix/tabs/shared/increment.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Mode } from '@sbzen/cron-core'; 3 | 4 | import { CronBaseProps } from './../../../cron-base-props.type'; 5 | import { genClassName } from './../../../helpers'; 6 | 7 | type Props = { 8 | checked?: boolean; 9 | disabled?: boolean; 10 | disabledControls?: boolean; 11 | onSelect: () => void; 12 | primaryOptions: { 13 | label: string|number, 14 | value: string 15 | }[]; 16 | primaryValue: string; 17 | onPrimaryValueChange: (value: string) => void; 18 | label1: string; 19 | label2: string; 20 | segmentId: string; 21 | } & CronBaseProps; 22 | 23 | export const SimpleIncrement = ({ 24 | cssClassPrefix = '', 25 | checked = false, 26 | disabled = false, 27 | disabledControls = false, 28 | label1, 29 | label2, 30 | onSelect, 31 | primaryOptions, 32 | primaryValue, 33 | onPrimaryValueChange, 34 | segmentId 35 | }: Props) => ( 36 |
    37 |
    38 | 46 | 47 | 52 |
    53 | 54 | 70 | 71 | 76 |
    77 | ); 78 | -------------------------------------------------------------------------------- /src/lib/unix/tabs/shared/index.ts: -------------------------------------------------------------------------------- 1 | export * from './increment'; 2 | export * from './tab-props.type'; 3 | -------------------------------------------------------------------------------- /src/lib/unix/tabs/shared/tab-props.type.ts: -------------------------------------------------------------------------------- 1 | import { CronUnixUIService } from '@sbzen/cron-core'; 2 | 3 | import { CronBaseTabProps } from './../../../cron-base-tab-props.type'; 4 | 5 | export type CronUnixTabProps = CronBaseTabProps; 6 | -------------------------------------------------------------------------------- /src/lib/unix/unix.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { 3 | CronUnixUIService, 4 | UnixType, 5 | Type, 6 | getSegmentsList, 7 | getTypeSegments, 8 | getUnixTypes 9 | } from '@sbzen/cron-core'; 10 | 11 | import { getLocalization, genSessionId, genClassName } from './../helpers'; 12 | import { CronHostProps } from './../cron-props.type'; 13 | import { 14 | UnixCronMinute, 15 | UnixCronHour, 16 | UnixCronMonth, 17 | UnixCronDay 18 | } from './tabs'; 19 | 20 | export type ReUnixCronProps = CronHostProps; 21 | 22 | export const ReUnixCron = ({ 23 | localization: propLocalization, 24 | hideTabs: propHideTabs, 25 | tabs: propTabs, 26 | value = '', 27 | cssClassPrefix, 28 | activeTab, 29 | disabled, 30 | onTabChange, 31 | onChange 32 | }: ReUnixCronProps) => { 33 | const tabs = (propTabs || getUnixTypes()).filter(t => ![ 34 | Type.SECONDS, 35 | Type.YEAR 36 | ].includes(t)); 37 | const [tab, setTab] = useState(activeTab || tabs[0]); 38 | const [service] = useState(new CronUnixUIService()); 39 | const [renderCount, setRenderCount] = useState(0); 40 | const [session] = useState(genSessionId()); 41 | const localization = getLocalization(propLocalization); 42 | const hasTabs = !propHideTabs && !!tabs.length; 43 | const tabProps = { 44 | cssClassPrefix, 45 | localization, 46 | session, 47 | service 48 | }; 49 | 50 | useEffect(() => { 51 | const shouldUpdate = !!activeTab && activeTab !== tab; 52 | shouldUpdate && setTab(activeTab); 53 | }, [activeTab]); 54 | useEffect(() => () => service.destroy(), [service]); 55 | useEffect(() => listenChangas()); 56 | useEffect(() => service.fillFromExpression(value), [value]); 57 | useEffect(() => service.setDisabled(disabled), [disabled]); 58 | 59 | const listenChangas = () => { 60 | const segments = getSegmentsList(); 61 | return service.listen(segments, (_, segment) => { 62 | const shouldApply = getTypeSegments(tab).includes(segment); 63 | if (shouldApply) { 64 | applyChanges(); 65 | } 66 | }); 67 | }; 68 | 69 | const genContent = () => { 70 | if (tab === Type.MINUTES) { 71 | return ; 72 | } else if (tab === Type.HOURS) { 73 | return ; 74 | } else if (tab === Type.MONTH) { 75 | return ; 76 | } else { 77 | return ; 78 | } 79 | }; 80 | 81 | const genTabs = (activeTab: UnixType) => { 82 | const className = genClassName(cssClassPrefix, ['nav', 'nav-tabs', 'mb-2'], ['c-tabs']); 83 | return ( 84 |
      88 | 89 | {tabs.map(t => genTab(t, activeTab))} 90 |
    91 | ); 92 | }; 93 | 94 | const genTab = (tab: UnixType, activeTab: UnixType) => { 95 | const { tabs: tabsLocalization } = localization; 96 | const isActive = activeTab === tab; 97 | const className = genClassName(cssClassPrefix, ['nav-link', isActive ? 'active': ''], [tab, 'c-tab']); 98 | const tabKey = tab.toLowerCase() as keyof typeof tabsLocalization; 99 | 100 | return ( 101 |
  • 104 | 105 | 115 |
  • 116 | ); 117 | }; 118 | 119 | const changeTab = (tab: UnixType) => { 120 | setTab(tab); 121 | if (onTabChange) { 122 | onTabChange(tab); 123 | } 124 | }; 125 | 126 | const applyChanges = () => { 127 | const str = service.toString(); 128 | if (str !== value && onChange) { 129 | onChange(str); 130 | } 131 | setRenderCount(renderCount + 1); 132 | }; 133 | 134 | return ( 135 |
    136 | {hasTabs && genTabs(tab)} 137 | 138 |
    144 | {genContent()} 145 |
    146 |
    147 | ); 148 | }; 149 | 150 | export default ReUnixCron; 151 | -------------------------------------------------------------------------------- /src/stories/Introduction.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/addon-docs'; 2 | import Code from './assets/code-brackets.svg'; 3 | import Colors from './assets/colors.svg'; 4 | import Comments from './assets/comments.svg'; 5 | import Direction from './assets/direction.svg'; 6 | import Flow from './assets/flow.svg'; 7 | import Plugin from './assets/plugin.svg'; 8 | import Repo from './assets/repo.svg'; 9 | import StackAlt from './assets/stackalt.svg'; 10 | 11 | 12 | 13 | 116 | 117 | # Welcome to Storybook 118 | 119 | Storybook helps you build UI components in isolation from your app's business logic, data, and context. 120 | That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA. 121 | 122 | Browse example stories now by navigating to them in the sidebar. 123 | View their code in the `stories` directory to learn how they work. 124 | We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages. 125 | 126 |
    Configure
    127 | 128 | 174 | 175 |
    Learn
    176 | 177 | 207 | 208 |
    209 | TipEdit the Markdown in{' '} 210 | stories/Introduction.stories.mdx 211 |
    212 | -------------------------------------------------------------------------------- /src/stories/assets/code-brackets.svg: -------------------------------------------------------------------------------- 1 | illustration/code-brackets -------------------------------------------------------------------------------- /src/stories/assets/colors.svg: -------------------------------------------------------------------------------- 1 | illustration/colors -------------------------------------------------------------------------------- /src/stories/assets/comments.svg: -------------------------------------------------------------------------------- 1 | illustration/comments -------------------------------------------------------------------------------- /src/stories/assets/direction.svg: -------------------------------------------------------------------------------- 1 | illustration/direction -------------------------------------------------------------------------------- /src/stories/assets/flow.svg: -------------------------------------------------------------------------------- 1 | illustration/flow -------------------------------------------------------------------------------- /src/stories/assets/plugin.svg: -------------------------------------------------------------------------------- 1 | illustration/plugin -------------------------------------------------------------------------------- /src/stories/assets/repo.svg: -------------------------------------------------------------------------------- 1 | illustration/repo -------------------------------------------------------------------------------- /src/stories/assets/stackalt.svg: -------------------------------------------------------------------------------- 1 | illustration/stackalt -------------------------------------------------------------------------------- /src/stories/quartz-cron.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, Fragment } from 'react'; 2 | import { Story, ComponentMeta } from '@storybook/react'; 3 | 4 | import { ReQuartzCron, ReQuartzCronProps, Tab } from './../lib'; 5 | 6 | const Wrapper = (args: ReQuartzCronProps) => { 7 | const [value, setValue] = useState(args.value); 8 | 9 | useEffect(() => setValue(args.value), [args.value]) 10 | 11 | return ( 12 | 13 | 14 | 15 | 16 | ); 17 | }; 18 | 19 | export default { 20 | title: 'ReQuartzCron', 21 | component: ReQuartzCron, 22 | argTypes: { 23 | tabs: { 24 | control: 'inline-check', 25 | options: [ 26 | Tab.SECONDS, 27 | Tab.MINUTES, 28 | Tab.HOURS, 29 | Tab.MONTH, 30 | Tab.DAY, 31 | Tab.YEAR 32 | ] 33 | }, 34 | activeTab: { 35 | control: 'inline-radio', 36 | options: [ 37 | Tab.SECONDS, 38 | Tab.MINUTES, 39 | Tab.HOURS, 40 | Tab.MONTH, 41 | Tab.DAY, 42 | Tab.YEAR 43 | ] 44 | } 45 | } 46 | } as ComponentMeta; 47 | 48 | const Template: Story = args => ; 49 | export const Default = Template.bind({}); 50 | Default.args = { 51 | value: '* * * * * * *' 52 | }; 53 | -------------------------------------------------------------------------------- /src/stories/unix-cron.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, Fragment } from 'react'; 2 | import { Story, ComponentMeta } from '@storybook/react'; 3 | 4 | import { ReUnixCron, ReUnixCronProps, Tab } from './../lib'; 5 | 6 | const Wrapper = (args: ReUnixCronProps) => { 7 | const [value, setValue] = useState(args.value); 8 | 9 | useEffect(() => setValue(args.value), [args.value]) 10 | 11 | return ( 12 | 13 | 14 | 15 | 16 | ); 17 | }; 18 | 19 | export default { 20 | title: 'ReUnixCron', 21 | component: ReUnixCron, 22 | argTypes: { 23 | tabs: { 24 | control: 'inline-check', 25 | options: [ 26 | Tab.MINUTES, 27 | Tab.HOURS, 28 | Tab.MONTH, 29 | Tab.DAY 30 | ] 31 | }, 32 | activeTab: { 33 | control: 'inline-radio', 34 | options: [ 35 | Tab.MINUTES, 36 | Tab.HOURS, 37 | Tab.MONTH, 38 | Tab.DAY 39 | ] 40 | } 41 | } 42 | } as ComponentMeta; 43 | 44 | const Template: Story = args => ; 45 | export const Default = Template.bind({}); 46 | Default.args = { 47 | value: '0 0 1 2,4 *' 48 | }; 49 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": false, 4 | "declaration": true, 5 | "moduleResolution": "node", 6 | "baseUrl": "./src/lib", 7 | "target": "es2015", 8 | "module": "esnext", 9 | "lib": ["es2017", "dom"], 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "strict": true, 15 | "jsx": "react", 16 | "noImplicitOverride": true, 17 | "noPropertyAccessFromIndexSignature": true, 18 | "noImplicitReturns": true, 19 | "noFallthroughCasesInSwitch": true, 20 | "paths": { 21 | "@sbzen/re-cron": ["./dist"] 22 | } 23 | }, 24 | "exclude": ["node_modules", "dist"] 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": [ 7 | "src/lib/**/*.ts", 8 | "src/lib/**/*.tsx" 9 | ] 10 | } 11 | --------------------------------------------------------------------------------