Compare commits

...

146 Commits

Author SHA1 Message Date
c1b2fb3cff bump: BETA 23
Signed-off-by: Florian BOUILLON <f.bouillon@aptatio.com>
2023-07-12 14:56:10 +02:00
a6c539918a fix: strict input not sending infos
Signed-off-by: Florian BOUILLON <f.bouillon@aptatio.com>
2023-07-12 14:55:58 +02:00
f76d0ab344 bump: version
Signed-off-by: Florian BOUILLON <f.bouillon@aptatio.com>
2023-06-26 11:16:33 +02:00
c7b554b088 fix: value not being updated
Signed-off-by: Florian BOUILLON <f.bouillon@aptatio.com>
2023-06-26 11:13:45 +02:00
33201a7d8c bump beta
Signed-off-by: Florian BOUILLON <f.bouillon@aptatio.com>
2023-06-23 11:19:28 +02:00
4066de1bb0 feat: handle number input better
Signed-off-by: Florian BOUILLON <f.bouillon@aptatio.com>
2023-06-23 11:14:24 +02:00
d5ba10eb31 bump: BETA 20
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-03-28 14:07:21 +02:00
589181ca48 fix: box background being too dark
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-03-28 14:06:32 +02:00
fc41cc40b7 bump: beta 19
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-03-28 12:36:58 +02:00
4617c3e228 fix: Col not respecting gap with static values
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-03-28 12:36:05 +02:00
edc82da864 bump: BETA 18
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-02-27 12:06:48 +01:00
40a5a2d9ac fix: button being decalled
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-02-27 12:06:29 +01:00
f197e92270 bump: beta 17
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-02-27 12:03:41 +01:00
fef3ebf9a0 feat: Add padding to input svgs so they are more clickable
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-02-27 12:01:34 +01:00
40c5a23035 bmup: BETA 16
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-02-20 10:47:20 +01:00
acc4230cb6 fix: Add pre-wrap to Text component so that elements with \n wrap
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-02-20 10:45:36 +01:00
eaf509262d bump: version
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-02-07 13:10:50 +01:00
e65c7e4a1a fix: PureComponent shit
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-02-07 13:08:11 +01:00
c1b9be4b74 bump: BETA 14
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-01-30 15:36:20 +01:00
2d8076e774 fix: Moved to using gap css instead of hacking
Signed-off-by: Avior <f.bouillon@aptatio.com>
2023-01-30 15:33:33 +01:00
caa646b6d2 bump version
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-12-12 12:32:51 +01:00
29654e38c8 fix: Add name to logo for accessibility
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-12-12 12:32:18 +01:00
bf8b06b7d3 bump: BETA-12
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-12-09 10:16:23 +01:00
5540647c04 fix: Add normal variant of colors for easyness of usage in projects
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-12-09 10:12:30 +01:00
d985ffc1c1 feat: Started moving texts to the new CSS variable ones
Signed-off-by: Avior <github@avior.me>
2022-11-26 14:37:17 +01:00
66ee86cfd2 bump: BETA 10
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-11-21 16:40:13 +01:00
931062a3e7 feat: Add parent elements to Col component
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-11-21 16:39:12 +01:00
e105399e15 bump
Signed-off-by: Avior <github@avior.me>
2022-10-24 00:17:08 +02:00
5601a40f1d feat: Customize range slider
Signed-off-by: Avior <github@avior.me>
2022-10-23 18:11:37 +02:00
cdedcddf4e bump: BETA 8
Signed-off-by: Avior <github@avior.me>
2022-10-23 16:31:16 +02:00
197617e2d5 fix: Fullscreen bein weird
Signed-off-by: Avior <github@avior.me>
2022-10-23 16:30:49 +02:00
2b75a3a68b bump: BETA 7
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-10-21 11:27:16 +02:00
752611414c fix: Allow to havem obile only links for navbar, menu not displaying
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-10-21 11:26:12 +02:00
bf4ceeb469 bump: BETA 6
Signed-off-by: Avior <github@avior.me>
2022-10-19 00:28:26 +02:00
128dbe3505 fix: Button not accepting custom classes
Signed-off-by: Avior <github@avior.me>
2022-10-19 00:27:59 +02:00
2646afae23 feat: Add back scrollbar
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-10-18 15:02:00 +02:00
41c01d8af5 bump BETA 3
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-10-18 13:26:17 +02:00
8a360e122c bump: BETA 3
Signed-off-by: Avior <github@avior.me>
2022-10-16 13:00:02 +02:00
14f36f0bbb fix: menu closing when clicking on it
Signed-off-by: Avior <github@avior.me>
2022-10-16 12:59:54 +02:00
6d7fc01517 bump: BETA 2
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-10-10 11:21:23 +02:00
43b6d64bfe fix: Missing color on Popup
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-10-10 11:19:37 +02:00
f213ec4f09 feat: add left childrens to navbar
Signed-off-by: Avior <github@avior.me>
2022-10-08 13:21:51 +02:00
2bb4e9830e bump: BETA 1
Signed-off-by: Avior <github@avior.me>
2022-10-04 00:55:26 +02:00
c47a02c529 feat: Remove post install compilation
Signed-off-by: Avior <github@avior.me>
2022-10-04 00:51:39 +02:00
6f8c627e33 feat: Allow to have a base theme and to have child theming in page
Signed-off-by: Avior <github@avior.me>
2022-10-03 22:53:35 +02:00
81f654e1b2 feat: Move to css variables !
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-10-03 17:50:46 +02:00
c2f6e7e055 fix: like the precedent commit
Signed-off-by: Avior <github@avior.me>
2022-10-02 18:47:35 +02:00
504f155699 fix: Build error when typing is not added correctly
Signed-off-by: Avior <github@avior.me>
2022-10-02 18:45:46 +02:00
71bcd60215 bump: Alpha 17
Signed-off-by: Avior <github@avior.me>
2022-10-02 18:43:40 +02:00
d8995ed9e0 fix: Build considering index.ts as an external deps
Signed-off-by: Avior <github@avior.me>
2022-10-02 18:37:26 +02:00
261f00805b fix: Dep not installed for custom build
Signed-off-by: Avior <github@avior.me>
2022-10-02 17:04:22 +02:00
36046592fa fix: choice not respected on startup
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-28 17:11:21 +02:00
4f16367039 bump: Alpha 16
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-28 15:50:49 +02:00
fb72d5a040 fix: if this.props.value is not set onValue event is launched
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-28 15:48:55 +02:00
a3f9cdf90c bump: Alpha 15
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-28 14:47:36 +02:00
cd2304de17 fix: Props event not updating internal value
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-28 14:47:15 +02:00
c92dc2d736 bump: version
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-28 14:39:38 +02:00
62008de7b0 fix: onValueEvent not sending with value
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-28 14:39:15 +02:00
e0b6743cd3 bump: Alpha 13
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-28 14:16:59 +02:00
644cb5b1aa fix: choices not having choices on start
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-28 14:16:01 +02:00
8d6e256fb1 bump: version 1.0.0-alpha.12
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-27 15:57:19 +02:00
fec1c9e7e4 fix: Build including NextJS
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-27 15:56:51 +02:00
b43329caa1 feat: Moved to vite
Signed-off-by: Avior <f.bouillon@aptatio.com>
2022-09-27 12:56:56 +02:00
895716cff0 fix: Upgrade deps
Signed-off-by: Avior <github@avior.me>
2022-09-12 21:59:06 +02:00
407ccca8e9 fix: Allow to disable automatic icons
Signed-off-by: Avior <github@avior.me>
2022-05-13 12:11:57 +02:00
6f1ab9665e bump: Alpha 10
Signed-off-by: Avior <github@avior.me>
2022-05-13 12:04:07 +02:00
71ec45c806 fix: Circular dependencies
Signed-off-by: Avior <github@avior.me>
2022-05-13 12:03:58 +02:00
98cfcf2b95 fix: Multiple inputs bugs
Signed-off-by: Avior <github@avior.me>
2022-05-13 12:01:56 +02:00
5e6d88c27e bump: 1.0.0-alpha.9
Signed-off-by: Avior <github@avior.me>
2022-05-10 11:27:58 +02:00
4d1b649a63 fix: Add back Fieldset
Signed-off-by: Avior <github@avior.me>
2022-05-10 11:26:59 +02:00
beaa9b13aa misc: bump version to alpha 8
Signed-off-by: Avior <github@avior.me>
2022-04-05 11:56:01 +02:00
aa4e7100fe misc: Cleanup
Signed-off-by: Avior <github@avior.me>
2022-04-05 11:53:00 +02:00
b5f44113b5 fix: Force Popup to have 0 margin
Signed-off-by: Avior <github@avior.me>
2022-04-05 11:52:42 +02:00
47399da4e0 feat: Add sidePadding parameter to BoxHeader
Signed-off-by: Avior <github@avior.me>
2022-04-05 11:52:15 +02:00
45616b1e92 misc: Add DebugCol to exports
Signed-off-by: Avior <github@avior.me>
2022-04-05 11:51:30 +02:00
6a6ab8d5fa misc: Update deps
Signed-off-by: Avior <github@avior.me>
2022-04-05 11:30:22 +02:00
fdb217b168 BUMP 1.0.0-alpha.7
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-12-12 14:56:17 +01:00
d5b438cd45 Fix last Sidebar bugs
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-12-12 14:53:11 +01:00
c35084fb9d Bump deps
Signed-off-by: Avior <github@avior.me>
2021-12-09 12:36:12 +01:00
32ea3b958f Added three new components
Signed-off-by: Avior <github@avior.me>
2021-12-09 12:17:11 +01:00
496bafa0e4 Fixed Plus/Minus Sign being missplaced
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-12-02 01:01:17 +01:00
30d507c227 Bump package to 1.0.0-alpha.6
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-12-02 00:55:16 +01:00
a97d849adc Fixed missing Sidebar export
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-12-02 00:54:46 +01:00
4ad8e7596b Bump to 1.0.0-alpha.5
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-12-02 00:38:17 +01:00
3495142453 new Fixes
- Box Header should be bold
- Buttons Hover/Active forms not correct
- Fixed one of the old Checkbox bug
- Footer Made with should be bold
- Sidebar fixes

Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-12-02 00:07:27 +01:00
f71223b1dc bump version
Signed-off-by: Avior <github@avior.me>
2021-10-07 12:38:32 +02:00
926d065602 Update
Signed-off-by: Avior <github@avior.me>
2021-10-07 12:38:01 +02:00
8d7a8c70f0 Updated
Signed-off-by: Avior <github@avior.me>
2021-10-06 17:57:59 +02:00
bb001148a5 Updated text
Signed-off-by: Avior <github@avior.me>
2021-10-01 16:35:14 +02:00
91674f6f6d Update Text to support more typography
Signed-off-by: Avior <github@avior.me>
2021-10-01 15:41:46 +02:00
bc10816467 bump
Signed-off-by: Avior <github@avior.me>
2021-09-30 10:09:56 +02:00
b5f80b08ba Added workflows
Signed-off-by: Avior <github@avior.me>
2021-09-30 10:09:31 +02:00
cd6502d9f5 Updated components
Signed-off-by: Avior <github@avior.me>
2021-09-30 10:09:02 +02:00
b34c3c800a Updated support for webpack 5
Signed-off-by: Avior <github@avior.me>
2021-09-30 10:08:29 +02:00
2ffd6dd204 bump
Signed-off-by: Avior <github@avior.me>
2021-09-27 16:34:18 +02:00
784b0807fa Added separator for container items
Signed-off-by: Avior <github@avior.me>
2021-09-27 16:33:55 +02:00
0eac63f88d bump version
Signed-off-by: Avior <github@avior.me>
2021-09-27 15:51:55 +02:00
79355a95f6 0.12.0-0 2021-09-27 15:48:44 +02:00
b403a94a2e Update
Signed-off-by: Avior <github@avior.me>
2021-09-27 15:48:04 +02:00
9ab56544d9 Update
Signed-off-by: Avior <github@avior.me>
2021-09-24 17:30:58 +02:00
96cd64151f Update
Signed-off-by: Avior <github@avior.me>
2021-09-24 17:30:38 +02:00
8b0c2f7ef1 Update to v1-alpha
Signed-off-by: Avior <github@avior.me>
2021-09-23 18:23:20 +02:00
000de60c42 0.11.3 2021-09-05 14:06:02 +02:00
60b7e187fe Readonly select will be basic readonly inputs
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-09-05 14:03:58 +02:00
1d0108ac80 Fixed button not sending to external link
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-09-05 14:02:46 +02:00
a704e43fbf 0.11.2 2021-07-12 23:39:34 +02:00
df06831c56 Updated GradientBackground to make child 100vh
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-07-12 23:39:15 +02:00
52df94ed25 0.11.1 2021-07-05 13:35:29 +02:00
ba0e70053b Fixed loader
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-07-05 13:34:04 +02:00
11dd18d8eb v0.11.0
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-06-28 11:00:16 +02:00
e24b31df10 Moved to NPM and lucide-icons
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-06-28 10:56:58 +02:00
e38387534a Filtering is now case insensitive
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-06-10 16:58:07 +02:00
ed49e9b961 Fixed some small bugs
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-05-18 15:01:16 +02:00
be01f386d4 Added Overflow X to table element
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-05-18 11:09:58 +02:00
7e26c8d5aa v0.10.1 2021-05-18 11:00:58 +02:00
a177e239c6 Fixed button with image
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-05-18 11:00:24 +02:00
e5d8592186 Removed intensive logging
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-05-18 10:53:39 +02:00
6c69dac257 v0.10.0 2021-05-13 18:16:41 +02:00
0e85ce9722 Dasticly changed Image component
It is now WAY lighter and LESS buggy

Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-05-13 18:15:34 +02:00
48d60f60e0 v0.9.2 2021-05-02 23:19:54 +02:00
d49c194654 Updated Table to use Light/Dark themes
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-05-02 23:19:33 +02:00
6f1c366289 v0.9.1 2021-05-01 23:35:11 +02:00
8ac899b3f0 Added HideExternal to links
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-05-01 23:34:45 +02:00
7ddce9ec85 v0.9.0 2021-05-01 23:02:53 +02:00
f3597f3008 Added Socials to Footer
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-05-01 23:01:02 +02:00
fe6b552a69 add margin when multiple Text elements are next to each others (#5)
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-05-01 22:58:40 +02:00
58a48b28fd Merge branch 'master' of https://github.com/dzeiocom/components 2021-04-26 11:07:11 +02:00
5675d8f225 Updated Box to be a single component
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-22 23:08:04 +02:00
0f7999ac50 v0.8.6 2021-04-16 11:11:07 +02:00
d43e62f04a Fixed Notification being behind Sidebar
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-04-16 11:09:55 +02:00
c680b613cc Swaped Colors for invalid Inputs
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-04-15 11:34:17 +02:00
d6b59509b0 v0.8.5 2021-04-15 01:27:21 +02:00
2491f23f49 Started moving Box to a single component
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-15 01:24:48 +02:00
0de363487d Fixed Footer links not being in the middle
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-15 01:22:13 +02:00
f957500580 Add NotificationManager
a Component that manage Notifications for you

Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-15 01:21:55 +02:00
ae7dde8802 Removed Menu as it was replaced in Navbar
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-15 01:21:23 +02:00
69b74e2f03 Fixed Col missing a letter
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-15 01:20:47 +02:00
506fa8c01b Updated Button Component
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-15 01:19:21 +02:00
d06386119b v0.8.4 2021-04-14 15:52:58 +02:00
209697ecd5 Removed deprecated colors, Added missing one
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-04-14 15:51:35 +02:00
d7e2bcfbdf Updated Deps
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-04-14 15:51:11 +02:00
fe17d4687b Fixed Navbar bugs
- Links having a <a> in a <a>
- UserMenu links eing squished together

Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-04-14 15:50:51 +02:00
cfd33ab0d6 Fixed Image in Button getting squished
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-04 23:01:28 +02:00
bf7b3a0d79 Updated dark color to be darker
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-04 22:06:39 +02:00
be724ecf0d v0.8.3 2021-04-04 21:57:44 +02:00
58d1db14ae Fixed Error color not set
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-04 21:57:27 +02:00
142 changed files with 28552 additions and 14061 deletions

12
.editorconfig Normal file
View File

@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = tab
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

5
.eslintrc.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
extends: [
"./node_modules/@dzeio/config/eslint/react-typescript"
]
}

5
.gitattributes vendored
View File

@ -1 +1,4 @@
* text=lf
* text=auto eol=lf
*.png binary
*.jpg binary
*.jpeg binary

17
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,17 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
# Workflow files stored in the
# default location of `.github/workflows`
directory: "/"
schedule:
interval: "daily"

29
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,29 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Build & Check
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 16
uses: actions/setup-node@v2.4.1
with:
node-version: 16.x
- name: Install packages
run: npm ci
- name: Build components
run: npm run build

65
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,65 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

37
.github/workflows/ossar-analysis.yml vendored Normal file
View File

@ -0,0 +1,37 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow integrates a collection of open source static analysis tools
# with GitHub code scanning. For documentation, or to provide feedback, visit
# https://github.com/github/ossar-action
name: OSSAR
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
jobs:
OSSAR-Scan:
# OSSAR runs on windows-latest.
# ubuntu-latest and macos-latest support coming soon
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Run open source static analysis tools
- name: Run OSSAR
uses: github/ossar-action@v1
id: ossar
# Upload results to the Security tab
- name: Upload OSSAR results
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: ${{ steps.ossar.outputs.sarifFile }}

28
.github/workflows/publish.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: publish
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup NodeJS
uses: actions/setup-node@v3
with:
node-version: '18.x'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Publish
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

13
.gitignore vendored
View File

@ -1,12 +1,7 @@
module/
node_modules/
*.mjs
*.js
!src/dzeio/stylusUtils.js
*.d.ts
types/
!rollup.config.js
!src/stylus.d.ts
!.storybook/*.js
index.es.js
index.umd.js
style.css
yarn-error.log
*.tgz

View File

@ -1,8 +1,13 @@
.github/
.storybook/
node_modules/
*.stories.js
src/
.editorconfig
.gitattributes
.gitignore
.npmignore
yarn.lock
yarn-error.log
LICENSE
vite.config.js
package-lock.json
tsconfig.json
README.md

View File

@ -1,13 +1,17 @@
const path = require("path");
const webpack = require('webpack')
module.exports = {
"stories": [
"../src/dzeio/**/*.stories.@(ts|tsx)",
"../src/**/*.stories.tsx",
],
core: {
builder: "@storybook/builder-vite"
},
staticDirs: ["./public"],
"addons": [
"@storybook/addon-essentials"
],
reactOptions: {
strictMode: true
},
typescript: {
check: false,
checkOptions: {},
@ -16,19 +20,5 @@ module.exports = {
shouldExtractLiteralValuesFromEnum: true,
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
},
},
presets: [path.resolve(__dirname, "./next.js")],
// Allow to use Next/Image
webpackFinal: (config) => {
config.plugins.push(new webpack.DefinePlugin({
'process.env.__NEXT_IMAGE_OPTS': JSON.stringify({
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
domains: [],
path: '/',
loader: 'default',
})
}))
return config
}
}

View File

@ -1,17 +0,0 @@
// https://stackoverflow.com/a/64765638/7335674
import * as nextImage from 'next/image'
Object.defineProperty(nextImage, 'default', {
configurable: true,
value: (props) => {
return (
<div style={{display: 'inline-block', maxWidth: '100%', overflow: 'hidden', position: 'relative', boxSizing: 'border-box', margin: 0}}>
<div style={{boxSizing: 'border-box', display: 'block', maxWidth: '100%'}}>
<img {...props} alt="" aria-hidden="true" role="presentation" style={{maxWidth: '100%', display: 'block', margin: 0, border: 'none', padding: 0}} />
</div>
<img {...props} style={{position: 'absolute', inset: 0, boxSizing: 'border-box', padding: 0, border: 'none', margin: 'auto', display: 'block', width: 0, height: 0, minWidth: '100%', maxWidth: '100%', minHeight: '100%', maxHeight: '100%'}} />
</div>
)
},
})

View File

@ -1,8 +1,12 @@
import Router from 'next/router';
import Router from 'next/router'
Router.router = {
push: async () => {},
push: async (route) => {
console.log('Pushing router to', route)
},
replace: async () => {},
prefetch: () => {},
route: '/mock-route',
pathname: 'mock-path',};
asPath: '/mock-route',
pathname: 'mock-path',
}

View File

@ -1,37 +0,0 @@
module.exports = {
webpackFinal: async (baseConfig, options) => {
const { module = {} } = baseConfig;
const newConfig = {
...baseConfig,
module: {
...module,
rules: [...(module.rules || [])],
},
};
// TypeScript
newConfig.module.rules.push({
test: /\.(ts|tsx)$/,
// include: [path.resolve(__dirname, '../src/client/components')],
use: ['babel-loader', 'ts-loader']
});
newConfig.resolve.extensions.push('.ts', '.tsx');
// Stylus
newConfig.module.rules.push({
test: /\.styl$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
url: false,
importLoaders: 1,
modules: true
},
}, 'stylus-loader'],
});
newConfig.resolve.extensions.push('.styl');
return newConfig;
},
};

View File

@ -1,7 +1,7 @@
import '../src/dzeio/general.styl'
import '../src/general.styl'
import './mockNextRouter'
import './mockNextImage'
export const parameters = {
layout: 'centered'
}
layout: 'centered',
actions: { argTypesRegex: '^on.*' }
}

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" fill="#C4C4C4"/>
</svg>

After

Width:  |  Height:  |  Size: 150 B

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Florian Bouillon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
README.md Normal file
View File

@ -0,0 +1 @@
# Dzeio Components

24336
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,50 +1,40 @@
{
"name": "@dzeio/components",
"version": "0.8.2",
"version": "1.0.0-beta.23",
"license": "MIT",
"main": "./index.js",
"main": "./index.umd.js",
"module": "./index.es.js",
"types": "./types/index.d.ts",
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/preset-env": "^7.12.16",
"@babel/preset-react": "^7.12.13",
"@storybook/addon-essentials": "^6.1.14",
"@storybook/cli": "^6.1.14",
"@storybook/react": "^6.1.14",
"@types/node": "^14.14.28",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"babel-loader": "^8.2.2",
"css-loader": "^5.0.2",
"next": "^10.0.5",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-feather": "^2.0.9",
"style-loader": "^2.0.0",
"stylus": "^0.54.8",
"stylus-loader": "^4.3.3",
"ts-loader": "^8.0.17",
"typescript": "^4.2.3",
"webpack": "^4.44.2"
"@storybook/addon-essentials": "^6",
"@storybook/builder-vite": "^0.2.2",
"@storybook/cli": "^6",
"@storybook/react": "^6",
"@types/json5": "^2",
"@types/node": "^18",
"@types/react": "^18",
"@types/react-dom": "^18",
"html-webpack-plugin": "^5.5.0",
"next": "^12",
"react": "^18",
"react-dom": "^18",
"style-loader": "^3",
"stylus": "^0.59.0",
"typescript": "^4",
"vite": "^3"
},
"peerDependencies": {
"next": "^10.0.5",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-feather": "^2.0.9",
"stylus": "^0.54.8",
"typescript": "^4.2.3"
"next": ">=11.0.0",
"react": ">=17.0.0",
"react-dom": ">=17.0.0"
},
"scripts": {
"dev": "rm -rf src/dzeio/**/*.js && start-storybook -s ./.storybook/public -p 6006",
"build": "rollup --config",
"prepublishOnly": "yarn build",
"postinstall": "rollup --config"
"dev": "start-storybook --port 6006 --no-version-updates --disable-telemetry --no-manager-cache --modern --no-open",
"build": "npx vite build && npx tsc --emitDeclarationOnly",
"prepublishOnly": "npx vite build && npx tsc --emitDeclarationOnly"
},
"dependencies": {
"rollup": "^2.44.0",
"rollup-plugin-styles": "^3.14.1",
"rollup-plugin-typescript2": "^0.30.0",
"tslib": "^2.1.0"
"@dzeio/object-util": "^1",
"lucide-react": "^0.104.1"
}
}

View File

@ -1,29 +0,0 @@
import typescript from 'rollup-plugin-typescript2';
import styles from 'rollup-plugin-styles'
import pkg from './package.json';
export default [
{
input: 'src/index.ts',
external: ['ms'],
plugins: [
styles({
modules: true,
url: false,
autoModules: true,
mode: 'extract',
modules: {
generateScopedName: '[local][hash:5]'
}
}),
typescript({useTsconfigDeclarationDir: true}), // so Rollup can convert TypeScript to JavaScript
],
output: [
{
file: pkg.main,
format: 'cjs',
assetFileNames: 'style.css'
}
]
}
];

36
src/Box/Box.module.styl Normal file
View File

@ -0,0 +1,36 @@
@import "../config"
.box
background var(--theme-50)
@media (prefers-color-scheme dark)
background var(--gray-800)
border-radius 16px
&.noBottomBorder
border-radius 16px 16px 0 0
.header
padding 16px
&.noSidePadding
padding 16px 0
+ .body
padding-top 0
.title
font-weight 700
font-size rem(16)
display inline-flex
// BODY
.body
padding 16px
.icon
padding 2px
width 24px
height 24px
display inline-block
background var(--theme-500)
border-radius 4px
margin-right 8px

30
src/Box/Box.stories.tsx Normal file
View File

@ -0,0 +1,30 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
import Text from '../Text'
import { Lightbulb } from 'lucide-react'
import BoxHeader from './BoxHeader'
export default {
title: 'DZEIO/Box',
component: Component,
parameters: {
layout: 'fullscreen'
}
} as Meta
export const Box = (args: any) => (
<Component {...args}><Text>Test</Text></Component>
)
export const Complete = (args: any) => (
<Component
title="Test"
icon={Lightbulb}
rightHeader={<Text>Test</Text>}
// {...args}
>
<Text>Test</Text>
<BoxHeader title="BoxHeader" />
</Component>
)

View File

@ -0,0 +1,24 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Component from './BoxHeader'
import Text from '../Text'
import { Lightbulb } from 'lucide-react'
export default {
title: 'DZEIO/BoxHeader',
component: Component,
parameters: {
layout: 'fullscreen'
}
} as Meta
export const BoxHeader = (args: any) => (
<Component titel="Test" {...args} />
)
export const Complete = (args: any) => (
<Component
title="Test"
icon={Lightbulb}
><Text>Test</Text></Component>
)

44
src/Box/BoxHeader.tsx Normal file
View File

@ -0,0 +1,44 @@
import React from 'react'
import css from './Box.module.styl'
import Row from '../Row'
import Col from '../Col'
import Text from '../Text'
import { Icon } from '../interfaces'
import { buildClassName } from '../Util'
interface Props extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
title?: string
icon?: Icon
sidePadding?: boolean
children?: React.ReactNode
}
/**
* The box header that caan be used out of a box
*
* @version 1.0.0
*/
export default class BoxHeader extends React.Component<Props> {
public render = () => (
<div className={buildClassName(css.header, [css.noSidePadding, !this.props.sidePadding])}>
<Row justify="space-between">
<Col>
<Text className={css.title} weight="bold">
{this.props.icon && (
<span className={css.icon}>
<this.props.icon strokeWidth="2" fontWeight="800" size="20" color="white" />
</span>
)}
{this.props.title ? this.props.title : undefined}
</Text>
</Col>
{this.props.children && (
<Col nogrow>
{this.props.children}
</Col>
)}
</Row>
</div>
)
}

42
src/Box/index.tsx Normal file
View File

@ -0,0 +1,42 @@
import React from 'react'
import { buildClassName } from '../Util'
import css from './Box.module.styl'
import { Icon } from '../interfaces'
import { objectOmit } from '@dzeio/object-util'
import BoxHeader from './BoxHeader'
interface Props extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
noPadding?: boolean
// Header
title?: string
icon?: Icon
rightHeader?: React.ReactNode
noBottomBorder?: boolean
}
/**
* The basic Box Component
*
* @version 1.0.0
*/
export default class Box extends React.Component<Props> {
public render = () => (
<div
{...objectOmit<Record<string, any>>(this.props, 'title', 'icon', 'rightHeader', 'noPadding')}
className={buildClassName(css.box, this.props?.className, [css.noBottomBorder, this.props.noBottomBorder])}
>
{(this.props.rightHeader || this.props.title || this.props.icon) && (
<BoxHeader title={this.props.title} sidePadding icon={this.props.icon}>{this.props.rightHeader}</BoxHeader>
)}
{this.props.children && (
<div className={buildClassName([css.body, !this.props.noPadding])}>
{this.props.children}
</div>
)}
</div>
)
}

View File

@ -0,0 +1,23 @@
@import "../config"
.breadcrumb ol
display flex
display inline-flex
padding 0
margin 0
align-items center
flex-wrap wrap
li
display inline-block
&:first-child .item
padding-left 0
&:last-child .item
padding-right 0
.item
padding 0 16px
.chevron
color var(--theme-500)
@media (prefers-color-scheme dark)
color white

View File

@ -0,0 +1,20 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import { Zap } from 'lucide-react'
import Box from '../Box'
import Component from '.'
export default {
title: 'DZEIO/Breadcrumb',
component: Component
} as Meta
export const Breadcrumb = (args: any) => <Box><Component {...args}>Button</Component></Box>
Breadcrumb.args = {
items: [{
display: "Pouet",
href: '/pouet'
}, {
display: "Pouet",
}]
}

49
src/Breadcrumb/index.tsx Normal file
View File

@ -0,0 +1,49 @@
import React from 'react'
import Link from '../Link'
import Text from '../Text'
import css from './Breadcrumb.module.styl'
import { ChevronRight, Home } from 'lucide-react'
interface Props {
items: Array<{
display: string
href?: string
}>
textProps?: Text['props']
}
/**
* A breadcrumb compatible with Schema.org BreadcrumbList type
*
* @version 1.0.0
*/
export default class Breadcrumb extends React.Component<Props> {
public render() {
return (
<nav className={css.breadcrumb}>
<ol vocab="https://schema.org/" typeof="BreadcrumbList">
<li>
<Link className={css.item} href="/" noStyle>
<Text {...this.props.textProps} tag="span"><Home size={16} /></Text>
</Link>
</li>
{this.props.items.map((el, index) => (
<li property="itemListElement" typeof="ListItem" key={index}>
<Text {...this.props.textProps} tag="span"><ChevronRight size={14} className={css.chevron} /></Text>
{el.href ? (
<Link className={css.item} noStyle href={el.href.replace(/ /g, '-')} linkProps={{ property: "item", typeof: "WebPage" }}>
<Text {...this.props.textProps} tag="span" textProps={{ property: "name" }}>{el.display}</Text>
</Link>
) : (
<Text className={css.item} {...this.props.textProps} tag="span" weight="bold" textProps={{ property: "name" }}>{el.display}</Text>
)}
<meta property="position" content={index.toString()} />
</li>
))}
</ol>
</nav>
)
}
}

View File

@ -0,0 +1,143 @@
@import '../config'
.button
font-size rem(16)
position relative
transition all $transition
font-weight 600
line-height 1.5
display inline-flex
padding 10px 20px
cursor pointer
align-items center
text-align center
border-radius 4px
border none
justify-content center
align-items center
color var(--theme-500-text)
background-color var(--theme-500)
// Chrome Specific
outline none
// Link specific
text-decoration none
&.outline
&.ghost
&:hover
box-shadow none
background-color nativeRGBA(var(--theme-500-rgb), .2)
&:active
&:focus
background-color var(--theme-500)
color var(--theme-500-text)
&.outline
border 2px solid @background-color
padding 8px 18px // @padding - @border
background transparent
color @background-color
&:not(:disabled)
&:active
&:focus
color var(--theme-500-text)
&.ghost
background transparent
color black
@media (prefers-color-scheme dark)
color white
&:hover
background-color @background-color
box-shadow 0 0 0 4px nativeRGBA(var(--theme-500-rgb), .2)
&:active
&:focus
background-color var(--theme-800)
&.block
display flex
width 100%
margin 0
margin-top 8px
@media (max-width $mobile)
&.mobileBlock
display flex
width 100%
margin 0
margin-top 8px
&.large
padding 15px 30px
font-size rem(20)
&.outline
padding 13px 28px // @padding - @border
&.small
padding 5px 10px
font-size rem(14)
&.outline
padding 3px 8px // @padding - @border
&:disabled
background var(--gray-500)
color var(--gray-500--text)
transform none
box-shadow none
cursor initial
&.outline
border 2px solid var(--gray-500)
background transparent
@media (prefers-color-scheme dark)
border 2px solid var(--gray-500)
&.loading
color transparent
position relative
pointer-events none
&::after
content ""
display block
border white 2px solid
border-color transparent transparent white white
width 1em
position absolute
top calc(50% - (1em / 2))
left calc(50% - (1em / 2))
border-radius 100%
height 1em
box-sizing inherit
animation ButtonLoading .75s infinite linear
// &:disabled::after
// $rgb = var(--gray-500)
// border-color transparent transparent $rgb $rgb
// @media (prefers-color-scheme dark)
// border-color transparent transparent $rgb $rgb
svg + .textInner
margin-left 8px
.textInner + svg
margin-left 8px
@keyframes ButtonLoading
0%
transform rotate(0)
100%
transform rotate(365deg)
.img
min-width 16px

View File

@ -0,0 +1,34 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import { Zap } from 'lucide-react'
import Box from '../Box'
import Component from '.'
export default {
title: 'DZEIO/Button',
component: Component
} as Meta
export const Button = (args: any) => <Box><Component {...args}>Button</Component></Box>
Button.args = {
nomargintop: true,
icon: Zap,
mobileBlock: true,
iconLeft: Zap,
size: 'small'
}
export const WithImg = (args: any) => <Box><Component {...args}>Button</Component></Box>
WithImg.args = {
nomargintop: true,
icon: '/16-16.svg',
size: 'small',
block: true
}
export const ExternalLinkButton = (args: any) => <Box><Component {...args}>Button</Component></Box>
ExternalLinkButton.args = {
nomargintop: true,
href: 'https://example.com',
block: true
}

78
src/Button/index.tsx Normal file
View File

@ -0,0 +1,78 @@
import React from 'react'
import Image from '../Image'
import { ColorType, Icon } from '../interfaces'
import Link from '../Link'
import { buildClassName } from '../Util'
import css from './Button.module.styl'
// MAKE OUTLINE use Fieldset instead of the current one xd
interface Props {
color?: ColorType
children?: React.ReactNode
className?: string
icon?: Icon | string
iconLeft?: Icon | string
size?: 'large' | 'small'
type?: 'outline' | 'ghost'
block?: boolean
href?: string
linkExternal?: boolean
mobileBlock?: boolean
disabled?: boolean
loading?: boolean
onClick?: (event: React.MouseEvent<HTMLButtonElement|HTMLAnchorElement, MouseEvent>) => void
}
export default class Button extends React.Component<Props> {
public render = () => {
let inner: any = this.props.children
if (this.props.icon || this.props.iconLeft) {
inner = (
<>
{this.props.icon && (typeof this.props.icon === 'string' ? (
<Image imageProps={{src: this.props.icon, width: 16, height: 16}} />
) : (
<this.props.icon size={this.props.size === 'large' ? 20 : this.props.size === 'small' ? 14 : 16} />
))}
{this.props.children && (
<span className={css.textInner}>{this.props.children}</span>
)}
{this.props.iconLeft && (typeof this.props.iconLeft === 'string' ? (
<Image imageProps={{src: this.props.iconLeft, width: 16, height: 16}} />
) : (
<this.props.iconLeft size={this.props.size === 'large' ? 20 : this.props.size === 'small' ? 14 : 16} />
))}
</>
)
}
const classes = buildClassName(
[css.button],
[css[this.props.color as string], this.props.color],
[css[this.props.type as string], this.props.type],
[css.block, this.props.block],
[css[this.props.size as string], this.props.size],
[css.loading, this.props.loading],
[css.mobileBlock, this.props.mobileBlock],
this.props.className
)
if (this.props.href && !this.props.disabled) {
return (
<Link external={this.props.linkExternal} linkProps={{onClick: this.props.onClick}} noStyle href={this.props.href} className={buildClassName(classes, [css.disabled, this.props.disabled])}>
{inner}
</Link>
)
}
return (
<button onClick={this.props.onClick} disabled={this.props.disabled} className={classes}>{inner}</button>
)
}
}

View File

@ -0,0 +1,113 @@
@import "../config.styl"
$backColor = #757575
.label
position relative
display flex
user-select none
align-items center
+ .label
margin-top 8px
p
margin-left 4px
span
top 0
left 0
width 20px
height @width
position relative
box-shadow inset 0 0 0 2px var(--gray-600)
@media (prefers-color-scheme dark)
box-shadow inset 0 0 0 2px var(--gray-400)
border-radius 4px
transition all $transition
&::after
border-radius 20px
position absolute
transition all $transition
background var(--theme-500)
svg
transition $transition
transform scale(0)
color transparent
margin 2px
input
// visibility hidden
position absolute
top 0
left 0
opacity 0
&:checked + span
background var(--theme-500)
box-shadow inset 0 0 0 2px var(--theme-500)
svg
color white
transform scale(1)
&:hover
span
box-shadow inset 0 0 0 2px var(--theme-500)
.radio
span
border-radius 20px
&::after
content " "
top 5px
left 5px
width 10px
height @width
transform scale(0)
input:checked + span::after
transform scale(1)
background white
.switch
padding 0 0 0 10px // 2px base padding 10px circle padding
&:hover span
box-shadow none
&::after
background var(--theme-500)
span
width 24px
height 14px
border-radius 20px
margin-right 10px
box-shadow none
background var(--gray-500)
&::after
content " "
top 50%
transform translate(-50%, -50%)
left 0
background var(--gray-600)
width 20px
height @width
input
margin 0 8px
width 20px
&:checked + span
box-shadow none
background var(--theme-300)
&::after
left 100%
transform translate(-50%, -50%)
background var(--theme-500)

View File

@ -1,10 +1,10 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Checkbox from '.'
import CheckboxReact from '.'
export default {
title: 'DZEIO/Checkbox',
component: Checkbox
component: CheckboxReact
} as Meta
export const Basic = (args: any) => <Checkbox {...args} />
export const Checkbox = (args: any) => <CheckboxReact {...args} />

View File

@ -1,8 +1,7 @@
import React from 'react'
import { Check } from 'react-feather'
import { Check } from 'lucide-react'
import { buildClassName } from '../Util'
import { ColorType } from '../interfaces'
import css from './Checkbox.module.styl'
import Text from '../Text'

15
src/Code/Code.module.styl Normal file
View File

@ -0,0 +1,15 @@
@import "../config"
.code
.pre
background var(--theme-50)
padding 4px 8px
border-radius 8px
@media (prefers-color-scheme dark)
background var(--gray-700)
color var(--gray-700-text)
.pre
display block
.code
padding 0

View File

@ -10,7 +10,7 @@ export default {
}
} as Meta
export const Basic = (args: any) => {
export const Code = (args: any) => {
const content = args.content
delete args.content

View File

@ -4,6 +4,7 @@ import css from './Code.module.styl'
interface Props {
block?: boolean
children?: React.ReactNode
}
export default class Code extends React.Component<Props> {

View File

@ -4,10 +4,9 @@
max-width 100%
flex-basis 0
flex-grow 1
padding $gapSize 0 0 $gapSize
&.nogrow
max-width intial
max-width initial
flex-grow 0
flex-basis initial
@ -16,12 +15,13 @@ for i in (0...$colCount+1)
if i == 0
display none
else
flex 0 0 ((i / 12) * 100)%
min-width ((i / 12) * 100)%
$tmp = ((i / $colCount) * 100)%
flex "0 0 calc(%s - %s)" % ($tmp $gapSize)
min-width "calc(%s - %s)" % ($tmp $gapSize)
if i != $colCount and i != 0
.offset-{i}
margin-left ((i / 12) * 100)% + (i * $gapSize + $gapSize)px
margin-left ((i / $colCount) * 100)% + (i * $gapSize + $gapSize)px
@media (max-width $tablet)
.col.tabletGrow
@ -32,8 +32,9 @@ for i in (0...$colCount+1)
if i == 0
display none
else
flex 0 0 ((i / $colCountTablet) * 100)%
min-width ((i / $colCountTablet) * 100)%
$tmp = ((i / $colCountTablet) * 100)%
flex "0 0 calc(%s - %s)" % ($tmp $gapSize)
min-width "calc(%s - %s)" % ($tmp $gapSize)
if i != $colCountTablet and i != 0
.offset-tablet-{i}
@ -48,8 +49,9 @@ for i in (0...$colCount+1)
if i == 0
display none
else
flex 0 0 ((i / $colCountMobile) * 100)%
min-width ((i / $colCountMobile) * 100)%
$tmp = ((i / $colCountMobile) * 100)%
flex "0 0 calc(%s - %s)" % ($tmp $gapSize)
min-width "calc(%s - %s)" % ($tmp $gapSize)
if i != $colCountMobile and i != 0
.offset-tablet-{i}
margin-left ((i / $colCountMobile) * 100)% + (i * $gapSize - $gapSize)

View File

@ -1,8 +1,9 @@
import { objectOmit } from '@dzeio/object-util'
import React from 'react'
import { buildClassName } from '../Util'
import css from './Col.module.styl'
interface Props {
interface Props extends React.HTMLAttributes<HTMLDivElement> {
size?: 0|1|2|3|4|5|6|7|8|9|10|11|12
offset?: 1|2|3|4|5|6|7|8|9|10|11
children?: React.ReactNode
@ -21,10 +22,25 @@ interface Props {
mobileGrow?: boolean
}
export default class Col extends React.Component<Props> {
public render = () => (
<div className={buildClassName(
<div {...objectOmit(
this.props,
"size",
"offset",
"children",
"className",
"nogrow",
"tabletSize",
"tabletoffset",
"tabletGrow",
"mobileSize",
"mobileoffset",
"mobileGrow"
)} className={buildClassName(
css.col,
// Normal

View File

@ -0,0 +1,10 @@
.container
padding 0 32px
width 100%
max-width 1280px + @padding[1] * 2
margin auto auto
> *:not(:first-child)
margin-top 48px
&.main
margin 0 auto
padding 48px @padding[1]

View File

@ -0,0 +1,19 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
import Text from '../Text'
export default {
title: 'DZEIO/Container',
component: Component,
argTypes: {
title: { control: 'text'}
},
parameters: {
layout: 'fullscreen'
}
} as Meta
export const Container = (args: any) => (
<Component {...args}><Text>Test</Text></Component>
)

19
src/Container/index.tsx Normal file
View File

@ -0,0 +1,19 @@
import React from 'react'
import { buildClassName } from '../Util'
import css from './Container.module.styl'
interface Props {
children: React.ReactNode
className?: string
mainContainer?: boolean
}
export default class Container extends React.Component<Props> {
public render = () => React.createElement(this.props.mainContainer ? 'main' : 'div', {
className: buildClassName(css.container, this.props.className, [css.main, this.props.mainContainer]),
children: this.props.children
})
}

View File

@ -1,8 +1,8 @@
@import '../config.styl'
.fieldset
border-radius 4px
border 2px solid $grayDark
border-radius 8px
border 2px solid var(--gray-500)
transition all $transition
margin 0
@ -12,8 +12,3 @@
padding 0 4px
@media (prefers-color-scheme dark)
color white
&:hover
border-color black
@media (prefers-color-scheme dark)
border-color white

View File

@ -1,21 +1,33 @@
@import '../config.styl'
.footer
padding 24px 16px
background $foregroundLight
padding 24px 0
background var(--theme-50)
@media (prefers-color-scheme dark)
background $foregroundDark
background var(--gray-800)
ul
list-type none
padding 0
margin 0
display flex
justify-content right
li
display inline-block
padding-left 24px
+ .icon
padding-left 16px
&:not(.icon) + .icon
padding-left 48px
.animation
animation grow 1s linear infinite
display inline-block
vertical-align middle
margin 0 2px
width 16px
height @width
vertical-align sub
@keyframes grow
0%

View File

@ -1,15 +1,22 @@
import { Meta, Story } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
export default {
title: 'DZEIO/Footer',
component: Component,
} as Meta
export const Basic: Story<any> = (args: any) => <Component {...args} />
let tmp = Basic.bind({})
tmp.args = {links: [{name: 'test1', path: '/'}, {name: 'test2', path: '/'}, {name: 'test3', path: '/'}]}
export const Normal = tmp
import { Meta, Story } from '@storybook/react/types-6-0'
import React from 'react'
import { Zap } from 'lucide-react'
import Component from '.'
export default {
title: 'DZEIO/Footer',
component: Component,
parameters: {
layout: 'fullscreen'
}
} as Meta
export const Basic: Story<any> = (args: any) => <Component {...args} />
let tmp = Basic.bind({})
tmp.args = {
links: [{name: 'test1', path: '/'}, {name: 'test2', path: '/'}, {name: 'test3', path: '/'}],
socials: [{icon: Zap, href: '/'}, {icon: '/16-16.svg', href: '/'}, {icon: Zap, href: '/'}]
}
export const Normal = tmp

58
src/Footer/index.tsx Normal file
View File

@ -0,0 +1,58 @@
import React from 'react'
import { Heart } from 'lucide-react'
import Link from '../Link'
import Text from '../Text'
import css from './Footer.module.styl'
import Image from 'next/image'
import { Icon } from '../interfaces'
import Container from '../Container'
import Row from '../Row'
import Col from '../Col'
interface Props {
text?: string
company?: string
links?: Array<{
path: string
name: string
}>
socials?: Array<{
href: string
icon: Icon | string
}>
}
export default class Footer extends React.Component<Props> {
public render = () => (
<footer className={css.footer}>
<Container>
<Row>
<Col>
{this.props.text ? (
<Text>{this.props.text}</Text>
) : (
<Text weight="bold">Made with <span className={css.animation}><Heart color={'#E6808A'} fill={'#E6808A'} size={16} fillOpacity={0.5} /></span> by {this.props.company || 'Dzeio'}</Text>
)}
</Col>
<Col>
<ul>
{this.props.links && this.props.links.map((l, index) => (
<li key={l.path + index}><Text><Link href={l.path} hideIcon>{l.name}</Link></Text></li>
))}
{this.props.socials && this.props.socials.map((l, index) => (
<li key={l.href + index} className={css.icon}><Text><Link hideIcon noStyle href={l.href}>
{typeof l.icon === 'string' ? (
<Image width={24} height={24} src={l.icon} />
) : (
<l.icon size={24} />
)}
</Link></Text></li>
))}
</ul>
</Col>
</Row>
</Container>
</footer>
)
}

View File

@ -0,0 +1,34 @@
.parent
transition-property padding, width, height, background, top, left
transition-duration .3s
transition-timing-function ease-in-out
cursor pointer
// Animation part 1
// this is set to move the image from a normal position to a fixed one
// + one the image itself there is style with position
.fs1
position fixed
z-index 1000
padding 0
> div
width 100%
height 100%
// Animation part 2
// this animation move the card from its original pos to a fullscreen one
.fs2
padding 8px
width 100% !important
height 100vh !important
background rgba(black, 50%)
// padding 0
user-select none
top 0 !important
left 0 !important
> div
width 100%
height 100%
.body
overflow hidden

View File

@ -0,0 +1,10 @@
import { Meta, Story } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
export default {
title: 'DZEIO/Image',
component: Component
} as Meta
export const Image: Story<any> = (args: any) => <Component imageProps={{src: '/90-38.svg', width: 90, height: 38}} fullscreen {...args} />

104
src/Image/index.tsx Normal file
View File

@ -0,0 +1,104 @@
import React, { MouseEventHandler } from 'react'
import NextImage, { ImageProps } from 'next/image'
import css from './Image.module.styl'
import { buildClassName } from '../Util'
interface Props {
imageProps: ImageProps
/**
* Define if the image can go fullscreen
*/
fullscreen?: boolean
}
interface States {
image?: {
size: [number | string, number | string]
pos: [number, number]
}
transform?: [number, number]
className?: string
}
export default class Image extends React.Component<Props, States> {
public state: States = {}
private animationCount = 0
public componentDidUpdate() {
if (!this.props.fullscreen) {return}
if (this.state.image) {
document.body.classList.add(css.body)
} else {
document.body.classList.remove(css.body)
}
}
public componentWillUnmount() {
if (!this.props.fullscreen) {return}
document.body.classList.remove(css.body)
}
public render() {
if (!this.props.fullscreen) {
return <NextImage
{...this.props.imageProps}
objectFit="contain"
/>
}
return (
<>
{this.state.image && (
<div style={{width: this.state.image.size[0], height: this.state.image.size[1]}}></div>
)}
<div
className={buildClassName(css.parent, [css.fs1, this.state.image], [this.state.className, this.state.image])}
style={this.state.image ? {
top: this.state.image.pos[1],
left: this.state.image.pos[0],
width: this.state.image.size[0],
height: this.state.image.size[1]
} : undefined}
onClick={this.props.fullscreen ? this.onClick : undefined}
>
<NextImage
priority
quality={100}
{...this.props.imageProps}
layout={this.state.image ? 'fill' : this.props.imageProps.layout}
objectFit="contain"
/>
</div>
</>
)
}
private onClick: MouseEventHandler<HTMLDivElement> = (ev) => {
const target = ev.currentTarget
const isFullscreen = !(this.state.image && this.state.className)
const currentCount = ++this.animationCount
this.setState(isFullscreen ? {
image: this.state.image ?? {
size: [target.offsetWidth, target.offsetHeight],
pos: [target.offsetLeft - window.scrollX, target.offsetTop - window.scrollY]
},
className: undefined
} : {
className: undefined
}, () => {
setTimeout(() => {
if (this.animationCount !== currentCount) {
return
}
this.setState({
className: isFullscreen ? css.fs2 : undefined,
image: isFullscreen ? this.state.image : undefined
})
}, isFullscreen ? 10 : 310)
})
}
}

221
src/Input/Input.module.styl Normal file
View File

@ -0,0 +1,221 @@
@import '../config'
.label
font-size rem(16)
display block
font-weight bold
color black
padding-left 8px
padding-bottom 4px
cursor pointer
@media (prefers-color-scheme dark)
color white
.parent
position relative
max-width 100%
display inline-block
&:not(.block) + .parent:not(.block)
margin-left 16px
svg
position absolute
user-select none
padding 16px
color black
@media (prefers-color-scheme dark)
color white
transition color $transition
pointer-events none
&.iconClickable
pointer-events all
cursor pointer
top -2px
&.left
left 0px // input padding-left
~ label
left 0px + 24px + 10px
&.right
right 0
select
appearance none
option
background var(--theme-50)
color black
@media (prefers-color-scheme dark)
background var(--gray-800)
color white
textarea
resize none
overflow-y hidden
/* Remove the arrows from the Number Input */
input[type="number"]
-moz-appearance textfield
input::-webkit-outer-spin-button
input::-webkit-inner-spin-button
-webkit-appearance none
margin 0
/* End */
.autocomplete
opacity 0
transition all $transition
overflow-x hidden
pointer-events none
position absolute
top calc(100% + 16px)
left 0
width 100%
z-index 100
max-height 25vh
overflow-y auto
@media (max-width $mobile)
max-height 50vh
&.reverse
top initial
bottom calc(100% + 16px)
div + .autocomplete
top 100%
input:focus ~ .autocomplete
select:focus ~ .autocomplete
textarea:focus ~ .autocomplete
.autocomplete:hover
opacity 1
pointer-events inherit
input
select
textarea
padding 12px
border-radius 8px
max-width 100%
font-size .875rem
outline none
background var(--theme-50)
transition all $transition
border 2px solid black
color black
@media (prefers-color-scheme dark)
background var(--gray-800)
border-color transparent
border 2px solid var(--gray-500)
color white
&::placeholder
font-size rem(16)
transition color $transition
opacity 1
color black
@media (prefers-color-scheme dark)
color white
&:disabled
border-color var(--gray-500)
&:not(:disabled)
&:hover
border-color black
@media (prefers-color-scheme dark)
border-color white
~ svg
&::placeholder
color black
@media (prefers-color-scheme dark)
color white
&:focus
border-color var(--theme-500)
~ svg
color @border-color
// &::placeholder
// color black
// @media (prefers-color-scheme dark)
// color white
&.iconLeft
padding-left 16px + 24px + 10px
&.iconRight
padding-right 16 + 24 + 10px
~ svg.rotate
transform rotateX(0)
transition $transition
&:focus ~ svg.rotate
~ .autocomplete:hover ~ svg.rotate
transform rotateX(180deg)
input[type="range"]
appearance none
background transparent
cursor pointer
width 100%
border none
$height = 4px
&::-webkit-slider-runnable-track
appearance none
background-color var(--theme-100)
height $height
width 100%
border-radius $height
transition $transition
@media (prefers-color-scheme dark)
background-color var(--gray-800)
&:active::-webkit-slider-runnable-track
background var(--theme-200)
@media (prefers-color-scheme dark)
background-color var(--gray-700)
&::-moz-range-track
appearance none
background-color var(--theme-100)
height $height
width 100%
border-radius $height
transition $transition
@media (prefers-color-scheme dark)
background-color var(--gray-800)
&::-webkit-slider-thumb
appearance none
$size = 16px
margin-top ($height / 2) - ($size / 2)
height $size
width $size
border none
background-color var(--theme-500)
border-radius $size
&::-moz-range-thumb
appearance none
$size = 16px
margin-top ($height / 2) - ($size / 2)
height $size
width $size
border none
background-color var(--theme-500)
border-radius $size
p
padding 0 8px
font-size rem(14)
&.block
&.block input
&.block textarea
width 100%
display block

115
src/Input/Input.stories.tsx Normal file
View File

@ -0,0 +1,115 @@
import { Story } from "@storybook/react"
import { Meta } from '@storybook/react/types-6-0'
import { X } from 'lucide-react'
import React from 'react'
import Component from '.'
export default {
title: 'DZEIO/Input',
component: Component
} as Meta
export const Input: Story<any> = (args: any) => <Component {...args} />
let tmp = Input.bind({})
tmp.args = {
label: 'Label',
helper: 'Helper',
maxLength: 6,
// iconLeft: {
// icon: X,
// transformer: (v: string) => v + 1
// },
min: 0,
id: 'pouet',
type: 'number',
step: 10,
defaultValue: 'test',
placeholder: 'test',
disabled: false
}
export const Normal = tmp
tmp = Input.bind({})
tmp.args = {defaultValue : 'd', label: 'Label', helper: 'Helper', choices: [
'a',
'a',
'a',
'a',
'a',
'a',
'a',
'a',
'b',
{value: 'd', display: 'D'},
{value: '4', display: 'Mai'},
'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'
], iconLeft: {
icon: X,
transformer: (v: string) => {
console.log("POUET :D")
return ""
}
}}
export const AutoComplete = tmp
tmp = Input.bind({})
tmp.args = {label: 'Label', helper: 'Helper', strictChoices: true, choices: [
'a',
'a',
'a',
'a',
'a',
'a',
'a',
'a',
'b',
{value: 'd', display: 'D'},
{value: '4', display: 'Mai'},
'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'
], iconLeft: {
icon: X,
transformer: (v: string) => {
console.log("POUET :D")
return ""
}
}}
export const Select = tmp
tmp = Input.bind({})
tmp.args = {label: 'Label', helper: 'Helper', type: 'number', iconLeft: {
icon: X,
transformer: (v: string) => {
console.log("POUET :D")
return v + 2
}
}}
export const Number = tmp
tmp = Input.bind({})
tmp.args = {block: true, type: 'textarea', defaultValue : 'd', label: 'Label', helper: 'Helper', choices: [
'a',
'a',
'a',
'a',
'a',
'a',
'a',
'a',
'b',
{value: 'd', display: 'D'},
{value: '4', display: 'Mai'},
'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'
], iconLeft: {
icon: X,
transformer: (v: string) => {
console.log("POUET :D")
return ""
}
}}
export const TextArea = tmp

407
src/Input/index.tsx Normal file
View File

@ -0,0 +1,407 @@
import React, { FocusEvent } from 'react'
import { objectEqual, objectOmit } from '@dzeio/object-util'
import { ChevronDown, MinusSquare, PlusSquare } from 'lucide-react'
import Menu from '../Menu'
import Text from '../Text'
import { buildClassName } from '../Util'
import { Icon } from '../interfaces'
import css from './Input.module.styl'
interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> {
id?: string
label?: string
iconLeft?: Icon | {
icon: Icon
transformer: (value: string) => string
}
iconRight?: Icon | {
icon: Icon
transformer: (value: string) => string
}
helper?: string
inputRef?: React.RefObject<HTMLInputElement>
type?: 'color' | 'text' | 'date' | 'datetime-local' |
'email' | 'file' | 'month' | 'number' | 'password' |
'range' | 'search' | 'tel' | 'time' | 'url' | 'week' |
// Custom Types
'textarea'
choices?: Array<string | {display: string, value: string}>
/**
* Always display every choices
*/
displayAllOptions?: boolean
/**
* Handle the change event
* you will be returned the value (for choices too)
*/
onValue?: (newValue: string) => void
/**
* Make the input take the whole width
*/
block?: boolean
/**
* if enabled value will not be sent if it is not contained in the choices
*/
strictChoices?: boolean
/**
* Allows you to disable automatic icons
*/
disableAutoIcons?: boolean
placeholder?: string
}
interface States {
textAreaHeight?: number
value?: string
displayedValue?: string
valueUpdate: boolean
isInFirstPartOfScreen?: boolean
list: Menu['props']['items']
}
export default class Input extends React.Component<Props, States> {
public state: States = {
valueUpdate: false,
list: []
}
private inputRef: React.RefObject<HTMLInputElement> = React.createRef()
private parentRef: React.RefObject<HTMLDivElement> = React.createRef()
public componentDidMount() {
// Handle Text Area
if (this.props.type === 'textarea') {
this.textareaHandler()
}
// Handle choices
if (this.props.choices) {
window.addEventListener('scroll', this.parentScroll)
this.parentScroll()
}
// Handle default Value
if (typeof (this.props.defaultValue ?? this.props.value) !== 'undefined') {
const value = this.props.defaultValue ?? this.props.value ?? ''
if (!this.props.choices) {
this.setState({displayedValue: value.toString()})
} else {
const res = this.props.choices.find(
(it) => typeof it === 'string' ? it === value : it.value === value
)
if (!res) {
if (this.props.strictChoices) {
this.setState({value: '', displayedValue: ''})
} else {
this.setState({displayedValue: value.toString()})
}
return
}
this.setState({
displayedValue: typeof res === 'string' ? res : res.display
})
}
}
if (this.props.choices) {
this.setState({list: this.buildList()})
}
}
public componentWillUnmount() {
if (this.props.choices) {
window.removeEventListener('scroll', this.parentScroll)
}
}
public async componentDidUpdate(prevProps: Props, prevStates: States) {
if (prevProps.value !== this.props.value && this.props.value !== this.state.value) {
if (this.props.choices) {
const choice = this.props.choices.find((it) => typeof it === 'string' ? it : it.value === this.props.value?.toString())
if (choice) {
this.setState({
displayedValue: typeof choice === 'string' ? choice : choice.display,
value: typeof choice === 'string' ? choice : choice.value,
valueUpdate: true
})
}
} else {
this.setState({ displayedValue: this.props.value?.toString(), value : this.props.value?.toString(), valueUpdate: true })
}
}
if (
prevStates.value !== this.state.value ||
prevStates.displayedValue !== this.state.displayedValue ||
prevStates.valueUpdate !== this.state.valueUpdate ||
!objectEqual(prevProps.choices ?? [], this.props.choices ?? [])
) {
this.setState({list: this.buildList()})
}
}
/**
* return the real value of the field (depending if you a choices and the display value)
* @returns the value of the field
*/
public value(): string | number | ReadonlyArray<string> | undefined {
return this.state?.value ?? this.state.displayedValue ?? this.props.value ?? undefined
}
public render() {
const props: Props = objectOmit(this.props, 'iconLeft', 'iconRight', 'inputRed', 'helper', 'choices', 'onValue', 'block', 'defaultValue', 'label', 'strictChoices')
const baseProps: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> = {
ref: this.props.inputRef || this.inputRef,
className: buildClassName(
[css.iconLeft, this.props.type === 'number' || this.props.iconLeft],
[css.iconRight, this.props.type === 'number' || this.props.iconRight || this.props.choices]
),
onInvalid: (ev: React.FormEvent<HTMLInputElement>) => ev.preventDefault(),
onFocus: (ev: FocusEvent<HTMLInputElement, Element>) => {
this.setState({valueUpdate: false})
if (props.onFocus) {
props.onFocus(ev)
}
},
value: this.state.displayedValue ?? this.state.value ?? this.props.value,
onChange: this.onChange
}
let iconRight = this.props.iconRight
let iconLeft = this.props.iconLeft
let input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> = <input
{...props}
{...baseProps}
/>
switch (this.props.type) {
case 'textarea':
delete baseProps.ref
input = (
<textarea
{...props as React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>}
{...baseProps as any}
ref={this.inputRef}
style={{minHeight: this.state?.textAreaHeight}}
onKeyDown={this.textareaHandler}
onKeyUp={this.textareaHandler}
onFocus={this.textareaHandler}
/>
)
break
case 'number':
baseProps.onWheel = (ev: React.WheelEvent<HTMLInputElement>) => ev.currentTarget.blur()
if (!this.props.disabled && !this.props.disableAutoIcons) {
iconLeft = this.props.iconLeft ?? {icon: MinusSquare, transformer: (v) => {
let value = this.ensureNumber(v)
return (value - this.ensureNumber(this.props.step, 1)).toString()
}}
iconRight = this.props.iconRight ?? {icon: PlusSquare, transformer: (v) => {
let value = this.ensureNumber(v)
return (value + this.ensureNumber(this.props.step, 1)).toString()
}}
}
input = <input
{...props}
{...baseProps}
/>
break
}
return (
<>
{this.props.label && (
<label className={css.label} htmlFor={this.props.id}>{this.props.label}</label>
)}
<div
className={buildClassName(
css.parent,
[css.block, this.props.block]
)}
ref={this.parentRef}
>
{input as any}
{/* Left Icon */}
{this.getIcon(iconLeft, 'left')}
{/* Right Icon */}
{iconRight ?
this.getIcon(iconRight, 'right') :
this.props.choices && !this.props.disabled && !this.props.disableAutoIcons && (
<ChevronDown size="18" className={buildClassName(css.right, css.rotate)} />
)
}
{/* Helper text */}
{this.props.helper && (
<Text>{this.props.helper}</Text>
)}
{/* List when this is an autocomplete */}
{this.props.choices && (
<Menu
outline
hideWhenEmpty
className={buildClassName(css.autocomplete, [css.reverse, !this.state.isInFirstPartOfScreen])}
items={this.state.list ?? []}
onClick={this.listSelection}
/>
)}
</div>
</>
)
}
private ensureNumber(item: string | number | undefined, defaultValue: number = 0): number {
console.log('ensureNumber', item, typeof item)
if (typeof item === 'number') return item
if (typeof item === 'undefined') return defaultValue
const res = parseFloat(item)
if (isNaN(res)) {
return defaultValue
}
return res
}
/**
* event for the menu to detect where on the screen it should be displayed
*/
private parentScroll = async () => {
const div = this.parentRef.current
if (!div) {return}
const result = !(div.offsetTop - window.scrollY >= window.innerHeight / 2)
if (this.state.isInFirstPartOfScreen !== result) {
this.setState({isInFirstPartOfScreen: result})
}
}
/**
* Build the interactive list for the item
* @returns the list
*/
private buildList(): Menu['props']['items'] {
if (!this.props.choices) {
return []
}
const v = this.state.displayedValue?.toLowerCase()
return this.props.choices
.map((item, index) => typeof item === 'string' ? {item: {display: item, value: item}, index} : {item, index})
.filter(
(item) => this.props.displayAllOptions || !this.state.valueUpdate || !v || item.item.display.toLowerCase().includes(v) || item.item.display.toLowerCase().toLowerCase().includes(v)
)
.map((item) => item.item)
}
/**
* handle when an item is selected
* @param key the index of the selected item
*/
private listSelection: Menu['props']['onClick'] = async (_, key) => {
const newValue = this.state.list[key]
if (!newValue) {
return
}
if (typeof newValue === 'string') {
this.onChange(newValue)
return
}
this.onChange(newValue.value)
}
/**
* get the icon duh
* @param icon the icon
* @returns the icon
*/
private getIcon(Icon: Icon | {
icon: Icon
transformer: (value: string) => string
} | undefined, position: 'left' | 'right') {
if (!Icon) {
return undefined
}
if ('icon' in Icon) {
// 18 + 16 of padding
return <Icon.icon size={16*2+18} className={buildClassName(css[position], css.iconClickable)} onClick={async () => {
if (this.props.disabled) {return}
const value = Icon.transformer(this.state.value ?? this.state.displayedValue ?? '')
this.onChange(value)
}} />
}
return <Icon size="18" className={css[position]} />
}
/**
* Handle textarea height changes
*/
private textareaHandler = async () => {
this.setState({textAreaHeight: undefined}, () => {
if (!this.inputRef.current) {return}
this.setState({ textAreaHeight: this.inputRef.current.scrollHeight })
})
}
/**
* handle the change event of the input
* @param event the event
*/
private onChange = async (event: React.ChangeEvent<HTMLInputElement>|string | number) => {
// get the input
let value = typeof event === 'object' ? event.currentTarget.value : event
if (typeof value === 'number') {
value = value.toString()
}
if (this.props.type === 'number') {
const val = this.ensureNumber(value)
const min = typeof this.props.min !== 'undefined' ? typeof this.props.min === 'string' ? this.ensureNumber(this.props.min) : this.props.min : -Infinity
const max = typeof this.props.max !== 'undefined' ? typeof this.props.max === 'string' ? this.ensureNumber(this.props.max) : this.props.max : Infinity
console.log('pouet', val, this.props.min, min, this.props.max, max)
value = Math.min(max, Math.max(min, val)).toString()
}
let displayedValue = value
if (this.props.choices) {
const item = this.props.choices.find((it) => typeof it === 'string' ? it === value : it.value === value)
if (this.props.strictChoices && !item) {
this.setState({ displayedValue: displayedValue, valueUpdate: true })
return
}
if (item && typeof item !== 'string') {
displayedValue = item?.display
}
}
if (this.props.onChange && typeof event === 'object') {
event.currentTarget.value = value
this.props.onChange(event)
}
this.props.onValue?.(value)
this.setState({ value: value, displayedValue: displayedValue, valueUpdate: true })
}
}

View File

@ -0,0 +1,9 @@
@import '../config'
.link
color var(--theme-500)
text-decoration underline
.icon
vertical-align sub
margin 2px

View File

@ -7,8 +7,10 @@ export default {
component: Component,
argTypes: {
href: {control: 'text', defaultValue: 'https://www.dzeio.com'},
text: {control: 'text', defaultValue: 'Dzeio'}
text: {control: 'text', defaultValue: 'Dzeio'},
external: {control: 'boolean'},
hideIcon: {control: 'boolean'}
}
} as Meta
export const Basic = (args: any) => <Component {...args}>{args.text}</Component>
export const Link = (args: any) => <Component {...args}>{args.text}</Component>

View File

@ -1,6 +1,6 @@
import React from 'react'
import NextLink from 'next/link'
import { ExternalLink } from 'react-feather'
import { ExternalLink } from 'lucide-react'
import css from './Link.module.styl'
import { buildClassName } from '../Util'
@ -19,23 +19,35 @@ interface Props {
* Override external detection system
*/
external?: boolean
/**
* force hiding the icon
*/
hideIcon?: boolean
}
export default class Link extends React.Component<Props> {
public render() {
const external = this.props.external ?? this.props.href.startsWith('http')
if (external) {
const isExternal = this.props.href.startsWith('http')
const externalProps = this.props.external ?? isExternal ? {
rel: 'noreferrer nofollow',
target: '_blank'
} : {}
if (isExternal) {
// external link
return (
<a
{...this.props.linkProps}
className={buildClassName(this.props.className, [css.link, !this.props.noStyle])}
href={this.props.href}
rel="noreferrer nofollow"
target="_blank"
{...externalProps}
>
{this.props.children}<ExternalLink size={16} className={css.icon} />
{this.props.children}
{(this.props.external !== false && !this.props.hideIcon) && (
<ExternalLink size={16} className={css.icon} />
)}
</a>
)
}
@ -43,6 +55,7 @@ export default class Link extends React.Component<Props> {
<NextLink href={this.props.href}>
<a
{...this.props.linkProps}
{...externalProps}
className={buildClassName(this.props.className, [css.link, !this.props.noStyle])}
>{this.props.children}</a>
</NextLink>

View File

@ -0,0 +1,14 @@
@import '../config'
.div
position fixed
left 0
width 100%
pointer-events none
z-index 200
transition top .5s ease-in-out
top 0
&.hide
top -8px

View File

@ -0,0 +1,17 @@
import { Meta, Story } from '@storybook/react/types-6-0'
import React from 'react'
import { Zap } from 'lucide-react'
import Component from '.'
export default {
title: 'DZEIO/Loader',
component: Component,
parameters: {
layout: 'fullscreen'
},
argTypes: {
percent: { control: 'number'}
},
} as Meta
export const Loader: Story<any> = (args: any) => <Component {...args} />

View File

@ -1,5 +1,6 @@
import { Router } from 'next/router'
import React from 'react'
import ProgressBar from '../ProgressBar'
import { buildClassName } from '../Util'
import css from './Loader.module.styl'
@ -30,7 +31,7 @@ interface State {
/**
* Display a simple loading animation at the top of the page
*
*
* @version 1.0.0
*/
export default class Loader extends React.Component<Props, State> {
@ -42,22 +43,24 @@ export default class Loader extends React.Component<Props, State> {
public componentDidMount() {
if (this.props.auto) {
Router.events.on('routeChangeComplete', this.routeChangeComplete)
Router.events.on('routeChangeError', this.routeChangeComplete)
Router.events.on('routeChangeStart', this.routeChangeStart)
}
}
public componentWillUnmount() {
if (this.props.auto) {
Router.events.off('routechangeComplete', this.routeChangeComplete)
Router.events.off('routechangeStart', this.routeChangeStart)
Router.events.off('routeChangeComplete', this.routeChangeComplete)
Router.events.off('routeChangeError', this.routeChangeComplete)
Router.events.off('routeChangeStart', this.routeChangeStart)
}
}
public render = () => (
<div className={buildClassName(css.div, [css.hide, (this.props.percent || this.state.percent) === 100])}>
<div style={{width: (this.props.percent || this.state.percent) ? `${(this.props.percent || this.state.percent)}%` : 0}}></div>
</div>
)
public render = () => <ProgressBar
noRoundBorders
progress={this.props.percent ?? this.state.percent ?? 0}
className={buildClassName(css.div, [css.hide, (this.props.percent ?? this.state.percent) === 100 || (this.props.percent ?? this.state.percent ?? 0) === 0])}
/>
private routeChangeComplete = () => {
if (this.interval) {

50
src/Menu/Menu.module.styl Normal file
View File

@ -0,0 +1,50 @@
@import "../config"
.menu
&.outline
border 2px solid var(--theme-500)
&.hidden
display none
ul
padding 0
margin 0
display flex
flex-direction column
li
svg
margin-right 8px
vertical-align sub
&:last-child
margin 0
padding 8px
&.link
padding 0
overflow-x hidden
text-overflow: ellipsis;
margin-bottom 8px
color black
cursor pointer
transition all $transition
font-weight bold
border-radius 8px
&:hover
color black
background var(--theme-100)
@media (prefers-color-scheme dark)
color white
&:hover
color white
background var(--gray-700)
&.selected
&:active
background var(--theme-500)
color white
.link
padding 0
.linkInternal
padding 8px
display inline-block
width 100%
.icon
vertical-align sub

26
src/Menu/Menu.stories.tsx Normal file
View File

@ -0,0 +1,26 @@
import { Meta } from '@storybook/react/types-6-0'
import { XOctagon } from 'lucide-react'
import React from 'react'
import Component from '.'
export default {
title: 'DZEIO/Menu',
component: Component
} as Meta
const list: Component['props']['items'] = [
{value: 'Menu item 1'},
{value: 'Menu with link', icon: XOctagon, href: '/'},
{value: 'Menu item 3', icon: XOctagon},
{value: 'Menu item 4', icon: XOctagon},
{value: 'Menu item 5', selected: true, icon: XOctagon},
{value: 'Menu item 6', icon: XOctagon},
{value: 'Menu item 7', icon: XOctagon},
{value: 'Menu item 8', icon: XOctagon},
{value: 'Menu item 9', icon: XOctagon},
{value: 'Menu item 10', icon: XOctagon}
]
export const Menu = (args: any) => (
<Component outline {...args} items={list} onClick={(_, index) => list[index].selected = !list[index].selected} />
)

46
src/Menu/index.tsx Normal file
View File

@ -0,0 +1,46 @@
import React from 'react'
import Link from '../Link'
import Box from '../Box'
import { Icon } from '../interfaces'
import { buildClassName } from '../Util'
import css from './Menu.module.styl'
interface Props {
items: Array<{display?: string, value: any, selected?: boolean, icon?: Icon, href?: string}>
outline?: boolean
onClick?: (value: any, key: number) => void
className?: string
hideWhenEmpty?: boolean
}
export default class Menu extends React.Component<Props> {
public render = () => (
<Box className={buildClassName(css.menu, this.props.className, [css.outline, this.props.outline], [css.hidden, this.props.hideWhenEmpty, this.props.items.length === 0])}>
<ul>
{this.props.items.map((item, key) => {
const content = (
<>
{item.icon && (
<item.icon size="24" />
)}
{item.display ?? item.value}
</>
)
if (item.href) {
return (
<li key={key} className={buildClassName([css.selected, item.selected], css.link)}>
<Link noStyle href={item.href} className={css.linkInternal}>{content}</Link>
</li>
)
}
return (
<li key={key} className={buildClassName([css.selected, item.selected])} onClick={() => this.props.onClick?.(item.value, key)}>
{content}
</li>
)
})}
</ul>
</Box>
)
}

View File

@ -0,0 +1,44 @@
@import '../config'
$height = 76px
.body-navbar
margin-top $height
.navbar
position fixed
left 0
background var(--theme-50)
@media (prefers-color-scheme dark)
background var(--gray-800)
top 0
height $height
width 100%
z-index 100
display flex
padding 16px
> ul
.userSpaceParent ul
display flex
.navbar
ul
list-style none
margin 0
padding 0
.mobileMenu
opacity 0
transition opacity $transition
pointer-events none
&.shown
opacity 1
pointer-events initial
.header
margin-right 16px
.menu
opacity 1

View File

@ -0,0 +1,55 @@
import { Meta, Story } from '@storybook/react/types-6-0'
import React from 'react'
import { Zap, ZapOff } from 'lucide-react'
import Component from '.'
import Text from '../Text'
import Col from '../Col'
import Row from '../Row'
export default {
title: 'DZEIO/Navbar',
component: Component,
parameters: {
layout: 'fullscreen'
}
} as Meta
export const Navbar: Story<any> = (args: any) => <Component {...args} />
Navbar.args = {
logo: {src: '/90-38.svg', width: 90, height: 38},
user: {
name: 'Username',
menu: [{
path: '/logout',
value: 'Logout'
}, {
path: '/logout',
value: 'Logout'
}]
},
menu: [{
name: 'Dasboard',
icon: Zap
}, {
name: 'With Childs',
icon: Zap,
subMenu: [{
name: 'Child 1'
}, {
name: 'Child with link',
path: '/dashboard'
}]
}, {
path: '/dashboard',
name: 'Link',
icon: ZapOff
}],
children: (
<Row align='center'>
<Col><Text>Test</Text></Col>
<Col><Text>Test</Text></Col>
<Col><Text>Test</Text></Col>
<Col><Text>Test</Text></Col>
</Row>
)
}

201
src/Navbar/index.tsx Normal file
View File

@ -0,0 +1,201 @@
import Router from 'next/router'
import { ChevronDown, Menu as LucideMenu } from 'lucide-react'
import Image, { ImageProps } from 'next/image'
import React from 'react'
import Col from '../Col'
import Link from '../Link'
import Menu from '../Menu'
import Row from '../Row'
import Sidebar from '../Sidebar'
import Text from '../Text'
import { buildClassName } from '../Util'
import Button from '../Button'
import { Icon } from '../interfaces'
import css from './Navbar.module.styl'
interface MenuItem {
path?: string
icon?: Icon
name: string
subMenu?: Array<MenuItem>
mobileOnly?: boolean
}
interface Props {
/**
* Logo to display
*/
logo?: ImageProps & {height: number, width: number, name?: string}
/**
* Login URL
*/
loginUrl?: string
/**
* Login URL
*/
registerUrl?: string
/**
* User Informations if loggedin
*/
user?: {
/**
* Username
*/
name: string
/**
* User Menu
*/
menu?: Array<MenuItem>
}
/**
* Links to display
*/
menu: Array<MenuItem>
children?: React.ReactNode
}
interface State {
path?: string
short: boolean
isMobile: boolean
menuActive: boolean
subMenu?: {
x: number
menu: Menu['props']['items']
}
}
/**
* Navbar Component
* @version 1.0.4
*/
export default class Navbar extends React.Component<Props, State> {
public state: State = {
short: false,
isMobile: false,
menuActive: false
}
public componentDidMount() {
this.setState({
path: Router.asPath,
menuActive: false
})
Router.events.on('routeChangeComplete', () => {
this.setState({path: Router.asPath, menuActive: false})
})
Router.events.on('routeChangeError', () => {
this.setState({path: Router.asPath, menuActive: false})
})
document.body.classList.add(css['body-navbar'])
document.body.addEventListener('click', this.onBodyClick)
window.addEventListener('resize', this.onResize)
this.onResize()
}
public onResize = () => {
const isMobile = window.innerWidth <= 768
if (this.state.isMobile !== isMobile) {
this.setState({isMobile})
}
}
public componentWillUnmount() {
document.body.classList.remove(css['body-sidebar'])
document.body.removeEventListener('click', this.onBodyClick)
window.removeEventListener('resize', this.onResize)
}
public menuCloseCallback = () => {
this.setState({menuActive: false})
return true
}
public render = () => (
<>
<nav className={css.navbar}>
<Row nowrap className={css.header} align="center">
{this.props.logo && (
<Col className={css.imgContainer}>
<Link href="/" linkProps={{"aria-label": this.props.logo.name}}>
<Image {...this.props.logo} height={34} width={this.props.logo.width*34/this.props.logo.height} />
</Link>
</Col>
)}
</Row>
{this.props.children}
{/* Spacer */}
<div style={{flex: 1}}></div>
{/* Menu */}
{!this.state.isMobile && (
<ul>
{this.props.menu.filter((it) => !it.mobileOnly).map((item) => (
<li key={item.path}><Button type="ghost" href={item.path} icon={item.icon} onClick={item.subMenu ? this.onClick(item.subMenu) : undefined}>{item.name}</Button></li>
))}
{this.props.user && (
<li>
<Button type="ghost" iconLeft={ChevronDown} onClick={this.props.user.menu ? this.onClick(this.props.user.menu) : undefined}>{this.props.user.name}</Button>
</li>
)}
</ul>
)}
{/* Menu Icon */}
{this.state.isMobile && (
<div className={css.userSpaceParent}>
<div onClick={() => this.setState({menuActive: !this.state.menuActive})} className={css.userSpace}>
<Text>
<LucideMenu size={38} className={css.mainGradient} />
</Text>
</div>
</div>
)}
</nav>
{this.state.isMobile && (
<div className={buildClassName(css.mobileMenu, [css.shown, this.state.menuActive])}>
<Sidebar fullWidth {...this.props} onClose={this.menuCloseCallback} menu={this.props.menu} />
</div>
)}
{this.state.subMenu && (
<div style={{position: 'fixed', top: 76, right: this.state.subMenu.x, zIndex: 1}}>
<Menu className={css.menu} outline items={this.state.subMenu.menu} />
</div>
)}
</>
)
private onBodyClick = (ev: MouseEvent) => {
let target = ev.target as HTMLElement | null
do {
if (target && target.classList.contains(css.menu)) {
return
}
target = target?.parentElement as HTMLElement | null
} while (target)
this.setState({subMenu: undefined})
}
private onClick = (subMenu?: Array<MenuItem>) => (ev: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => {
ev.stopPropagation()
const x = window.innerWidth - (ev.currentTarget.offsetLeft + ev.currentTarget.offsetWidth)
if (subMenu && (!this.state.subMenu || x !== this.state.subMenu?.x)) {
this.setState({
subMenu: {
x,
menu: subMenu.map((v) => ({
display: v.name,
value: v.path,
href: v.path
}))
}
})
} else {
this.setState({subMenu: undefined})
}
}
}

View File

@ -0,0 +1,39 @@
@import '../config.styl'
.section
position fixed
bottom 0
left 0
padding 0 16px
z-index 400
max-width 25%
@media (max-width $tablet)
max-width 50%
@media (max-width $mobile)
width 100%
max-width 100%
> div
margin-bottom 16px
animation spawn 1 forwards ease-in-out .3s
&.remove
animation despawn 1 both ease-in-out .3s
.title
font-weight normal
margin 0
@keyframes spawn
from
opacity 0
transform translateY(100%)
to
opacity 1
transform translateY(0)
@keyframes despawn
from
opacity 1
transform translateY(0)
to
opacity 0
transform translateY(100%)

View File

@ -0,0 +1,18 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import { Zap } from 'lucide-react'
import Component from '.'
export default {
title: 'DZEIO/NotificationManager',
component: Component
} as Meta
export const NotificationManager = (args: any) => <Component {...args} />
NotificationManager.args = {
ttl: 999999999999,
notifications: [
'Test',
'LArge text lorem ipsum dolor sit amet, i dont know what to type yolo :D'
]
}

View File

@ -0,0 +1,175 @@
import { buildClassName } from '../Util'
import Button from '../Button'
import Box from '../Box'
import Text from '../Text'
import Router from 'next/router'
import React from 'react'
import { X } from 'lucide-react'
import css from './NotificationManager.module.styl'
export interface Notification {
message: string
actions?: Array<{
txt: string
action: (this: HTMLInputElement, event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
}>
ttl?: number
internal?: {
timeRemaining: number
}
}
interface Props {
manageRoutes?: boolean
ttl?: number
notifications?: Array<string | Notification>
}
interface State {
notifications: Array<Notification | undefined>
}
export default class NotificationManager extends React.Component<Props, State> {
private static instance: NotificationManager
public state: State = {
notifications: []
}
private interval?: NodeJS.Timeout
private freezedNotification?: number
public constructor(props: Props | Readonly<Props>) {
super(props)
NotificationManager.instance = this
}
public static addNotification(notif: Omit<Notification, 'internal'> | string): number {
const realNotif: Notification = typeof notif === 'string' ? { message: notif, ttl: this.instance.props.ttl ?? 2000 } : notif
if (realNotif.ttl) {
realNotif.ttl /= 100
realNotif.internal = { timeRemaining: realNotif.ttl }
}
const notifs = this.instance.state.notifications
const id = notifs.push(realNotif) - 1
this.instance.setState({
notifications: notifs
})
return id
}
public static removeNotification(id: number, array?: Array<Notification|undefined>) {
const notifs = array || this.instance.state.notifications
notifs[id] = undefined
if (array) {
return
}
this.instance.setState({
notifications: notifs
})
}
public componentDidMount() {
if (this.props.notifications) {
for (const notif of this.props.notifications) {
NotificationManager.addNotification(notif)
}
}
if (this.props.manageRoutes) {
Router.events.on('routeChangeComplete', this.checkRouteForMessage)
this.checkRouteForMessage()
}
}
public componentDidUpdate() {
if (this.state.notifications.length > 0 && !this.interval) {
this.interval = setInterval(() => {
const notifs = this.state.notifications
for (let id = 0; id < notifs.length; id++) {
const notif = notifs[id]
if (this.freezedNotification === id && notif?.ttl) {
notif.internal = {timeRemaining: notif.ttl}
}
if (!notif || typeof notif.internal?.timeRemaining !== 'number' || id === this.freezedNotification) {
continue
}
if (notif.internal.timeRemaining < 1) {
NotificationManager.removeNotification(id)
}
notif.internal.timeRemaining -= 1
}
this.setState({
notifications: notifs
})
}, 100)
}
if (this.state.notifications.length === 0 && this.interval) {
clearInterval(this.interval)
this.interval = undefined
}
}
public componentWillUnMount() {
if (this.interval) {
clearInterval(this.interval)
}
}
public render = () => (
<section className={css.section}>
{this.state.notifications.map((el, index) => {
if (el === undefined) {
return
}
return (
<div
key={index}
onMouseEnter={this.onMouseEnter(index)}
onMouseLeave={this.onMouseExit(index)}
className={buildClassName([css.remove, typeof el.internal?.timeRemaining === 'number' && el.internal.timeRemaining <= 3])}
>
<Box
title={el.message}
rightHeader={(
<Text><X onClick={() => NotificationManager.removeNotification(index)} /></Text>
)}
>
{el.actions && (
<div>
{el.actions.map((btn, aIndex) => (
<Button
onClick={btn.action as unknown as ((event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => void)}
key={aIndex}
>{btn.txt}</Button>
))}
</div>
)}
</Box>
</div>
)
})}
</section>
)
private onMouseEnter = (id: number) => () => {
this.freezedNotification = id
}
private onMouseExit = (id: number) => () => {
if (this.freezedNotification === id) {
this.freezedNotification = undefined
}
}
private checkRouteForMessage = () => {
const msg = Router.query.msg
console.log(msg)
if (typeof msg === 'string') {
NotificationManager.addNotification(decodeURI(msg))
}
}
}

View File

@ -5,10 +5,11 @@
height 100%
width 100%
top 0
margin 0 !important // sorry :(
left 0
background rgba($backgroundLight, .7)
background nativeRGBA(var(--gray-100), .7)
@media (prefers-color-scheme dark)
background rgba($backgroundDark, .7)
background nativeRGBA(var(--gray-500), .7)
cursor pointer
z-index 200
animation fadeIn .3s ease-in-out 1 forwards

View File

@ -1,10 +1,13 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
import Text from '../Text'
export default {
title: 'DZEIO/Button',
title: 'DZEIO/Popup',
component: Component
} as Meta
export const Basic = (args: any) => <Component {...args}>Button</Component>
export const Popup = (args: any) => (
<Component><Text>Test</Text></Component>
)

View File

@ -1,23 +1,29 @@
import React from 'react'
import { X } from 'react-feather'
import { X } from 'lucide-react'
import Text from '../Text'
import Box from '../Box'
import Row from '../Row'
import { Props as HeaderProps } from '../Box/BoxHeader'
import css from './Popup.module.styl'
interface Props {
children: React.ReactNode
onClose?: () => void
header?: HeaderProps
title?: string
}
export default class Popup extends React.Component<Props> {
public render = () => (
<Row nomargin onClick={this.parentClose} justify="center" align="center" className={css.popup}>
<Box {...this.props.header} className={css.popupChild} onClick={(ev) => ev.stopPropagation()} headerButtons={(<Text><X onClick={this.props.onClose} className={css.exit} /></Text>)}>
<Row onClick={this.parentClose} justify="center" align="center" className={css.popup}>
<Box
title={this.props.title}
className={css.popupChild}
onClick={(ev) => ev.stopPropagation()}
rightHeader={
(<Text><X onClick={this.props.onClose} className={css.exit} /></Text>)
}
>
{this.props.children}
</Box>
</Row>

View File

@ -0,0 +1,25 @@
@import '../config'
.bar
width 100%
background nativeRGBA(var(--theme-500), 0.15)
height 8px
border-radius 8px
&.noBorder
border-radius 0
div
border-radius 0px 8px 8px 0px
&[style="width: 100%;"]
border-radius 0
div
transition width,border-radius
transition-duration $transitionTime
transition-timing-function $transitionFunction
height 100%
max-width 100%
width 0
background var(--theme-500)
border-radius 8px

View File

@ -0,0 +1,19 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
export default {
title: 'DZEIO/Progress Bar',
component: Component,
argTypes: {
progress: { control: 'number', defaultValue: 0},
noRoundBorders: { control: 'boolean'},
},
parameters: {
layout: 'fullscreen'
}
} as Meta
export const ProgressBar = (args: any) => (
<Component {...args} />
)

29
src/ProgressBar/index.tsx Normal file
View File

@ -0,0 +1,29 @@
import React from 'react'
import { buildClassName } from '../Util'
import css from './ProgressBar.module.styl'
interface Props {
/**
* Number between 0 and 100%
*/
progress: number
/**
* disable the round borders
*/
noRoundBorders?: boolean
className?: string
}
/**
* Display a simple customizable Progress bar
*
* @version 1.0.0
*/
export default class extends React.Component<Props> {
public render = () => (
<div className={buildClassName(css.bar, [css.noBorder, this.props.noRoundBorders], this.props.className)}>
<div style={{ width: `${this.props.progress}%`}}></div>
</div>
)
}

View File

@ -0,0 +1,4 @@
@import '../config'
.padding
padding 24px

View File

@ -0,0 +1,20 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
export default {
title: 'DZEIO/Progress Box',
component: Component,
argTypes: {
progress: { control: 'number', defaultValue: 0},
text: { control: 'text'},
textProgress: { control: 'text'},
},
parameters: {
layout: 'fullscreen'
}
} as Meta
export const ProgressBox = (args: any) => (
<Component {...args} />
)

49
src/ProgressBox/index.tsx Normal file
View File

@ -0,0 +1,49 @@
import React from 'react'
import Box from '../Box'
import Col from '../Col'
import ProgressBar from '../ProgressBar'
import Row from '../Row'
import Text from '../Text'
import css from './ProgressBox.module.styl'
interface Props {
/**
* Number between 0 and 100%
*/
progress: number
/**
* Text displayed in the middle
*/
text: string
/**
* text displayed in the right of the box
*/
textProgress: string
}
/**
* Display a simple Progress box that can be used for multiple things
*
* @version 1.0.0
*/
export default class extends React.Component<Props> {
public render = () => (
<Box noPadding noBottomBorder>
<Row className={css.padding}>
<Col nogrow>
<Text color="main">{this.props.progress}%</Text>
</Col>
<Col>
<Text weight="bold" align="center">{this.props.text}</Text>
</Col>
<Col nogrow>
<Text color="main">{this.props.textProgress}</Text>
</Col>
</Row>
<ProgressBar noRoundBorders progress={this.props.progress} />
</Box>
)
}

View File

@ -3,18 +3,16 @@
.row
display flex
flex-wrap wrap
margin (0 - $gapSize) 0 0 (0 - $gapSize)
padding $gapSize
&.nomargin
.row
padding 0
&:not(.nogap)
gap $gapSize
.nowrap
flex-wrap nowrap
.nogrow > *
max-width initial
flex-grow 0
flex-basis initial
for dir in 'row-reverse' 'column' 'column-reverse'
.direction-{dir}

View File

@ -12,7 +12,7 @@ interface Props {
nowrap?: boolean
nogrow?: boolean
className?: string
nomargin?: boolean
nogap?: boolean
onClick?: (ev: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
}
@ -28,8 +28,8 @@ export default class Row extends React.Component<Props> {
[css[`align-${this.props.align}`], this.props.align],
[css.nowrap, this.props.nowrap],
[css.nogrow, this.props.nogrow],
this.props.className,
[css.nomargin, this.props.nomargin]
[css.nogap, this.props.nogap],
this.props.className
)}
onClick={this.props.onClick}
>

11
src/Scrollbar.stories.tsx Normal file
View File

@ -0,0 +1,11 @@
import { Meta, Story } from '@storybook/react/types-6-0'
import React from 'react'
export default {
title: 'DZEIO/Scrollbar',
parameters: {
layout: 'fullscreen'
}
} as Meta
export const Scrollbar: Story<any> = (args: any) => <div style={{height: '1000vh'}} />

View File

@ -0,0 +1,194 @@
@import '../config'
// $transition = 10s linear
// $transitionTime = 10s
// $transitionFunction = linear
.sidebarBody
margin-left 300px
transition margin-left $transition
&.short
margin-left 56px
.sidebar
background var(--theme-50)
@media (prefers-color-scheme dark)
background var(--gray-800)
position fixed
left 0
top 0
padding 24px
height 100vh
width 300px
&.fullWidth
width 100%
z-index 100
display flex
flex-direction column
transition width $transition
.header
.userSpace
.header .imgContainer
> ul span
// transition all $transition
transition-property width, padding, margin, max-width
transition-duration $transitionTime
transition-timing-function $transitionFunction
overflow hidden
> ul span
width calc(100% - 40px)
max-width 100%
.header p
cursor pointer
.userSpace
cursor pointer
div, p
transition all $transition
p
overflow hidden
white-space nowrap
>div:last-child p
padding 8px 8px 4px
border-radius 8px
&:hover
color black
background var(--theme-100)
@media (prefers-color-scheme dark)
color white
background var(--gray-700)
&:active
color white
background var(--theme-500)
&.short
width 88px
.userSpace > div:not(:last-child)
.header > div:first-child
width 0
max-width 0
padding-left 0
.header
margin-left -16px
> div:first-child
padding-left 0 !important
.header > div svg
.header .imgContainer
> ul
span
svg:last-child
width 0
padding-left 0
padding-right 0
margin 0 !important
max-width 0
svg line:first-child
transform rotateX(0) !important
.header
margin-left -8px
//min-height 70px
svg
margin-right 8px
line:first-child
transition transform $transition
transform rotateZ(90deg)
min-height 24px
hr
margin 0
ul ul
padding-left 40px
max-height 0
opacity 0
overflow hidden
transition all $transition
position relative
&::before
content " "
position absolute
background black
@media (prefers-color-scheme dark)
background white
border-radius 2px
width 2px
height 100%
margin-top 8px
left 18px
li.activeMenu
svg:last-child
transform rotateX(180deg)
ul
opacity 1
// not the best but IDK what is better
max-height 100vh
ul li
margin-top 8px
div
width 100%
cursor pointer
border-radius 8px
transition all $transition
color black
@media (prefers-color-scheme dark)
color white
&:first-child
margin-top 0
&:hover
color black
background var(--theme-100)
@media (prefers-color-scheme dark)
color white
background var(--gray-700)
&:active
color white
background var(--theme-500)
padding 8px
display flex
align-items center
z-index 111
position relative
svg
transition color $transition, transform $transition
&:first-child + span
margin-left 16px
span
display inline-block
white-space nowrap
a
width 100%
display flex
//max-height 24px
&.link
padding 0
a
padding 8px
&.active > div
color white
background var(--theme-500)
ul
list-style none
margin 0
padding 0
.userMenu
position fixed
bottom 16px
left 316px
z-index 200
&.short
left 104px
&.fullWidth
left 16px
width calc(100% - 32px)

View File

@ -0,0 +1,54 @@
import React from 'react'
import { Meta, Story } from '@storybook/react/types-6-0'
import { Zap, ZapOff } from 'lucide-react'
import Component from '.'
export default {
title: 'DZEIO/Sidebar',
component: Component,
parameters: {
layout: 'fullscreen'
}
} as Meta
export const Sidebar: Story<any> = (args: any) => <Component {...args} />
Sidebar.args = {
logo: {src: '/90-38.svg', width: 90, height: 38},
user: {
picture: '/16-16.svg',
name: 'Username',
menu: [{
path: '/logout',
name: 'Logout'
}, {
path: '/mock-route',
name: 'Mock Route'
}]
},
menu: [{
name: 'Dasboard',
icon: Zap
}, {
name: 'With Childs',
icon: Zap,
subMenu: [{
name: 'Child 1'
}, {
name: 'Mock Route',
path: '/mock-route'
}]
}, {
name: 'With Childs2',
icon: Zap,
subMenu: [{
name: 'Child 1'
}, {
name: 'Child with link',
path: '/mock-route2'
}]
}, {
path: '/dashboard',
name: 'Link',
icon: ZapOff
}],
// fullWidth: true
}

248
src/Sidebar/index.tsx Normal file
View File

@ -0,0 +1,248 @@
import React, { MouseEvent } from 'react'
import Router from 'next/router'
import { ChevronDown, MoreHorizontal, Plus } from 'lucide-react'
import Text from '../Text'
import Col from '../Col'
import Row from '../Row'
import Link from '../Link'
import Image from '../Image'
import { buildClassName } from '../Util'
import css from './Sidebar.module.styl'
import { Icon } from '../interfaces'
import Menu from '../Menu'
interface MenuItem {
path?: string
icon?: Icon
name: string
}
interface Props {
/**
* Logo to display
*/
logo?: Image['props']['imageProps'] & {height: number, width: number}
/**
* User Informations if loggedin
*/
user?: {
picture?: string
/**
* Username
*/
name: string
/**
* User Menu
*/
menu?: Array<MenuItem>
}
/**
* Links to display
*/
menu: Array<MenuItem & {subMenu?: Array<MenuItem>}>
onClose?: () => boolean
fullWidth?: boolean
}
interface State {
path?: string
/**
* Define if the menu is open or closed
*
* in mobile it will be hidden when closed
*/
open: boolean
activeMenu?: string
userMenu?: boolean
subMenu?: {
y: number
menu: Menu['props']['items']
}
}
/**
* Sidebar Component
* @version 1.0.0
*/
export default class Sidebar extends React.Component<Props, State> {
public state: State = {
open: true
}
public componentDidMount() {
this.onRouteChange(Router.asPath)
Router.events.on('routeChangeComplete', () => {
this.onRouteChange(Router.asPath)
})
Router.events.on('routeChangeError', () => {
this.onRouteChange(Router.asPath)
})
if (!this.props.fullWidth) {
document.body.classList.add(css.sidebarBody)
}
document.body.addEventListener('click', this.onBodyClick)
}
private onRouteChange(newRoute: string) {
let activeMenu = undefined
for (const menu of this.props.menu) {
if (newRoute === menu.path || menu.subMenu?.find((it) => newRoute === it.path)) {
activeMenu = menu.name + (menu.path ?? '')
}
}
this.setState({path: newRoute, subMenu: undefined, userMenu: false, activeMenu})
}
public componentDidUpdate() {
//console.log(this.state.path)
if (this.state.open) {
document.body.classList.remove(css.short)
} else {
document.body.classList.add(css.short)
}
}
public componentWillUnmount() {
document.body.classList.remove(css.short, css.sidebarBody)
document.body.removeEventListener('click', this.onBodyClick)
}
private onBodyClick = () => {
this.setState({subMenu: undefined, userMenu: false})
}
public onClick = (id: string, subMenu?: Array<MenuItem>) => (ev: MouseEvent) => {
ev.stopPropagation()
if (!this.state.open && subMenu) {
//console.log(ev)
this.setState({
subMenu: {
y: (ev.currentTarget as HTMLElement).offsetTop,
menu: subMenu.map((v) => ({
display: v.name,
value: v.path,
href: v.path,
selected: this.state.path === v.path
}))
}
})
} else {
this.setState({activeMenu: this.state.activeMenu === id ? undefined : id, subMenu: undefined})
}
}
public render = () => (
<>
<nav className={buildClassName(
css.sidebar,
[css.short, !this.state.open],
[css.fullWidth, this.props.fullWidth]
)}>
<Row nowrap justify="space-between" className={css.header} align="center">
<Col>
{this.props.logo && (
<Link href="/">
<Image imageProps={{ ...this.props.logo, height: 34, width: this.props.logo.width * 34 / this.props.logo.height }} />
</Link>
)}
</Col>
<Col nogrow><Text tag="div">
<Plus size={24} onClick={this.onCloseOpenClick} />
</Text></Col>
</Row>
<ul>
{this.props.menu.map((item) => this.makeMenuItem(item))}
</ul>
<div style={{flex: 1}}></div>
{/* Spacer */}
{this.props.user && (
<Row className={css.userSpace} align="center" onClick={(ev) => {ev.stopPropagation(); this.setState({userMenu: !this.state.userMenu})}}>
{this.props.user.picture && (
<Col nogrow><Image imageProps={{ src: this.props.user.picture, width: 38, height: 38 }} /></Col>
)}
<Col><Text>{this.props.user.name}</Text></Col>
<Col nogrow><Text><MoreHorizontal size={24} /></Text></Col>
</Row>
)}
</nav>
{this.props.user?.menu && this.state.userMenu && (
<div className={buildClassName(css.userMenu, [css.fullWidth, this.props.fullWidth], [css.short, !this.state.open])}>
<Menu onClick={this.onMenuClick} outline items={this.props.user.menu.map((v) => ({
display: v.name,
value: v.path,
href: v.path,
selected: this.state.path === v.path
}))} />
</div>
)}
{this.state.subMenu && (
<div style={{position: 'absolute', top: this.state.subMenu.y, left: this.state.open ? 316 : 104}}>
<Menu onClick={this.onMenuClick} outline items={this.state.subMenu.menu} />
</div>
)}
</>
)
private onCloseOpenClick = () => {
let willBeOpen = !this.state.open
if (this.props.onClose && !willBeOpen) {
willBeOpen = this.props.onClose()
}
this.setState(!willBeOpen ? {open: false, activeMenu: undefined} : {open: true})
}
private onMenuClick = (value?: string) => {
this.setState({userMenu: false, subMenu: undefined})
if (value) {
Router.push(value)
}
}
private makeMenuItem(obj: MenuItem & {subMenu?: Array<MenuItem>}, isSub = false) {
const id = obj.name + (obj.path ?? '')
const content = (
<>
{obj.icon && (
<obj.icon size={24} />
)}
<Text color="none" weight="bold" tag="span">
{obj.name}
</Text>
{obj.subMenu && (
<ChevronDown size={24} />
)}
</>
)
const isActive = this.state.path === obj.path || obj.subMenu?.find((it) => this.state.path === it.path)
return <li
key={id}
className={buildClassName(
[css.active, isActive],
[css.activeMenu, id === this.state.activeMenu && this.state.open]
)}
onClick={isSub ? undefined : this.onClick(id, obj.subMenu)}
>
<div
className={buildClassName([css.link, obj.path])}
>
{obj.path ? (
<Link noStyle href={obj.path}>
{content}
</Link>
) : content}
</div>
{obj.subMenu && (
<ul>
{obj.subMenu.map((it) => this.makeMenuItem(it, true))}
</ul>
)}
</li>
}
}

View File

@ -0,0 +1,29 @@
@import '../config'
.table
border-spacing 0
width 100%
tr:not(:last-child) td
border-bottom 1px solid var(--gray-600)
tr th
border-bottom 2px solid var(--gray-600)
tr td:not(:first-child)
tr th:not(:first-child)
border-left 1px solid var(--gray-600)
th
font-weight bold
font-size rem(18)
th
td
padding 16px
text-align left
color black
@media (prefers-color-scheme dark)
color white
.parent
overflow-x auto

View File

@ -0,0 +1,49 @@
import { Meta } from '@storybook/react/types-6-0'
import { Settings } from 'lucide-react'
import React from 'react'
import Component from '.'
import Box from '../Box'
export default {
title: 'DZEIO/Table',
component: Component
} as Meta
export const Table = (args: any) => (
<Box icon={Settings} title="Table">
<Component {...args}>
<thead>
<tr>
<th>item1</th>
<th>item1</th>
<th>item1</th>
<th>item1</th>
<th>item1</th>
</tr>
</thead>
<tbody>
<tr>
<td>item1</td>
<td>item1</td>
<td>item1</td>
<td>item1</td>
<td>item1</td>
</tr>
<tr>
<td>item1</td>
<td>item1</td>
<td>item1</td>
<td>item1</td>
<td>item1</td>
</tr>
<tr>
<td>item1</td>
<td>item1</td>
<td>item1</td>
<td>item1</td>
<td>item1</td>
</tr>
</tbody>
</Component>
</Box>
)

30
src/Table/index.tsx Normal file
View File

@ -0,0 +1,30 @@
import React from 'react'
import { buildClassName } from '../Util'
import css from './Table.module.styl'
interface Props {
children: React.ReactNode
parentClassName?: string
className?: string
horizontalBorders?: boolean
verticalBorders?: boolean
}
export default class Table extends React.Component<Props> {
public render = () => (
<div className={buildClassName(
css.parent,
this.props.parentClassName
)}>
<table className={buildClassName(
css.table,
this.props.className,
[css.horizontalBorders, this.props.horizontalBorders],
[css.verticalBorders, this.props.verticalBorders]
)}>{this.props.children}</table>
</div>
)
}

44
src/Text/Text.module.styl Normal file
View File

@ -0,0 +1,44 @@
@import "../config"
.text
margin 0
font-size rem(16)
font-weight normal
white-space pre-wrap
+ .text
margin-top 8px
.white
color white
.black
color black
.main
color var(--theme-500)
for size in 36 28 24 20 18 16 14
.size-{size}
font-size rem(size)
for weight in 'normal' 'bold'
.weight-{weight}
font-weight unquote(weight)
.weight-light
font-weight 300
.align-center
text-align center
.align-right
text-align right
.align-left
text-align left
@media (prefers-color-scheme dark)
.white:not(.noDarkTheme)
color black
.black:not(.noDarkTheme)
color white

15
src/Text/Text.stories.tsx Normal file
View File

@ -0,0 +1,15 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
export default {
title: 'DZEIO/Text',
component: Component
} as Meta
export const Text = (args: any) => (
<>
<Component {...args}>TExt</Component>
<Component {...args}>TExt</Component>
</>
)

93
src/Text/index.tsx Normal file
View File

@ -0,0 +1,93 @@
import React from 'react'
import { buildClassName } from '../Util'
import css from './Text.module.styl'
type Types = 'hero' | 'h1' | 'h2' | 'h3' | 'h4' | 'text' | 'light' | 'bold'
interface Props {
color?: 'black' | 'white' | 'none' | 'main'
type?: Types
tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'em' | 'span' | 'div'
weight?: 'normal' | 'bold' | 'light'
size?: 36 | 28 | 24 | 20 | 18 | 16 | 14
className?: string
noDarkTheme?: boolean
align?: 'left' | 'right' | 'center'
children: React.ReactNode
textProps?: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLElement>, HTMLElement>
}
const types: Record<Types, {
tag?: Props['tag']
size?: Props['size']
weight?: Props['weight']
}> = {
hero: {
size: 36,
weight: 'bold'
},
h1: {
size: 28,
weight: 'bold',
tag: 'h1'
},
h2: {
size: 24,
weight: 'bold',
tag: 'h2'
},
h3: {
size: 20,
weight: 'bold',
tag: 'h3'
},
h4: {
size: 18,
weight: 'bold',
tag: 'h4'
},
text: {},
bold: {
weight: 'bold'
},
light: {
weight: 'light',
size: 14
}
}
/**
* Display Text lol
*
* @version 1.0.1
*/
export default class Text extends React.Component<Props> {
public render() {
let data: { weight: Props['weight'], size: Props['size'], tag?: Props['tag'] } = Object.assign({
size: 16,
weight: 'normal'
}, this.props.type ? types[this.props.type] : {})
if (this.props.size) {
data.size = this.props.size
}
if (this.props.weight) {
data.weight = this.props.weight
}
const classes = buildClassName(
css.text,
css[this.props.color ?? 'black'],
[css[`weight-${data.weight}`], data.weight !== 'normal'],
[css[`size-${data.size}`], data.size !== 16],
[css.noDarkTheme, this.props.noDarkTheme],
[css[`align-${this.props.align}`], this.props.align],
this.props.className
)
if (this.props.tag === 'em') {
return <p className={classes}><em>{this.props.children}</em></p>
}
return React.createElement(this.props.tag ?? data.tag ?? 'p', {...this.props.textProps, className: classes, children: this.props.children})
}
}

View File

@ -1,4 +1,4 @@
export function buildClassName(...classes: Array<Array<any> | string | undefined>): string|undefined {
export function buildClassName(...classes: Array<Array<any> | string | undefined>): string | undefined {
const classesFinal: Array<string> = []
root: for (const classe of classes) {
if (typeof classe === 'undefined') {

View File

@ -1,3 +1,6 @@
// not used anymore
// kept if it is used again
$path = "/assets/fonts/aileron"
@font-face

355
src/colors.styl Normal file
View File

@ -0,0 +1,355 @@
// quick function to transform hex to rgb
hextorgb(color)
red(color) unquote(', ') green(color) unquote(', ') blue(color)
generateTheme($name, c50, c100, c200, c300, c400, c500, c600, c700, c800, c900)
{"--" + $name + "-50"}: c50
{"--" + $name + "-100"}: c100
{"--" + $name + "-200"}: c200
{"--" + $name + "-300"}: c300
{"--" + $name + "-400"}: c400
{"--" + $name + "-500"}: c500
{"--" + $name + "-600"}: c600
{"--" + $name + "-700"}: c700
{"--" + $name + "-800"}: c800
{"--" + $name + "-900"}: c900
{"--" + $name + "-50-rgb"}: hextorgb(c50)
{"--" + $name + "-100-rgb"}: hextorgb(c100)
{"--" + $name + "-200-rgb"}: hextorgb(c200)
{"--" + $name + "-300-rgb"}: hextorgb(c300)
{"--" + $name + "-400-rgb"}: hextorgb(c400)
{"--" + $name + "-500-rgb"}: hextorgb(c500)
{"--" + $name + "-600-rgb"}: hextorgb(c600)
{"--" + $name + "-700-rgb"}: hextorgb(c700)
{"--" + $name + "-800-rgb"}: hextorgb(c800)
{"--" + $name + "-900-rgb"}: hextorgb(c900)
{"--" + $name + "-50-text"}: light(c50) ? black : white
{"--" + $name + "-100-text"}: light(c100) ? black : white
{"--" + $name + "-200-text"}: light(c200) ? black : white
{"--" + $name + "-300-text"}: light(c300) ? black : white
{"--" + $name + "-400-text"}: light(c400) ? black : white
{"--" + $name + "-500-text"}: light(c500) ? black : white
{"--" + $name + "-600-text"}: light(c600) ? black : white
{"--" + $name + "-700-text"}: light(c700) ? black : white
{"--" + $name + "-800-text"}: light(c800) ? black : white
{"--" + $name + "-900-text"}: light(c900) ? black : white
:root
// gray is not a theme
generateTheme(
'gray',
#FAFAFA,
#F5F5F5,
#EEEEEE,
#E0E0E0,
#BDBDBD,
#9E9E9E,
#757575,
#616161,
#424242,
#212121
)
:root
:root.theme-base-red
.red
.red *
generateTheme(
'theme',
#FFEBEE,
#FFCDD2,
#EF9A9A,
#E57373,
#EF5350,
#F44336,
#E53935,
#D32F2F,
#C62828,
#D50000
)
:root.theme-base-pink
.pink
.pink *
generateTheme(
'theme',
#FCE4EC,
#F8BBD0,
#F48FB1,
#F06292,
#EC407A,
#E91E63,
#D81B60,
#C2185B,
#AD1457,
#880E4F
)
:root.theme-base-purple
.purple
.purple *
generateTheme(
'theme',
#F3E5F5,
#E1BEE7,
#CE93D8,
#BA68C8,
#AB47BC,
#9C27B0,
#8E24AA,
#7B1FA2,
#6A1B9A,
#4A148C
)
.deep-purple
.deep-purple *
generateTheme(
'theme',
#EDE7F6,
#D1C4E9,
#B39DDB,
#9575CD,
#7E57C2,
#673AB7,
#5E35B1,
#512DA8,
#4527A0,
#311B92
)
:root.theme-base-indigo
.indigo
.indigo *
generateTheme(
'theme',
#E8EAF6,
#C5CAE9,
#9FA8DA,
#7986CB,
#5C6BC0,
#3F51B5,
#3949AB,
#303F9F,
#283593,
#1A237E
)
:root.theme-base-blue
.blue
.blue *
generateTheme(
'theme',
#E3F2FD,
#BBDEFB,
#90CAF9,
#64B5F6,
#42A5F5,
#2196F3,
#1E88E5,
#1976D2,
#1565C0,
#0D47A1
)
.light-blue
.light-blue *
generateTheme(
'theme',
#E1F5FE,
#B3E5FC,
#81D4FA,
#4FC3F7,
#29B6F6,
#03A9F4,
#039BE5,
#0288D1,
#0277BD,
#01579B
)
:root.theme-base-cyan
.cyan
.cyan *
generateTheme(
'theme',
#E0F7FA,
#B2EBF2,
#80DEEA,
#4DD0E1,
#26C6DA,
#00BCD4,
#00ACC1,
#0097A7,
#00838F,
#006064
)
:root.theme-base-teal
.teal
.teal *
generateTheme(
'theme',
#E0F2F1,
#B2DFDB,
#80CBC4,
#4DB6AC,
#26A69A,
#009688,
#00897B,
#00796B,
#00695C,
#004D40
)
:root.theme-base-green
.green
.green *
generateTheme(
'theme',
#E8F5E9,
#C8E6C9,
#A5D6A7,
#81C784,
#66BB6A,
#4CAF50,
#43A047,
#388E3C,
#2E7D32,
#1B5E20
)
:root.theme-base-light-green
.light-green
.light-green *
generateTheme(
'theme',
#F1F8E9,
#DCEDC8,
#C5E1A5,
#AED581,
#9CCC65,
#8BC34A,
#7CB342,
#689F38,
#558B2F,
#33691E
)
:root.theme-base-lime
.lime
.lime *
generateTheme(
'theme',
#F9FBE7,
#F0F4C3,
#E6EE9C,
#DCE775,
#D4E157,
#CDDC39,
#C0CA33,
#AFB42B,
#9E9D24,
#827717
)
:root.theme-base-yellow
.yellow
.yellow *
generateTheme(
'theme',
#FFFDE7,
#FFF9C4,
#FFF59D,
#FFF176,
#FFEE58,
#FFEB3B,
#FDD835,
#FBC02D,
#F9A825,
#F57F17
)
:root.theme-base-amber
.amber
.amber *
generateTheme(
'theme',
#FFF8E1,
#FFECB3,
#FFE082,
#FFD54F,
#FFCA28,
#FFC107,
#FFB300,
#FFA000,
#FF8F00,
#FF6F00
)
:root.theme-base-orange
.orange
.orange *
generateTheme(
'theme',
#FFF3E0,
#FFE0B2,
#FFCC80,
#FFB74D,
#FFA726,
#FF9800,
#FB8C00,
#F57C00,
#EF6C00,
#E65100
)
:root.theme-base-deep-orange
.deep-orange
.deep-orange *
generateTheme(
'theme',
#FBE9E7,
#FFCCBC,
#FFAB91,
#FF8A65,
#FF7043,
#FF5722,
#F4511E,
#E64A19,
#D84315,
#BF360C
)
:root.theme-base-brown
.brown
.brown *
generateTheme(
'theme',
#EFEBE9,
#D7CCC8,
#BCAAA4,
#A1887F,
#8D6E63,
#795548,
#6D4C41,
#5D4037,
#4E342E,
#3E2723
)
:root.theme-base-blue-gray
.blue-gray
.blue-gray *
generateTheme(
'theme',
#ECEFF1,
#CFD8DC,
#B0BEC5,
#90A4AE,
#78909C,
#607D8B,
#546E7A,
#455A64,
#37474F,
#263238
)
:root.theme-base-gray
.gray
.gray *
generateTheme(
'theme',
#FAFAFA,
#F5F5F5,
#EEEEEE,
#E0E0E0,
#BDBDBD,
#9E9E9E,
#757575,
#616161,
#424242,
#212121
)

26
src/config.styl Normal file
View File

@ -0,0 +1,26 @@
// native RGBA function to replace Stylus rgba function
nativeRGBA(color, opacity)
unquote('rgba(') color unquote(', ') opacity unquote(');')
nativeRGB(color)
unquote('rgb(') color unquote(');')
$transitionTime = .15s
$transitionFunction = ease-in-out
$transition = $transitionTime $transitionFunction
// Breakpoints
$mobile = 768px
$tablet = 1200px
// Row/Col
$totalGapSize = 10%
$colCount = 12
$colCountTablet = 8
$colCountMobile = 4
$gapSize = 16px
rem($a)
($a / 16)rem

View File

@ -1,2 +0,0 @@
.body
padding 0 16px 16px

View File

@ -1,18 +0,0 @@
import React from 'react'
import css from './BoxBody.module.styl'
import { buildClassName } from '../../Util'
interface Props {
noPadding?: boolean
}
export default class BoxBody extends React.Component<Props> {
public render = () => (
<div className={buildClassName([css.body, !this.props.noPadding])}>
{this.props.children}
</div>
)
}

View File

@ -1,13 +0,0 @@
@import "../../config.styl"
.header
padding 16px
.title
font-weight bold
font-size rem(18)
margin 0 0 8px
.subtitle
font-size rem(16)
margin 0

Some files were not shown because too many files have changed in this diff Show More