Compare commits

..

104 Commits

Author SHA1 Message Date
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
7000d3ebac v0.8.2 2021-04-04 18:50:11 +02:00
3bde426325 Fixed Checkbox
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-04 18:47:49 +02:00
b13a40ef18 Fixed Color changing while input is disabled
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-02 00:50:34 +02:00
e481160528 Fixed padding being added to block Inputs
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-02 00:48:20 +02:00
a0342050db Removed Margin from the UserMenu if it's the Navbar
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-01 23:44:44 +02:00
c1f37b6e7c body will not display if there is no body to display
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-01 23:41:40 +02:00
d2fbe42db8 Updated Navbar storybook to include an Image
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-04-01 23:34:37 +02:00
031971b859 v0.8.1 2021-04-01 23:34:02 +02:00
1488386e1d Merge branch 'master' of github.com:dzeiocom/components 2021-04-01 20:43:51 +02:00
26c2c50f23 v0.8.0 2021-04-01 10:19:52 +02:00
c591c2f0a8 Made Loader Stable
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-04-01 10:19:32 +02:00
7fe816c997 v0.7.6 2021-04-01 10:09:59 +02:00
7e28baf2aa Fixed Text
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-04-01 10:09:26 +02:00
b19992b57c v0.7.5 2021-04-01 09:57:32 +02:00
0bb84f9946 Fixed rollup config
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-04-01 09:57:08 +02:00
8cddeab594 v0.7.4 2021-03-31 14:40:22 +02:00
68939d8390 Fixed External link
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-31 14:38:33 +02:00
8186284ded GradientBackground is now stable
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-31 13:51:04 +02:00
e173bd2a37 Changed generated classes names to be shorter
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-31 10:48:48 +02:00
754da58a68 v0.7.3 2021-03-31 10:41:12 +02:00
3b314d5307 Fixed missing global file
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-31 10:40:45 +02:00
4a55f00ff0 v0.7.2 2021-03-31 10:17:23 +02:00
d30ae95a0d Fixed everything :D
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-31 10:16:46 +02:00
264e9a4cad v0.7.1 2021-03-31 09:55:47 +02:00
6a8fb50b0e Made Logo optionnal
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-31 09:54:20 +02:00
8c64a57a16 Merge branch 'master' of github.com:dzeiocom/components
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-03-30 21:33:54 +02:00
d2b5181a60 v0.7.0 2021-03-30 17:04:37 +02:00
95657b147a Finalised Navbar
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-30 17:04:16 +02:00
2bfcc6f4ad Misc changes to Link
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-30 16:59:04 +02:00
40ce098d63 Removed SidebarContainer
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-30 16:57:57 +02:00
b35ab42cc5 v0.6.4 2021-03-30 15:43:02 +02:00
c7487f27b8 Finally Fixed normaly
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-30 15:42:46 +02:00
dd179edc2a v0.6.3 2021-03-30 15:38:45 +02:00
c6436d0914 Try to fix compilation errors
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-30 15:38:29 +02:00
847f7f2649 v0.6.2 2021-03-30 15:34:33 +02:00
c58918268a Fixed missing file part 2
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-30 15:34:11 +02:00
3b65de9103 v0.6.1 2021-03-30 15:32:23 +02:00
208d0f8c06 Fixed missing file
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-30 15:32:10 +02:00
75ed12efea v0.6.0 2021-03-30 15:31:00 +02:00
2569db42f8 Changed back to prebuilt but this time it will build on install to allow theme.styl
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-30 15:30:26 +02:00
c1e40c6789 v0.5.4 2021-03-30 01:48:12 +02:00
bd0787d0ec Fixed Problems
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-03-30 01:47:40 +02:00
5f51088ab0 v0.5.3 2021-03-29 18:49:45 +02:00
d7a09a4dcd Finished Work On Navbar
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-03-29 18:49:23 +02:00
6c2b3466ba v0.5.2 2021-03-29 14:55:50 +02:00
31c9e30d3d Fixed Link colors
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-03-29 14:55:29 +02:00
0b60fa84ba v0.5.1 2021-03-29 14:07:38 +02:00
b586a32d00 Added back stylus utils
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-03-29 14:07:07 +02:00
ba3dcb22a1 v0.5.0 2021-03-29 14:01:46 +02:00
60f0ec23fc Updated Navbar Component
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-29 14:01:25 +02:00
803915999b v0.4.2 - Changed to be used only inconjonction with stylus
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-03-29 13:48:50 +02:00
af99c13922 v0.4.2 2021-03-29 13:43:03 +02:00
3d2fb93d72 v0.4.1 2021-03-29 13:32:26 +02:00
552f5b3dc6 v0.4.0 2021-03-29 12:48:16 +02:00
07dd430ffa Removed Tag element + Global Update
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-18 14:57:35 +01:00
6ff39ff3a7 Cleaned ignore files
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-15 17:28:58 +01:00
594e77a509 v0.3.1 2021-03-15 17:26:41 +01:00
8b12c56fe7 Moved types to the module folder
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-15 17:23:04 +01:00
3a908ccce0 Fixed Loader not found
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-15 17:22:40 +01:00
b34d25e5fd v0.3.0 2021-03-15 14:55:14 +01:00
67a4825cda Added a Loader module
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-15 14:54:27 +01:00
d898dc2f5b Fixed problems with Input and Fieldset
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-05 10:07:02 +01:00
8d99f1f3e6 v0.2.1 2021-03-05 10:06:11 +01:00
78 changed files with 42427 additions and 12969 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

12
.gitignore vendored
View File

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

View File

@ -1,11 +1,8 @@
.storybook/ .storybook/
src/ node_modules/
storybook-static/ *.stories.js
.gitattributes
.gitignore .gitignore
.npmignore .npmignore
rollup.config.js
tsconfig.json
yarn.lock yarn.lock
yarn-error.log
tsconfig.json
yarn-error.log yarn-error.log

View File

@ -1,11 +1,11 @@
const path = require("path"); const path = require("path");
const webpack = require('webpack')
module.exports = { module.exports = {
"stories": [ "stories": [
"../src/dzeio/**/*.stories.@(js|jsx|ts|tsx)", "../src/dzeio/**/*.stories.tsx",
], ],
"addons": [ "addons": [
"@storybook/addon-links",
"@storybook/addon-essentials" "@storybook/addon-essentials"
], ],
typescript: { typescript: {
@ -13,9 +13,22 @@ module.exports = {
checkOptions: {}, checkOptions: {},
reactDocgen: 'react-docgen-typescript', reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: { reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: true, shouldExtractLiteralValuesFromEnum: true,
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true), propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
}, },
}, },
presets: [path.resolve(__dirname, "./next.js")] 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

@ -0,0 +1,17 @@
// 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

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

View File

@ -18,12 +18,13 @@ module.exports = {
}); });
newConfig.resolve.extensions.push('.ts', '.tsx'); newConfig.resolve.extensions.push('.ts', '.tsx');
// SCSS // Stylus
newConfig.module.rules.push({ newConfig.module.rules.push({
test: /\.styl$/, test: /\.styl$/,
use: ['style-loader', { use: ['style-loader', {
loader: 'css-loader', loader: 'css-loader',
options: { options: {
url: false,
importLoaders: 1, importLoaders: 1,
modules: true modules: true
}, },

View File

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

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

View File

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

After

Width:  |  Height:  |  Size: 150 B

40667
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,50 +1,50 @@
{ {
"name": "@dzeio/components", "name": "@dzeio/components",
"version": "0.2.0", "version": "0.11.2",
"license": "MIT", "license": "MIT",
"main": "./index.js", "main": "./index.js",
"module": "./module/index.js",
"types": "./types/index.d.ts", "types": "./types/index.d.ts",
"dependencies": {},
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.16", "@babel/core": "^7.12.16",
"@babel/preset-env": "^7.12.16", "@babel/preset-env": "^7.12.16",
"@babel/preset-react": "^7.12.13", "@babel/preset-react": "^7.12.13",
"@rollup/plugin-typescript": "^8.2.0",
"@storybook/addon-actions": "^6.1.14",
"@storybook/addon-essentials": "^6.1.14", "@storybook/addon-essentials": "^6.1.14",
"@storybook/addon-knobs": "^6.1.14",
"@storybook/addon-links": "^6.1.14",
"@storybook/cli": "^6.1.14", "@storybook/cli": "^6.1.14",
"@storybook/react": "^6.1.14", "@storybook/react": "^6.1.14",
"@types/node": "^14.14.28", "@types/node": "^15.12.1",
"@types/react": "^17.0.2", "@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1", "@types/react-dom": "^17.0.1",
"babel-loader": "^8.2.2", "babel-loader": "^8.2.2",
"css-loader": "^5.0.2", "css-loader": "^5.0.2",
"next": "^10.0.5", "lucide-react": "^0.15.19",
"react": "^17.0.1", "next": "^10.0.0",
"react-dom": "^17.0.1", "react": "^16.0.0",
"react-feather": "^2.0.9", "react-dom": "^16.0.0",
"rollup": "^2.39.0",
"rollup-plugin-styles": "^3.14.1",
"style-loader": "^2.0.0", "style-loader": "^2.0.0",
"stylus": "^0.54.8", "stylus": "^0.54.8",
"stylus-loader": "^4.3.3", "stylus-loader": "^4.3.3",
"ts-loader": "^8.0.17", "ts-loader": "^8.0.17",
"typescript": "^4.1.3", "typescript": "^4.2.3",
"webpack": "^4.44.2" "webpack": "^4.44.2"
}, },
"peerDependencies": { "peerDependencies": {
"next": "^10.0.5", "lucide-react": "^0.15.19",
"react": "^17.0.1", "next": "^10.0.0 || ^11.0.0",
"react-dom": "^17.0.1", "react": "^16.0.0 || ^17.0.0",
"react-feather": "^2.0.9" "react-dom": "^16.0.0 || ^17.0.0",
"stylus": "^0.54.8",
"typescript": "^4.0.0"
}, },
"scripts": { "scripts": {
"storybook": "start-storybook -p 6006", "dev": "start-storybook -s ./.storybook/public -p 6006",
"build-storybook": "build-storybook", "build": "rollup --config",
"build": "tsc && rollup --config", "prepublishOnly": "npm run build",
"prepublishOnly": "yarn build" "postinstall": "rollup --config"
},
"dependencies": {
"rollup": "^2.44.0",
"rollup-plugin-styles": "^3.14.1",
"rollup-plugin-typescript2": "^0.30.0",
"tslib": "^2.1.0"
} }
} }

View File

@ -1,4 +1,4 @@
import typescript from '@rollup/plugin-typescript'; import typescript from 'rollup-plugin-typescript2';
import styles from 'rollup-plugin-styles' import styles from 'rollup-plugin-styles'
import pkg from './package.json'; import pkg from './package.json';
@ -12,20 +12,17 @@ export default [
url: false, url: false,
autoModules: true, autoModules: true,
mode: 'extract', mode: 'extract',
modules: {
generateScopedName: '[local][hash:5]'
}
}), }),
typescript(), // so Rollup can convert TypeScript to JavaScript typescript({useTsconfigDeclarationDir: true}), // so Rollup can convert TypeScript to JavaScript
], ],
output: [ output: [
{ {
dir: './', file: pkg.main,
format: 'cjs', format: 'cjs',
assetFileNames: 'style.css' assetFileNames: 'style.css'
},
{
file: pkg.module,
format: 'es',
assetFileNames: 'style.css'
} }
] ]
} }

View File

@ -0,0 +1,33 @@
@import "../config"
.box
background $foregroundLight
@media (prefers-color-scheme dark)
background $foregroundDark
border-radius 8px
.outline
border 2px solid $grayDark
@media (prefers-color-scheme dark)
border-color $grayLight
.header
padding 16px
+ .body
padding-top 0
.title
font-weight bold
font-size rem(18)
margin 0 0 8px
.subtitle
font-size rem(16)
margin 0
// BODY
.body
padding 16px

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,21 +0,0 @@
@import "../../config.styl"
.header
padding 16px
.delimiter
border-bottom 2px solid grey
padding-bottom 2px
.img
border-top-left-radius 4px
border-top-right-radius 4px
.title
font-weight bold
font-size rem(20)
margin 0 0 8px
.subtitle
font-size rem(14)
margin 0

View File

@ -1,56 +0,0 @@
import React from 'react'
import { buildClassName } from '../../Util'
import css from './BoxHeader.module.styl'
import Row from '../../Row'
import Col from '../../Col'
import Text from '../../Text'
export interface Props {
title?: string
titleColSize?: number
subtitle?: string
delimiter?: boolean
titleClassName?: string
// image?: ImageProps
}
export default class BoxHeader extends React.Component<Props> {
public render = () => (
<>
{/* {this.props.image && (
<Image {...this.props.image} />
)} */}
<div className={buildClassName(
[css.header],
[css.delimiter, this.props.delimiter]
)}>
<Row>
<Col size={this.props.titleColSize as 1 || 8}>
<Text className={buildClassName(css.title, this.props.titleClassName)}>{this.props.title}</Text>
<Text className={css.subtitle}>{this.props.subtitle}</Text>
</Col>
<Col>
<Row justify="flex-end">
{this.props.children}
</Row>
</Col>
</Row>
</div>
</>
)
}
/*
Header
delimiter?: boolean
picture?: string // url
category?: string // subtitle but above title
title string
subtitle string
center?: boolean // if Center children is not used
children?: content
*/

View File

@ -1,21 +0,0 @@
@import "../../config"
.box
background white
border-radius 8px
box-shadow 0px 2px 4px 0px rgba(black, .33)
transition all $transition
.outline
border 2px solid #E0E0E0
box-shadow none
transition border-color $transition
&:hover
border-color darken(@border[2], 20%)
@media (prefers-color-scheme dark)
.box
background #202020
.outline
border-color #1F1F1F

View File

@ -1,28 +0,0 @@
import React from 'react'
import css from './BoxWrapper.module.styl'
import { buildClassName } from '../../Util'
interface Props extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
outline?: boolean
className?: string
}
export default class BoxWrapper extends React.Component<Props> {
public render = () => (
<div {...this.props}
className={buildClassName(css.box, [css.outline, this.props.outline], this.props.className)}
>
{this.props.children}
</div>
)
}
/*
Wrapper extends div
Body
noPadding?: boolean
*/

View File

@ -1,15 +1,22 @@
import React from 'react' import React from 'react'
import BoxWrapper from './BoxWrapper' import { buildClassName } from '../Util'
import BoxHeader from './BoxHeader'
import BoxBody from './BoxBody' import css from './Box.module.styl'
import Row from '../Row'
import Col from '../Col'
import Text from '../Text'
interface Props { interface Props {
// Wrapper // Wrapper
wrapperProps?: Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, 'className'>
outline?: boolean outline?: boolean
/**
* @deprecated use wrapperProps.onClick
*/
onClick?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>['onClick']
className?: string className?: string
onClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
// Header // Header
title?: string title?: string
@ -26,28 +33,39 @@ interface Props {
export default class Box extends React.Component<Props> { export default class Box extends React.Component<Props> {
public render = () => ( public render = () => (
<BoxWrapper onClick={this.props.onClick} outline={this.props.outline} className={this.props.className}> <div
{...this.props.wrapperProps}
onClick={this.props.onClick}
className={buildClassName(css.box, this.props.className, [css.outline, this.props.outline])}
>
{(this.props.headerButtons || this.props.title || this.props.titleColSize || this.props.subtitle || this.props.delimiter || this.props.titleClassName) && ( {(this.props.headerButtons || this.props.title || this.props.titleColSize || this.props.subtitle || this.props.delimiter || this.props.titleClassName) && (
<BoxHeader <div className={buildClassName(
title={this.props.title} css.header
titleColSize={this.props.titleColSize} )}>
subtitle={this.props.subtitle} <Row nomargin justify="space-between">
delimiter={this.props.delimiter} <Col>
titleClassName={this.props.titleClassName} {this.props.title && (
> <Text className={buildClassName(css.title, this.props.titleClassName)}>{this.props.title}</Text>
{this.props.headerButtons} )}
</BoxHeader> {this.props.subtitle && (
<Text className={css.subtitle}>{this.props.subtitle}</Text>
)}
</Col>
{this.props.children && (
<Col nogrow>
<Row justify="flex-end">
{this.props.headerButtons}
</Row>
</Col>
)}
</Row>
</div>
)} )}
<BoxBody noPadding={this.props.noPadding}> {this.props.children && (
{this.props.children} <div className={buildClassName([css.body, !this.props.noPadding])}>
</BoxBody> {this.props.children}
</BoxWrapper> </div>
)}
</div>
) )
} }
export {
BoxWrapper,
BoxHeader,
BoxBody
}

View File

@ -15,6 +15,7 @@
border-radius 4px border-radius 4px
border none border none
justify-content center justify-content center
align-items center
color $textOnMain color $textOnMain
background-color $main background-color $main
@ -24,9 +25,6 @@
// Link specific // Link specific
text-decoration none text-decoration none
&.nomargintop
margin-top 0
&.outline &.outline
border 2px solid @background-color border 2px solid @background-color
padding 8px 18px // @padding - @border padding 8px 18px // @padding - @border
@ -36,7 +34,7 @@
&:hover &:hover
&:active &:active
&:focus &:focus
color @color color $textOnMain
&:hover &:hover
background-color @background-color background-color @background-color
@ -46,6 +44,12 @@
&:active &:active
&:focus &:focus
background-color darken(@background-color, 30%) background-color darken(@background-color, 30%)
&.block
display flex
width 100%
margin 0
margin-top 8px
&.large &.large
padding 15px 30px padding 15px 30px
@ -61,11 +65,8 @@
&.outline &.outline
padding 3px 8px // @padding - @border padding 3px 8px // @padding - @border
&.block &.nomargintop
display flex margin-top 0
width 100%
margin 0
margin-top 8px
&:disabled &:disabled
background $grayLight background $grayLight
@ -124,11 +125,17 @@ btn($color, $theme)
&.outline &.outline
color @background-color color @background-color
border-color @background-color border-color @background-color
// background none
&:hover &:hover
&:active &:active
&:focus &:focus
color @color color $textColor
if $theme is 'darken'
&:active
&:focus
background-color darken(@color, 30%)
&:hover &:hover
background-color @background-color background-color @background-color
@ -172,3 +179,6 @@ btn($color, $theme)
100% 100%
transform rotate(365deg) transform rotate(365deg)
.img
min-width 16px

View File

@ -1,5 +1,6 @@
import { Meta } from '@storybook/react/types-6-0' import { Meta } from '@storybook/react/types-6-0'
import React from 'react' import React from 'react'
import { Zap } from 'lucide-react'
import Component from '.' import Component from '.'
export default { export default {
@ -8,3 +9,19 @@ export default {
} as Meta } as Meta
export const Basic = (args: any) => <Component {...args}>Button</Component> export const Basic = (args: any) => <Component {...args}>Button</Component>
Basic.args = {
nomargintop: true,
icon: Zap,
size: 'small',
href: '/pouet',
block: true
}
export const WithImg = (args: any) => <Component {...args}>Button</Component>
WithImg.args = {
nomargintop: true,
icon: '/16-16.svg',
size: 'small',
href: '/pouet',
block: true
}

View File

@ -14,7 +14,8 @@ interface Props {
color?: ColorType color?: ColorType
children?: React.ReactNode children?: React.ReactNode
icon?: FC<IconProps> | string icon?: FC<IconProps> | string
size?: 'large' | 'small' | 'block' size?: 'large' | 'small'
block?: boolean
href?: string href?: string
as?: string as?: string
disabled?: boolean disabled?: boolean
@ -33,9 +34,9 @@ export default class Button extends React.Component<Props> {
inner = ( inner = (
<> <>
{typeof Icon === 'string' ? ( {typeof Icon === 'string' ? (
<Image src={Icon} width={16} height={16} /> <Image imageProps={{src: Icon, width: 16, height: 16}} />
) : ( ) : (
<Icon size={16} /> <Icon size={this.props.size === 'large' ? 20 : this.props.size === 'small' ? 14 : 16} />
)} )}
{this.props.children && ( {this.props.children && (
<span className={css.textInner}>{this.props.children}</span> <span className={css.textInner}>{this.props.children}</span>
@ -48,6 +49,7 @@ export default class Button extends React.Component<Props> {
[css.button], [css.button],
[css[this.props.color as string], this.props.color], [css[this.props.color as string], this.props.color],
[css.outline, this.props.outline], [css.outline, this.props.outline],
[css.block, this.props.block],
[css[this.props.size as string], this.props.size], [css[this.props.size as string], this.props.size],
[css.nomargintop, this.props.nomargintop], [css.nomargintop, this.props.nomargintop],
[css.loading, this.props.loading] [css.loading, this.props.loading]

View File

@ -5,28 +5,33 @@ $backColor = #757575
.label .label
position relative position relative
display flex display flex
padding-left 8px
margin 8px
user-select none user-select none
align-items center
+ .label
margin-top 8px
p p
margin-left 18px margin-left 8px
span span
top 0 top 0
left 0 left 0
width 20px width 20px
height @width height @width
position absolute position relative
box-shadow inset 0 0 0 2px $backColor box-shadow inset 0 0 0 2px $grayDark
border-radius 2px border-radius 2px
transition all $transition transition all $transition
@media (prefers-color-scheme dark)
box-shadow inset 0 0 0 2px $grayLight
&::after &::after
border-radius 20px border-radius 20px
position absolute position absolute
transition all $transition transition all $transition
background $default background $main
svg svg
transition $transition transition $transition
@ -41,19 +46,9 @@ $backColor = #757575
left 0 left 0
opacity 0 opacity 0
&:focus + span
box-shadow inset 0 0 0 2px black, 0 0 0 2px rgba(black,.3)
@media (prefers-color-scheme dark)
box-shadow inset 0 0 0 2px white, 0 0 0 2px rgba(white,.3)
&:focus:checked + span
box-shadow inset 0 0 0 2px $default, 0 0 0 2px rgba($default,.3)
@media (prefers-color-scheme dark)
box-shadow inset 0 0 0 2px $default, 0 0 0 2px rgba($default,.3)
&:checked + span &:checked + span
background rgba($default, .5) background rgba($main, .5)
box-shadow inset 0 0 0 2px $default box-shadow inset 0 0 0 2px $main
svg svg
color white color white
@ -67,7 +62,6 @@ $backColor = #757575
box-shadow inset 0 0 0 2px white box-shadow inset 0 0 0 2px white
.radio .radio
margin-left 18px // Margin + margin for Circle
span span
border-radius 20px border-radius 20px
@ -86,20 +80,19 @@ $backColor = #757575
.switch .switch
padding 2px 0 2px 10px // 2px base padding 10px circle padding padding 2px 0 2px 10px // 2px base padding 10px circle padding
&:hover span &:hover span::after
box-shadow none
&::after
background black background black
@media (prefers-color-scheme dark)
background white
span span
width 28px width 28px
height 14px height 14px
border-radius 20px border-radius 20px
top 50% top 50%
margin-right 10px
box-shadow none box-shadow none
background rgba($backColor, .5) background rgba($backColor, .5)
transform translateY(-50%)
&::after &::after
content " " content " "
@ -115,93 +108,39 @@ $backColor = #757575
width 20px width 20px
&:checked + span &:checked + span
box-shadow none
&::after &::after
left 100% left 100%
transform translate(-50%, -50%) transform translate(-50%, -50%)
background $default background $main
.primary
$color = $primary
checkBox($color)
input:checked + span input:checked + span
background rgba($color, .5) background rgba($color, .5)
box-shadow inset 0 0 0 2px $color box-shadow inset 0 0 0 2px $color
&::after &::after
background $color background $color
input:focus:checked + span
box-shadow inset 0 0 0 2px $color, 0 0 0 2px rgba($color,.3)
&.switch &.switch
input:checked + span input:checked + span
box-shadow none box-shadow none
.secondary
$color = $secondary
input:checked + span
background rgba($color, .5)
box-shadow inset 0 0 0 2px $color
&::after
background $color
&.switch
input:checked + span
box-shadow none
.info .info
$color = $info checkBox($infoLight)
@media (prefers-color-scheme dark)
input:checked + span checkBox($infoDark)
background rgba($color, .5)
box-shadow inset 0 0 0 2px $color
&::after
background $color
&.switch
input:checked + span
box-shadow none
.success .success
$color = $success checkBox($successLight)
@media (prefers-color-scheme dark)
checkBox($successDark)
input:checked + span .error
background rgba($color, .5) checkBox($errorLight)
box-shadow inset 0 0 0 2px $color @media (prefers-color-scheme dark)
checkBox($errorDark)
&::after
background $color
&.switch
input:checked + span
box-shadow none
.danger
$color = $danger
input:checked + span
background rgba($color, .5)
box-shadow inset 0 0 0 2px $color
&::after
background $color
&.switch
input:checked + span
box-shadow none
.warning .warning
$color = $warning checkBox($warningLight)
@media (prefers-color-scheme dark)
input:checked + span checkBox($warningDark)
background rgba($color, .5)
box-shadow inset 0 0 0 2px $color
&::after
background $color
&.switch
input:checked + span
box-shadow none

View File

@ -1,50 +1,43 @@
import React from 'react' import React from 'react'
import { Check } from 'react-feather' import { Check } from 'lucide-react'
import { buildClassName } from '../Util' import { buildClassName } from '../Util'
import { ColorType } from '../interfaces' import css from './Checkbox.module.styl'
import css from './Checkbox.module.styl' import Text from '../Text'
import Text from '../Text'
interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> {
interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> { label: string
label?: string type?: 'checkbox' | 'radio' | 'switch'
id: string }
type?: undefined
radio?: boolean export default class Checkbox extends React.Component<Props> {
switch?: boolean
color?: ColorType public render() {
} const props: Partial<Props> = Object.assign({}, this.props)
delete props.label
export default class Checkbox extends React.Component<Props> { delete props.type
public render() { const realType = this.props.type ?? 'checkbox'
const props: Props = Object.assign({}, this.props)
delete props.label return (
delete props.type <label htmlFor={this.props.id ?? this.props.label} className={buildClassName(
delete props.color [css.label],
delete props.switch [css.radio, realType === 'radio'],
delete props.radio [css.switch, realType === 'switch'],
[css[this.props.color as string], this.props.color]
const realType = this.props.radio ? 'radio' : 'checkbox' )}>
<input {...props}
return ( id={this.props.id ?? this.props.label}
<label htmlFor={this.props.id} className={buildClassName( type={realType === 'switch' ? 'checkbox' : realType}
[css.label], />
[css.radio, realType === 'radio'], <span>
[css.switch, this.props.switch], {realType === 'checkbox' && (
[css[this.props.color as string], this.props.color] <Check strokeWidth={4} size={16}/>
)}> )}
<input {...props} </span>
type={realType} <Text>{this.props.label}</Text>
/> </label>
<span> )
{realType === 'checkbox' && ! this.props.switch && ( }
<Check strokeWidth={4} size={16}/>
)} }
</span>
<Text>{this.props.label}</Text>
</label>
)
}
}

View File

@ -3,3 +3,11 @@
background #E8EAF6 background #E8EAF6
padding 4px 8px padding 4px 8px
border-radius 8px border-radius 8px
.pre
border-radius 8px
padding 4px 8px
background #E8EAF6
display block
.code
padding 0

View File

@ -19,7 +19,7 @@ export default class Code extends React.Component<Props> {
} }
return ( return (
<pre> <pre className={css.pre}>
{code} {code}
</pre> </pre>
) )

View File

@ -7,7 +7,7 @@
padding $gapSize 0 0 $gapSize padding $gapSize 0 0 $gapSize
&.nogrow &.nogrow
max-width intial max-width initial
flex-grow 0 flex-grow 0
flex-basis initial flex-basis initial

View File

@ -3,7 +3,7 @@ import Row from '../Row'
import css from './DebugCols.module.styl' import css from './DebugCols.module.styl'
import Col from '.' import Col from '../Col'
enum Breakpoint { enum Breakpoint {
MOBILE, MOBILE,

View File

@ -2,7 +2,7 @@
.fieldset .fieldset
border-radius 4px border-radius 4px
border 2px solid grey border 2px solid $grayDark
transition all $transition transition all $transition
margin 0 margin 0

View File

@ -0,0 +1,16 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
import Input from '../Input'
export default {
title: 'DZEIO/Fieldset',
component: Component,
argTypes: {
title: { control: 'text'}
}
} as Meta
export const Basic = (args: any) => (
<Component {...args}><Input label="Test" /></Component>
)

View File

@ -2,22 +2,32 @@
.footer .footer
padding 24px 16px padding 24px 16px
background $foregroundLight
@media (prefers-color-scheme dark)
background $foregroundDark
ul
padding 0
display flex
justify-content center
li
display inline-block
&.socials a
padding 0 8px
.animation .animation
animation grow 1s linear infinite animation grow 1s linear infinite
display inline-block display inline-block
vertical-align middle
margin 0 2px
@keyframes grow @keyframes grow
0% 0%
transform scale(1)
40% 40%
transform scale(1)
50%
transform scale(1.2)
60% 60%
transform scale(1)
70%
transform scale(1.2)
95% 95%
transform scale(1) transform scale(1)
50%
70%
transform scale(1.2)

View File

@ -0,0 +1,19 @@
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,
} 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

View File

@ -1,13 +1,48 @@
import React from 'react' import React, { FC } from 'react'
import { Heart } from 'lucide-react'
import Text from '../Text' import Link from '../Link'
import css from './Footer.module.styl' import { LucideProps } from 'lucide-react'
import Text from '../Text'
export default class Footer extends React.Component { import css from './Footer.module.styl'
public render = () => ( import Image from 'next/image'
<footer className={css.footer}>
<Text align="center">Made with <span className={css.animation}>💗</span> by Dzeio</Text> interface Props {
<Text align="center">Copyright © 2020 Dzeio. All rights reserved.</Text> text?: string
</footer> company?: string
) links?: Array<{
} path: string
name: string
}>
socials?: Array<{
href: string
icon: FC<LucideProps> | string
}>
}
export default class Footer extends React.Component<Props> {
public render = () => (
<footer className={css.footer}>
{this.props.text ? (
<Text align="center">{this.props.text}</Text>
) : (
<Text align="center">Made with <span className={css.animation}><Heart color={'#E6808A'} fill={'#E6808A'} size={16} fillOpacity={0.5} /></span> by {this.props.company || 'Dzeio'}</Text>
)}
{this.props.links && (
<ul>{this.props.links.map((l, index) => (
<li key={l.path}><Text>{index !== 0 && (<>&nbsp;- </>)}<Link href={l.path}>{l.name}</Link></Text></li>
))}</ul>
)}
{this.props.socials && (
<ul className={css.socials}>{this.props.socials.map((l, index) => (
<li key={l.href}><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>
)}
</footer>
)
}

View File

@ -1,35 +1,30 @@
@import "../config" @import "../config"
$transparent = 75% $percent = 15%
.back .back
transition all $transition transition all $transition
background linear-gradient(to left, $default, transparentify($default, $transparent)) background $mainGradient
.primary &.fullscreen > :first-child
$color = $primary min-height 100vh
background linear-gradient(to left, $color, transparentify($color, $transparent))
.secondary
$color = $secondary
background linear-gradient(to left, $color, transparentify($color, $transparent))
.info .info
$color = $info background linear-gradient(to right, $infoLight, lighten($infoLight, $percent))
background linear-gradient(to left, $color, transparentify($color, $transparent)) @media (prefers-color-scheme dark)
background linear-gradient(to right, $infoDark, darken($infoDark, $percent))
.success .success
$color = $success background linear-gradient(to right, $successLight, lighten($successLight, $percent))
background linear-gradient(to left, $color, transparentify($color, $transparent)) @media (prefers-color-scheme dark)
background linear-gradient(to right, $successDark, darken($successDark, $percent))
.danger .error
$color = $danger background linear-gradient(to right, $errorLight, lighten($errorLight, $percent))
background linear-gradient(to left, $color, transparentify($color, $transparent)) @media (prefers-color-scheme dark)
background linear-gradient(to right, $errorDark, darken($errorDark, $percent))
.warning .warning
$color = $warning background linear-gradient(to right, $warningLight, lighten($warningLight, $percent))
background linear-gradient(to left, $color, transparentify($color, $transparent)) @media (prefers-color-scheme dark)
background linear-gradient(to right, $warningDark, darken($warningDark, $percent))
@media (prefers-color-scheme dark)
.back
background $darkBackground

View File

@ -8,12 +8,18 @@ interface Props {
color?: ColorType color?: ColorType
className?: string className?: string
children: React.ReactNode children: React.ReactNode
fullscreen?: boolean
} }
/**
* Make the background a linear-gradient
*
* @version 1.0.2
*/
export default class GradientBackground extends React.Component<Props> { export default class GradientBackground extends React.Component<Props> {
public render = () => ( public render = () => (
<div className={buildClassName([css.back], [css[this.props.color as string], this.props.color], [this.props.className])}> <div className={buildClassName(css.back, [css[this.props.color as string], this.props.color], this.props.className, [css.fullscreen, this.props.fullscreen])}>
{this.props.children} {this.props.children}
</div> </div>
) )

View File

@ -1,42 +1,34 @@
.parent .parent
position relative transition-property padding, width, height, background, top, left
transition-duration .3s
transition-timing-function ease-in-out
cursor pointer
.image // Animation part 1
transition .3s // this is set to move the image from a normal position to a fixed one
object-fit contain // + one the image itself there is style with position
z-index 2 .fs1
position fixed
&.ph1 z-index 1000
position fixed padding 0
transition 0s > div
width 100%
&.ph2
width 100% !important
position fixed
max-width 100% !important
max-height 100% !important
box-sizing border-box
padding 5%
top 0 !important
left 0 !important
height 100% height 100%
background #000000A0
// 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%
&.after .body
background #00000000
// height 100%
box-sizing border-box
position fixed
// width 100%
z-index 0
padding initial
.hideOverflow
overflow hidden overflow hidden
.none
display none

View File

@ -1,182 +1,103 @@
import React from 'react' import React, { MouseEventHandler } from 'react'
import { buildClassName } from '../Util'
import NextImage from 'next/image'
import NextImage, { ImageProps } from 'next/image'
import css from './Image.module.styl' import css from './Image.module.styl'
import { buildClassName } from '../Util'
export interface ImageProps { interface Props {
src: string imageProps: ImageProps
deleteOnError?: boolean /**
canFullscreen?: boolean * Define if the image can go fullscreen
width: number */
height: number fullscreen?: boolean
alt?: string
// ClassNames
parentClassName?: string
className?: string
// Events
onClick?: () => void
} }
type evType<T = HTMLImageElement> = React.SyntheticEvent<T, Event> interface States {
image?: {
size: [number | string, number | string]
pos: [number, number]
}
transform?: [number, number]
className?: string
}
export default class Image extends React.Component<ImageProps> { export default class Image extends React.Component<Props, States> {
private ref: React.RefObject<HTMLImageElement> = React.createRef() public state: States = {}
private plchldr: React.RefObject<HTMLDivElement> = React.createRef()
private parent: React.RefObject<HTMLDivElement> = React.createRef()
private pic: React.RefObject<HTMLDivElement> = React.createRef()
private cardPos: Array<number> = [] private animationCount = 0
private cardSize: Array<number> = []
private isFullscreen = false public componentDidUpdate() {
if (!this.props.fullscreen) {return}
public async componentDidMount() { if (this.state.image) {
if (this.props.canFullscreen) { document.body.classList.add(css.body)
window.addEventListener('scroll', this.onScroll) } else {
window.addEventListener('resize', this.onResize) document.body.classList.remove(css.body)
this.onScroll()
this.onResize()
} }
} }
public async componentDidUpdate() { public componentWillUnmount() {
this.pic.current?.classList.remove(css.none) if (!this.props.fullscreen) {return}
if (this.props.canFullscreen) { document.body.classList.remove(css.body)
this.onScroll()
this.onResize()
}
if (this.isFullscreen) {
this.onClick()
}
}
public async componentWillUnmount() {
if (this.props.canFullscreen) {
window.removeEventListener('scroll', this.onScroll)
window.removeEventListener('resize', this.onResize)
}
} }
public render() { public render() {
const pic = ( if (!this.props.fullscreen) {
<div ref={this.pic} className={buildClassName(this.props.parentClassName, css.parent)}> return <NextImage
<NextImage {...this.props.imageProps}
className={buildClassName([css.image], [this.props.className])} objectFit="contain"
src={this.props.src} />
onClick={this.props.canFullscreen ? this.onClick : this.props.onClick} }
onError={this.props.deleteOnError && this.onError || undefined} return (
// layout="fill" <>
width={this.props.width} {this.state.image && (
height={this.props.height} <div style={{width: this.state.image.size[0], height: this.state.image.size[1]}}></div>
alt={this.props.alt} )}
/>
</div> <div
) className={buildClassName(css.parent, [css.fs1, this.state.image], [this.state.className, this.state.image])}
if (this.props.canFullscreen) { style={this.state.image ? {
return ( top: this.state.image.pos[1],
<div ref={this.parent}> left: this.state.image.pos[0],
<div ref={this.plchldr} className={css.none}></div> width: this.state.image.size[0],
{pic} height: this.state.image.size[1]
} : undefined}
onClick={this.props.fullscreen ? this.onClick : undefined}
>
<NextImage
priority
quality={100}
{...this.props.imageProps}
objectFit="contain"
/>
</div> </div>
) </>
} )
return pic
} }
private onScroll = async () => { private onClick: MouseEventHandler<HTMLDivElement> = (ev) => {
if (!this.ref.current || this.isFullscreen || !this.props.canFullscreen) { const target = ev.currentTarget
return const isFullscreen = !(this.state.image && this.state.className)
} const currentCount = ++this.animationCount
this.setState(isFullscreen ? {
this.cardPos = [this.ref.current.offsetTop - window.scrollY, this.ref.current.offsetLeft - window.scrollX] image: this.state.image ?? {
this.ref.current.style.top = this.cardPos[0] + 'px' size: [target.offsetWidth, target.offsetHeight],
this.ref.current.style.left = this.cardPos[1] + 'px' pos: [target.offsetLeft - window.scrollX, target.offsetTop - window.scrollY]
} },
className: undefined
private onResize = async () => { } : {
if (!this.ref.current || !this.plchldr.current || !this.props.canFullscreen || this.isFullscreen) { className: undefined
return }, () => {
}
let tmp = [this.ref.current.offsetHeight, this.ref.current.offsetWidth]
if (this.parent.current) {
tmp = [this.parent.current.offsetHeight, this.ref.current.offsetWidth]
}
this.plchldr.current.style.width = `${tmp[1]}px`
this.plchldr.current.style.height = `${tmp[0]}px`
}
private onClick = async () => {
if (!this.ref.current || !this.props.canFullscreen || !this.plchldr.current) {
return
}
if (this.props.onClick) {
this.props.onClick()
}
const i = this.ref.current
const c = this.plchldr.current
const body = document.body
i.style.top = this.cardPos[0] + 'px'
i.style.left = this.cardPos[1] + 'px'
if (this.isFullscreen) {
i.style.width = this.cardSize[1] + 'px'
i.style.height = this.cardSize[0] + 'px'
body.classList.remove(css.hideOverflow)
i.classList.remove(css.ph2)
i.classList.add(css.after)
setTimeout(() => { setTimeout(() => {
if (i.classList.contains(css.ph2) || i.classList.contains(css.ph1) || this.isFullscreen) { if (this.animationCount !== currentCount) {
return return
} }
const w = this.valToPixel(this.props.width) this.setState({
const mh = this.valToPixel(this.props?.height) className: isFullscreen ? css.fs2 : undefined,
const mw = this.valToPixel(this.props?.width) image: isFullscreen ? this.state.image : undefined
c.classList.add(css.none) })
i.style.height = '' }, isFullscreen ? 10 : 310)
i.style.width = w })
i.style.maxHeight = mh
i.style.maxWidth = mw
i.classList.remove(css.after)
}, 350)
this.isFullscreen = false
} else {
i.classList.add(css.ph1)
c.classList.remove(css.none)
i.classList.add(css.ph2)
i.classList.remove(css.ph1)
body.classList.add(css.hideOverflow)
this.isFullscreen = true
}
} }
private valToPixel(value: number|string|undefined): string {
if (typeof value === 'number') {
return `${value}px`
}
if (typeof value === 'undefined') {
return ''
}
return value
}
private onLoad = async (ev: evType) => {
ev.currentTarget.style.height = ''
ev.currentTarget.style.width = ''
}
private onError = async (ev: evType) => {
this.w('Picture not loaded', ev.currentTarget.src)
ev.currentTarget.parentElement?.classList.add(css.none)
}
private w(...messages: any) {
console.warn('[ Picture ]', ...messages)
}
} }

View File

@ -6,7 +6,7 @@
max-width 100% max-width 100%
display inline-block display inline-block
+ .parent &:not(.block) + .parent:not(.block)
margin-left 16px margin-left 16px
label label
@ -34,7 +34,7 @@
left 16px // input padding-left left 16px // input padding-left
~ label ~ label
left 16px + 24px + 16px left 16px + 24px + 10px
&.right &.right
right 16px right 16px
@ -68,6 +68,7 @@
display flex display flex
opacity 0 opacity 0
transition all $transition transition all $transition
overflow-x hidden
pointer-events none pointer-events none
// display flex // display flex
flex-direction column flex-direction column
@ -109,10 +110,12 @@
@media (prefers-color-scheme dark) @media (prefers-color-scheme dark)
background lighten(lighten($foregroundDark, 5%), 20%) background lighten(lighten($foregroundDark, 5%), 20%)
div + .autocomplete
top calc(100% - 4px - .9em)
input:focus + .autocomplete input:focus ~ .autocomplete
select:focus + .autocomplete select:focus ~ .autocomplete
textarea:focus + .autocomplete textarea:focus ~ .autocomplete
.autocomplete:hover .autocomplete:hover
opacity 1 opacity 1
pointer-events inherit pointer-events inherit
@ -122,7 +125,7 @@
textarea textarea
padding 14px 16px padding 14px 16px
height 56px height 56px
border 2px solid rgba(black, .3) border 2px solid $grayDark
border-radius 4px border-radius 4px
max-width 100% max-width 100%
box-sizing border-box box-sizing border-box
@ -132,20 +135,9 @@
transition all $transition transition all $transition
color black color black
@media (prefers-color-scheme dark) @media (prefers-color-scheme dark)
border-color rgba(white, .3) border-color $grayLight
color white color white
&:not(:disabled):hover
border-color black
@media (prefers-color-scheme dark)
border-color white
+ svg
color black
@media (prefers-color-scheme dark)
color white
&:not(:placeholder-shown) &:not(:placeholder-shown)
&:focus &:focus
&:not([placeholder=" "]) &:not([placeholder=" "])
@ -168,27 +160,47 @@
~ label ~ label
color #999 color #999
&:invalid &:not(:disabled)
border-color $danger &:hover
border-color black
@media (prefers-color-scheme dark)
border-color white
~ label + svg
color @border-color color black
~ svg @media (prefers-color-scheme dark)
color @border-color color white
&:focus &:focus
border-color $default border-color $main
~ label ~ label
color @border-color color @border-color
~ svg
color @border-color
&:invalid
border-color $errorDark
~ label
color @border-color
~ svg
color @border-color
@media (prefers-color-scheme dark)
border-color $errorLight
~ label
color @border-color
~ svg
color @border-color
~ svg
color @border-color
&.iconLeft &.iconLeft
padding-left 16px + 24px + 16px padding-left 16px + 24px + 10px
&.iconRight &.iconRight
padding-right 16 + 24 + 16px padding-right 16 + 24 + 10px
&.filled &.filled
border none border none
background rgba(gray, .1) background rgba(gray, .1)
@ -205,7 +217,7 @@
@media (prefers-color-scheme dark) @media (prefers-color-scheme dark)
background #202020 background #202020
&:hover &:hover:not(:disabled)
background rgba(gray, .2) background rgba(gray, .2)
@media (prefers-color-scheme dark) @media (prefers-color-scheme dark)
@ -217,7 +229,7 @@
background #1c1c1c background #1c1c1c
&:focus &:focus
background rgba(gray, .3) background rgba(gray, .3)
border-bottom 2px solid $default border-bottom 2px solid $main
&.opaque &.opaque
background white background white
@ -235,12 +247,12 @@
padding 0 padding 0
font-size .75rem font-size .75rem
~ svg.left ~ label ~ svg.left ~ label
left 16px + 24px + 16px // .input/padding-left label/padding-left left 16px + 24px + 10px // .input/padding-left label/padding-left
~ svg.rotate ~ svg.rotate
transform rotateX(0) transform rotateX(0)
transition $transition transition $transition
&:hover:focus ~ svg.rotate, ~.autocomplete:hover ~ svg.rotate &:focus ~ svg.rotate, ~.autocomplete:hover ~ svg.rotate
transform rotateX(180deg) transform rotateX(180deg)
div div
display flex display flex

View File

@ -1,10 +1,29 @@
import { Meta } from '@storybook/react/types-6-0' import { Meta } from '@storybook/react/types-6-0'
import React from 'react' import { Story } from "@storybook/react"
import Component from '.' import React from 'react'
import Component from '.'
export default { import { X } from 'lucide-react'
title: 'DZEIO/Input',
component: Component export default {
} as Meta title: 'DZEIO/Input',
component: Component
export const Basic = (args: any) => <Component {...args} /> } as Meta
export const Basic: Story<any> = (args: any) => <Component {...args} />
let tmp = Basic.bind({})
tmp.args = {label: 'Label', helper: 'Helper', maxLength: 6, characterCount: true, icon: X}
export const Normal = tmp
tmp = Basic.bind({})
tmp.args = {label: 'Label', filled:true, helper: 'Helper', autocomplete: ['a', 'b', 'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'], characterCount: true, icon: X}
export const AutoComplete = tmp
export const Select: Story<any> = (args: any) => <Component type="select" {...args}>
<option>a</option>
<option>b</option>
<option>c</option>
<option>d</option>
</Component>

View File

@ -1,6 +1,6 @@
import React, { FC } from 'react' import React, { FC } from 'react'
import { ChevronDown } from 'react-feather' import { ChevronDown } from 'lucide-react'
import Text from '../Text' import Text from '../Text'
import { IconProps } from '../interfaces' import { IconProps } from '../interfaces'
import { buildClassName } from '../Util' import { buildClassName } from '../Util'
@ -37,6 +37,8 @@ interface States {
export default class Input extends React.Component<Props, States> { export default class Input extends React.Component<Props, States> {
public state: States = {}
// any because f*ck types // any because f*ck types
private inputRef: React.RefObject<any> = React.createRef() private inputRef: React.RefObject<any> = React.createRef()
private parentRef: React.RefObject<HTMLDivElement> = React.createRef() private parentRef: React.RefObject<HTMLDivElement> = React.createRef()
@ -50,11 +52,14 @@ export default class Input extends React.Component<Props, States> {
} }
if (this.props.autocomplete) { if (this.props.autocomplete) {
window.addEventListener('scroll', this.parentScroll) window.addEventListener('scroll', this.parentScroll)
this.parentScroll()
} }
} }
public componentWillUnmount() { public componentWillUnmount() {
window.removeEventListener('scroll', this.parentScroll) if (this.props.autocomplete) {
window.removeEventListener('scroll', this.parentScroll)
}
} }
public render() { public render() {
@ -65,7 +70,9 @@ export default class Input extends React.Component<Props, States> {
delete props.opaque delete props.opaque
delete props.helper delete props.helper
delete props.infinityText delete props.infinityText
delete props.autocomplete
delete props.filled delete props.filled
delete props.iconRight
delete props.inputRef delete props.inputRef
delete props.selectRef delete props.selectRef
delete props.block delete props.block
@ -77,7 +84,7 @@ export default class Input extends React.Component<Props, States> {
ref: this.props.inputRef || this.inputRef, ref: this.props.inputRef || this.inputRef,
className: buildClassName( className: buildClassName(
[css.iconLeft, this.props.icon], [css.iconLeft, this.props.icon],
[css.iconRight, this.props.iconRight], [css.iconRight, this.props.iconRight || this.props.autocomplete],
[css.filled, this.props.filled], [css.filled, this.props.filled],
[css.opaque, this.props.opaque] [css.opaque, this.props.opaque]
), ),
@ -94,7 +101,7 @@ export default class Input extends React.Component<Props, States> {
input = ( input = (
<select <select
ref={this.props.selectRef || this.inputRef} ref={this.props.selectRef || this.inputRef}
{...this.props as React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>} {...props as React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>}
className={buildClassName( className={buildClassName(
[css.iconLeft, this.props.icon], [css.iconLeft, this.props.icon],
[css.iconRight, !this.props.disabled || this.props.iconRight], [css.iconRight, !this.props.disabled || this.props.iconRight],
@ -138,12 +145,6 @@ export default class Input extends React.Component<Props, States> {
> >
{input} {input}
{this.props.autocomplete && this.props.autocomplete.indexOf(this.state?.value || '') === -1 && (
<ul className={buildClassName(css.autocomplete, [css.reverse, !this.state?.isInFirstPartOfScreen])}>
{this.props.autocomplete.filter((item) => item.includes(this.state?.value || '')).map((item) => (<li key={item} onClick={this.onAutoCompleteClick(item)}><Text>{item}</Text></li>))}
</ul>
)}
{/* Process Icon */} {/* Process Icon */}
{this.props.icon && ( {this.props.icon && (
<this.props.icon className={css.left} /> <this.props.icon className={css.left} />
@ -167,6 +168,12 @@ export default class Input extends React.Component<Props, States> {
)} )}
</div> </div>
)} )}
{this.props.autocomplete && this.props.autocomplete.indexOf(this.state?.value ?? this.props.value?.toString() ?? '') === -1 && (
<ul className={buildClassName(css.autocomplete, [css.reverse, !this.state.isInFirstPartOfScreen])}>
{this.props.autocomplete.filter((item) => item.toLowerCase().includes(this.state?.value?.toLowerCase() ?? this.props.value?.toString().toLowerCase() ?? '')).map((item) => (<li key={item} onClick={this.onAutoCompleteClick(item)}><Text>{item}</Text></li>))}
</ul>
)}
</div> </div>
) )
} }
@ -175,6 +182,7 @@ export default class Input extends React.Component<Props, States> {
const div = this.parentRef.current const div = this.parentRef.current
if (!div) {return} if (!div) {return}
const result = !(div.offsetTop - window.scrollY >= window.innerHeight / 2) const result = !(div.offsetTop - window.scrollY >= window.innerHeight / 2)
// console.log(result, div, this.state.isInFirstPartOfScreen)
if (this.state.isInFirstPartOfScreen !== result) { if (this.state.isInFirstPartOfScreen !== result) {
this.setState({isInFirstPartOfScreen: result}) this.setState({isInFirstPartOfScreen: result})
} }
@ -192,8 +200,8 @@ export default class Input extends React.Component<Props, States> {
this.setState({textAreaHeight: this.inputRef.current.scrollHeight}) this.setState({textAreaHeight: this.inputRef.current.scrollHeight})
}) })
private onAutoCompleteClick = (value: string) => () => { private onAutoCompleteClick = (value: string) => async () => {
console.log('test') // console.log('test')
const item = this.getElement() const item = this.getElement()
if (!item) {return} if (!item) {return}
const valueSetter = Object.getOwnPropertyDescriptor(item, 'value')?.set const valueSetter = Object.getOwnPropertyDescriptor(item, 'value')?.set
@ -207,6 +215,9 @@ export default class Input extends React.Component<Props, States> {
valueSetter.call(item, value) valueSetter.call(item, value)
} }
item.dispatchEvent(new Event('input', {bubbles: true})) item.dispatchEvent(new Event('input', {bubbles: true}))
if (this.props.type === 'textarea') {
await this.parentScroll()
}
} }
private onChange = async (event?: React.FormEvent<HTMLDivElement>) => { private onChange = async (event?: React.FormEvent<HTMLDivElement>) => {

View File

@ -1,3 +1,12 @@
@import '../config'
.link
color $infoDark
@media (prefers-color-scheme dark)
color $infoLight
&:hover
text-decoration underline
.icon .icon
vertical-align sub vertical-align sub
margin 2px margin 2px

View File

@ -7,7 +7,9 @@ export default {
component: Component, component: Component,
argTypes: { argTypes: {
href: {control: 'text', defaultValue: 'https://www.dzeio.com'}, 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 } as Meta

View File

@ -1,41 +1,65 @@
import React from 'react' import React from 'react'
import NextLink from 'next/link' import NextLink from 'next/link'
import { ExternalLink } from 'react-feather' import { ExternalLink } from 'lucide-react'
import css from './Link.module.styl' import css from './Link.module.styl'
import { buildClassName } from '../Util'
interface Props {
href: string interface Props {
children?: React.ReactNode linkProps?: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>
className?: string href: string
forceNewTab?: boolean children?: React.ReactNode
} className?: string
/**
export default class Link extends React.Component<Props> { * Remove styling
*/
public render() { noStyle?: boolean
if (!this.props.href.startsWith('/')) {
// external link /**
return ( * Override external detection system
<a */
className={this.props.className} external?: boolean
href={this.props.href}
rel="noreferrer nofollow" /**
target="_blank" * force hiding the icon
> */
{this.props.children}<ExternalLink size={16} className={css.icon} /> hideIcon?: boolean
</a> }
)
} export default class Link extends React.Component<Props> {
return (
<NextLink href={this.props.href}> public render() {
<a const isExternal = this.props.href.startsWith('http')
className={this.props.className} const externalProps = this.props.external ? {
target={this.props.forceNewTab ? '_blank' : undefined} rel: 'noreferrer nofollow',
rel={this.props.forceNewTab ? 'noreferrer nofollow' : undefined} target: '_blank'
>{this.props.children}</a> } : {}
</NextLink>
) if (isExternal) {
} // external link
return (
} <a
{...this.props.linkProps}
className={buildClassName(this.props.className, [css.link, !this.props.noStyle])}
href={this.props.href}
{...externalProps}
>
{this.props.children}
{(this.props.external !== false && !this.props.hideIcon) && (
<ExternalLink size={16} className={css.icon} />
)}
</a>
)
}
return (
<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,19 @@
@import '../config'
.div
position fixed
left 0
width 100%
height 7px
pointer-events none
z-index 200
top 0
&.hide
transition top .5s ease-in-out
top -7px
div
&:not([style="width: 0px;"])
transition width 50ms ease-in-out
background rgba($main, .7)
height 100%

View File

@ -0,0 +1,97 @@
import { Router } from 'next/router'
import React from 'react'
import { buildClassName } from '../Util'
import css from './Loader.module.styl'
interface Props {
/**
* The new Percentage (if you calculate it yourself)
*/
percent?: number
/**
* Auto random loader
*/
auto?: {
/**
* the minimum and maximum interval between two increment
*/
interval: [number, number]
/**
* the minimum and maximum incrementation (MUST be an integer)
*/
increment: [number, number]
}
}
interface State {
percent?: number
}
/**
* Display a simple loading animation at the top of the page
*
* @version 1.0.0
*/
export default class Loader extends React.Component<Props, State> {
public state: State = {}
private interval?: NodeJS.Timeout
public componentDidMount() {
if (this.props.auto) {
Router.events.on('routeChangeComplete', 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)
}
}
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>
)
private routeChangeComplete = () => {
if (this.interval) {
clearTimeout(this.interval)
this.interval = undefined
}
this.setState({percent: 100})
}
private routeChangeStart = () => {
if (this.interval) {
clearTimeout(this.interval)
}
this.setState({percent: 0}, () => {
if (!this.props.auto) {return}
this.interval = setTimeout(this.timeoutFn, this.randomInt(this.props.auto.interval[0], this.props.auto.interval[1]))
})
}
private timeoutFn = () => {
if (!this.props.auto) {return}
const p = this.state.percent || 0
this.setState({
percent: Math.min(
99,
p >= 80 ? p + Math.random() : p + this.randomInt(this.props.auto.increment[0], this.props.auto.increment[1])
)
})
this.interval = setTimeout(this.timeoutFn, this.randomInt(this.props.auto.interval[0], this.props.auto.interval[1]))
}
private randomInt(min = 0, max = 10) {
min = Math.ceil(min)
max = Math.floor(max)
return Math.floor(Math.random() * (max - min + 1)) + min
}
}

View File

@ -1,19 +0,0 @@
@import '../config'
.menu
position absolute
opacity 0
background white
pointer-events none
border-radius 4px
box-shadow 0 2px 4px 2px rgba(black, 25%)
z-index 99
transition opacity $transition
a
display block
text-align center
padding 16px
&.shown
opacity 1
pointer-events initial

View File

@ -1,13 +0,0 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
export default {
title: 'DZEIO/Menu',
component: Component,
argTypes: {
content: {control: 'array', defaultValue: [{name: 'Name', href: 'https://www.google.com'}]}
}
} as Meta
export const Basic = (args: any) => <Component {...args} />

View File

@ -1,25 +0,0 @@
import React from 'react'
import Link from 'next/link'
import { buildClassName } from '../Util'
import css from './Menu.module.styl'
interface Props {
pos?: {top?: number, bottom?: number, left?: number, right?: number}
content: Array<{name: string, href: string, as?: string}>
show?: boolean
}
export default class Menu extends React.Component<Props> {
public render = () => (
<div className={buildClassName([css.menu], [css.shown, this.props.show])} style={this.props.pos}>
{this.props.content.map((item, index) => (
<Link key={index} href={item.href} as={item.as}>
<a>{item.name}</a>
</Link>
))}
</div>
)
}

View File

@ -1,49 +1,235 @@
@import "../config" @import '../config'
// $transition = 10s linear
// $transitionTime = 10s
// $transitionFunction = linear
.body-sidebar
margin-left 300px
transition margin-left $transition
&.short
margin-left 56px
.body-navbar
margin-top 70px
.navbar .navbar
width 100% background $foregroundLight
height 70px @media (prefers-color-scheme dark)
padding 16px background $foregroundDark
position absolute position fixed
top 0
left 0 left 0
border-bottom 1px solid white top 0
&.small
padding-left 216px
.alignRight
text-align right
.favicon img
height 38px
border-radius 8px
border 2px solid white
padding 2px
background white
width 38px
.userIcon img
padding 0
cursor pointer
.text
display inline-block
margin 0
height 38px
line-height 1
font-weight bold
font-size rem(20)
padding 7px 0 11px 16px
text-decoration none
color white
cursor pointer
.spacer
height 70px height 70px
width 100%
z-index 100
display flex
padding 16px
.icon > ul, .userSpaceParent ul
padding 7px 16px display flex
color white
box-sizing content-box li:first-child p
cursor pointer margin-left 0
> ul p, .userSpaceParent > ul p
padding 8px
margin-left 16px
border-radius 4px
&.active
background $mainGradient
color $textOnMain
.userSpace
height 100%
display flex
align-items center
cursor pointer
user-select none
svg
margin-left 16px
vertical-align top
.userMenu
position fixed
top 70px
right 0
border-bottom-left-radius 4px
transform translateX(100%)
background inherit
transition transform $transition
&.menuActive
transform translateX(0%)
.sidebar
background $foregroundLight
@media (prefers-color-scheme dark)
background $foregroundDark
position fixed
left 0
top 0
height 100vh
width 300px
z-index 100
display flex
flex-direction column
&.mobile
width 100%
z-index 101
&
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
.userSpaceParent
background $backgroundLight
@media (prefers-color-scheme dark)
background $backgroundDark
> ul, .userSpaceParent ul
display flex
padding 16px
justify-content center
li:first-child p
margin-left 0
> ul p, .userSpaceParent > ul p
padding 8px
margin-left 16px
border-radius 4px
&.active
background $mainGradient
color $textOnMain
.userSpace
cursor pointer
user-select none
padding 16px
width 100%
max-width 100%
min-height 86px
p
overflow hidden
white-space nowrap
p:last-child:not(:first-child)
margin-top 8px
font-style italic
font-size rem(14)
p:first-child
font-weight 500
svg
vertical-align top
transition transform $transition
&.menuActive
transform rotateX(180deg)
.userMenu
max-height 0px
transition all $transition
&.menuActive
// TODO find better way to animate this shit
max-height 100%
&.short
width 56px
.header > div
padding 0
.header .imgContainer
.userSpace
> ul span
width 0
padding-left 0
padding-right 0
margin 0
max-width 0
.header
min-height 70px
padding 0
margin 0
> div p > div
> div:first-child
padding 16px
> div:last-child
padding 0
hr
margin 0
> ul li
width 100%
p
padding 16px 0
display flex
align-items center
z-index 111
position relative
// Temporary fix the transition for linear-gradient
&::before
transition opacity $transition
opacity 0
width 100%
height 100%
content " "
position absolute
z-index -1
background-image $mainGradient
&:hover
&.active
color $textOnMain
&::before
opacity 1
svg
margin-left 16px
span
padding-left 16px
height inherit
.navbar, .sidebar
ul
list-style none
margin 0
padding 0
.userMenu
padding 8px 16px
a
display inline-block
padding-bottom 16px
.mobileMenu
opacity 0
transition opacity $transition
pointer-events none
&.shown
opacity 1
pointer-events initial
.mainGradient
//WIP
fill $mainGradient

View File

@ -1,10 +1,41 @@
import { Meta } from '@storybook/react/types-6-0' import { Meta, Story } from '@storybook/react/types-6-0'
import React from 'react' import React from 'react'
import Component from '.' import { Zap, ZapOff } from 'lucide-react'
import Component from '.'
export default { import Text from '../Text'
title: 'DZEIO/Navbar',
component: Component export default {
} as Meta title: 'DZEIO/Navbar',
component: Component
export const Basic = (args: any) => <Component {...args} /> } as Meta
export const Basic: Story<any> = (args: any) => <Component {...args} />
Basic.args = {
type: 'navbar',
logo: {src: '/90-38.svg', width: 90, height: 38},
loginUrl: '/login',
registerUrl: '/register',
user: {
name: 'Username',
description: 'User Description',
menu: {
links: [{
path: '/logout',
name: 'Logout'
}, {
path: '/logout',
name: 'Logout'
}],
informations: (<Text>Testing :D</Text>)
}
},
items: [{
path: '/dashboard',
name: 'Dasboard',
icon: Zap
}, {
path: '/dashboard',
name: 'Dasboard',
icon: ZapOff
}],
}

View File

@ -1,11 +0,0 @@
import React from 'react'
import css from './Navbar.module.styl'
export default class NavbarSpace extends React.Component {
public render = () => (
<div className={css.spacer}></div>
)
}

View File

@ -1,48 +1,273 @@
import React from 'react' import Router from 'next/router'
import Link from 'next/link'
import Row from '../Row' import Image, { ImageProps } from 'next/image'
import Col from '../Col' import React, { FC } from 'react'
import Image from '../Image' import { ChevronDown, ChevronsRight, Menu, X } from 'lucide-react'
import css from './Navbar.module.styl' import Text from '../Text'
import Col from '../Col'
import Row from '../Row'
interface Props { import Link from '../Link'
logo?: { import { buildClassName } from '../Util'
link?: string
label?: string import css from './Navbar.module.styl'
src: string
alt?: string interface Props {
} /**
} * Type of Navbar
* _note: when in mobile it is not listened_
export default class Navbar extends React.Component<Props> { */
type: 'navbar' | 'sidebar'
public render = () => (
<nav className={css.navbar}> /**
<Row nomargin> * Logo to display
{this.props.logo && ( */
<Col> logo?: ImageProps & {height: number, width: number}
<Row align="center"> /**
<Link href={this.props.logo.link || '/'}> * Login URL
<a aria-label={this.props.logo.label || 'Homepage'}> */
<Image loginUrl?: string
alt={this.props.logo.alt} /**
src={this.props.logo.src} * Login URL
height={38} */
width={120} registerUrl?: string
/> /**
</a> * User Informations if loggedin
</Link> */
</Row> user?: {
</Col> /**
)} * Username
*/
<Col> name: string
<Row justify="flex-end" align="center"> /**
{this.props.children} * User Short description
</Row> */
</Col> description?: string
</Row> /**
</nav> * User Menu
) */
} menu?: {
/**
* Menu links
*/
links: Array<{
path: string
name: string
}>
/**
* Custom informations shown next to the links
*/
informations?: JSX.Element
}
}
/**
* Links to display
*/
items: Array<{
path: string
icon?: FC
name: string
}>
/**
* Internal Use don't use it !
*/
mobileMenu?: () => void
}
interface State {
path?: string
short: boolean
isMobile: boolean
menuActive: boolean
}
/**
* Navbar/Sidebar Component
* @version 1.0.3
*/
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: !!this.props.mobileMenu
})
Router.events.on('routeChangeComplete', () => {
this.setState({path: Router.asPath, menuActive: false})
})
Router.events.on('routeChangeError', () => {
this.setState({path: Router.asPath, menuActive: false})
})
if (!this.props.mobileMenu) {
window.addEventListener('resize', this.onResize)
this.onResize()
}
}
public onResize = () => {
const isMobile = window.innerWidth <= 768
if (this.state.isMobile !== isMobile) {
this.setState({isMobile})
}
}
public componentDidUpdate() {
if (!this.props.mobileMenu) {
if (this.state.short) {
document.body.classList.add(css.short)
} else {
document.body.classList.remove(css.short)
}
if (this.getType() === 'sidebar') {
document.body.classList.add(css['body-sidebar'])
document.body.classList.remove(css['body-navbar'])
} else {
document.body.classList.remove(css['body-sidebar'])
document.body.classList.add(css['body-navbar'])
}
}
}
public componentWillUnmount() {
if (!this.props.mobileMenu) {
document.body.classList.remove(css.short, css[`body-${this.getType()}`])
window.removeEventListener('resize', this.onResize)
}
}
public onSidebarButton = () => {
if (!this.props.mobileMenu) {
this.setState({short: !this.state.short, menuActive: false})
} else {
this.props.mobileMenu()
}
}
public menuCloseCallback = () => {
this.setState({menuActive: false})
}
public getType(): 'sidebar' | 'navbar' {
if (this.props.mobileMenu) {
return 'sidebar'
}
return this.state.isMobile ? 'navbar' : this.props.type
}
public render = () => (
<>
<nav className={buildClassName(css[this.getType()], [css.short, this.state.short && !this.props.mobileMenu], [css.mobile, this.props.mobileMenu])}>
<Row nowrap className={css.header} align="center">
{this.props.logo && (
<Col className={css.imgContainer}>
<Link href="/">
<Image {...this.props.logo} height={34} width={this.props.logo.width*34/this.props.logo.height} />
</Link>
</Col>
)}
{this.getType() === 'sidebar' && (
<Col nogrow><Text><div onClick={this.onSidebarButton}>
{this.state.short ? (
<ChevronsRight size={30} />
) : (
<X size={30} />
)}
</div></Text></Col>
)}
</Row>
{this.getType() === 'sidebar' && (
<hr/>
)}
<ul>
{!this.state.isMobile && this.props.items.map((item) => (
<li key={item.path}><Link noStyle href={item.path}>
<Text className={buildClassName([css.active, this.state.path?.startsWith(item.path)])}>
{this.getType() === 'sidebar' && item.icon && (
<item.icon />
)}
<span>{item.name}</span>
</Text>
</Link></li>
))}
</ul>
<div style={{flex: 1}}></div>
{/* Spacer */}
{this.state.isMobile && (
<div className={css.userSpaceParent}>
<div onClick={() => this.setState({menuActive: !this.state.menuActive})} className={css.userSpace}>
<Text>
<Menu size={38} className={css.mainGradient} />
</Text>
</div>
</div>
)}
{!this.state.isMobile && this.props.user ? (
<>
<div className={css.userSpaceParent}>
{this.getType() === 'sidebar' && (
<hr/>
)}
<div onClick={() => this.setState({menuActive: !this.state.menuActive})} className={css.userSpace}>
<Text>
{this.props.user.name}
<ChevronDown className={buildClassName([css.menuActive, this.state.menuActive])} />
</Text>
{this.getType() === 'sidebar' && this.props.user.description && (
<Text>{this.props.user.description}</Text>
)}
</div>
</div>
<div className={buildClassName(css.userMenu, [css.menuActive, !this.state.isMobile && this.state.menuActive])}>
<Row nomargin>
{this.props.user.menu?.informations && (
<Col>{this.props.user.menu?.informations}</Col>
)}
<Col>
<ul>
{this.props.user.menu?.links.map((l) => (
<li key={l.path}><Text><Link noStyle href={l.path}>{l.name}</Link></Text></li>
))}
</ul>
</Col>
</Row>
</div>
</>
) : !this.state.isMobile ? (
<div className={css.userSpaceParent}>
{this.getType() === 'sidebar' && (
<hr/>
)}
<ul>
{this.props.registerUrl && (
<li><Link noStyle href={this.props.registerUrl}><a>
<Text className={buildClassName(css.active)}>
<span>Register</span>
</Text>
</a></Link></li>
)}
{this.props.loginUrl && (
<li><Link noStyle href={this.props.loginUrl}><a>
<Text>
<span>Login</span>
</Text>
</a></Link></li>
)}
</ul>
</div>
) : undefined}
</nav>
{!this.props.mobileMenu && this.state.isMobile && (
<div className={buildClassName(css.mobileMenu, [css.shown, this.state.menuActive])}>
<Navbar {...this.props} type="sidebar" mobileMenu={this.menuCloseCallback} />
</div>
)}
</>
)
}

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 Basic = (args: any) => <Component {...args} />
Basic.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,179 @@
import { buildClassName } from '../Util'
import Button from '../Button'
import Box from '../Box'
import Col from '../Col'
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}
titleClassName={css.title}
headerButtons={(
<Col nogrow>
<Text><X onClick={() => NotificationManager.removeNotification(index)} /></Text>
</Col>
)}
>
{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

@ -6,22 +6,38 @@
width 100% width 100%
top 0 top 0
left 0 left 0
background rgba(black, .3) background rgba($backgroundLight, .7)
@media (prefers-color-scheme dark)
background rgba($backgroundDark, .7)
cursor pointer cursor pointer
z-index 1000 z-index 200
animation fadeIn .3s ease-in-out 1 forwards
@keyframes fadeIn
from
opacity 0
to
opacity 1
.popupChild .popupChild
cursor initial cursor initial
z-index 1001 z-index 201
min-width 50% min-width 50%
animation popin .3s ease-in-out 1
@media (max-width $tablet) @media (max-width $tablet)
min-width 70% min-width 70%
@media (max-width $mobile) @media (max-width $mobile)
min-width 90% min-width 90%
// min-height 50vh
@keyframes popin
from
margin-top 50px
to
margin-top 0
.exit .exit
cursor pointer cursor pointer

View File

@ -1,33 +1,32 @@
import React from 'react' import React from 'react'
import { X } from 'react-feather' import { X } from 'lucide-react'
import Text from '../Text' import Text from '../Text'
import Box from '../Box' import Box from '../Box'
import Row from '../Row' import Row from '../Row'
import { Props as HeaderProps } from '../Box/BoxHeader'
import css from './Popup.module.styl'
import css from './Popup.module.styl'
interface Props {
interface Props { children: React.ReactNode
children: React.ReactNode onClose?: () => void
onClose?: () => void header?: Box['props']
header?: HeaderProps }
}
export default class Popup extends React.Component<Props> {
export default class Popup extends React.Component<Props> {
public render = () => (
public render = () => ( <Row nomargin onClick={this.parentClose} justify="center" align="center" className={css.popup}>
<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>)}>
<Box {...this.props.header} className={css.popupChild} onClick={(ev) => ev.stopPropagation()} headerButtons={(<Text><X onClick={this.props.onClose} className={css.exit} /></Text>)}> {this.props.children}
{this.props.children} </Box>
</Box> </Row>
</Row> )
)
private parentClose = (ev: React.MouseEvent<HTMLDivElement>) => {
private parentClose = (ev: React.MouseEvent<HTMLDivElement>) => { const target = ev.currentTarget
const target = ev.currentTarget if (target.classList.contains(css.popup) && this.props.onClose) {
if (target.classList.contains(css.popup) && this.props.onClose) { this.props.onClose()
this.props.onClose() }
} }
}
}
}

View File

@ -3,14 +3,12 @@
.row .row
display flex display flex
flex-wrap wrap flex-wrap wrap
margin (0 - $gapSize) 0 0 (0 - $gapSize)
padding $gapSize
&:not(.nomargin) &.nomargin
margin (0 - $gapSize) 0 0 .row
padding 0 $gapSize * 2 0 $gapSize padding 0
.row:not(.nomargin)
padding 0
margin (0 - $gapSize) 0 0 (0 - $gapSize)
.nowrap .nowrap
flex-wrap nowrap flex-wrap nowrap

View File

@ -1,32 +0,0 @@
@import "../config.styl"
.sidebar
position fixed
width 200px
top 0
left 0
height 100%
background white
padding 16px
.content
padding-left 200px
.item
display flex
padding 8px 16px
margin-top 8px
transition $transition
border-radius 8px
svg
margin-right 8px
div
text-align center
flex-grow 1
margin-left -32px
&:hover
background $default
color white

View File

@ -1,45 +0,0 @@
import React, { FC } from 'react'
import { ChevronLeft, TrendingUp } from 'react-feather'
import Link from 'next/link'
import Image from '../Image'
import { IconProps } from '../interfaces'
import css from './SidebarContainer.module.styl'
interface Props {
domain: string
children: React.ReactNode
}
export default class SidebarContainer extends React.Component<Props> {
private menu: Array<{name: string, icon: FC<IconProps>, href: string, as?: string}> = [
{ name: 'back', icon: ChevronLeft, href: '/dashboard' },
{ name: 'Uptime', icon: TrendingUp, as: `/dashboard/${this.props.domain}/uptime`, href: '/dashboard/[domain]/uptime' }
]
public render = () => (
<>
<nav className={css.sidebar}>
<Link href="/dashboard">
<a>
<Image src="/assets/logo.svg" width={175} height={100} />
</a>
</Link>
{this.menu.map((item, index) => (
<Link key={index} href={item.href} as={item.as}>
<a className={css.item}>
<item.icon />
<div>{item.name}</div>
</a>
</Link>
))}
</nav>
<div className={css.content}>
{this.props.children}
</div>
</>
)
}

View File

@ -1,13 +1,22 @@
@import '../config'
.table .table
border-spacing 0 border-spacing 0
border 2px solid #EEE border 2px solid $grayDark
@media (prefers-color-scheme dark)
border-color $grayLight
border-radius 4px border-radius 4px
width 100% width 100%
td td
border-top 1px solid #EEE border-top 1px solid $grayDark
@media (prefers-color-scheme dark)
border-color $grayLight
th th
td td
padding 8px padding 8px
text-align left text-align left
.parent
overflow-x auto

View File

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

View File

@ -1,139 +0,0 @@
@import '../config'
.tag
padding 8px 12px
border-radius 8px
margin-left 8px
height 32px
line-height 1
display inline-block
color white
background $default
outline none
&:hover
background darken($default, 10%)
&:focus
background darken($default, 20%)
&.outline
border 2px solid $default
padding 6px 10px
background transparent
&:hover
background rgba($default, .5)
&:focus
background rgba($default, .7)
.primary
$color = $primary
color white
background $color
&:hover
background lighten($color, 30%)
&:focus
background lighten($color, 15%)
&.outline
color black
border 2px solid $color
background transparent
&:hover
color white
background rgba($color, .5)
&:focus
color white
background rgba($color, .7)
.secondary
$color = $secondary
background $color
&:hover
background lighten($color, 30%)
&:focus
background lighten($color, 15%)
&.outline
color white
border 2px solid $color
background transparent
&:hover
color black
background rgba($color, .5)
&:focus
color black
background rgba($color, .7)
.info
$color = $info
color white
background $color
&:hover
background lighten($color, 30%)
&:focus
background lighten($color, 15%)
&.outline
color black
border 2px solid $color
background transparent
&:hover
color white
background rgba($color, .5)
&:focus
color white
background rgba($color, .7)
.success
$color = $success
color white
background $color
&:hover
background lighten($color, 30%)
&:focus
background lighten($color, 15%)
&.outline
color black
border 2px solid $color
background transparent
&:hover
color white
background rgba($color, .5)
&:focus
color white
background rgba($color, .7)
.danger
$color = $danger
color white
background $color
&:hover
background lighten($color, 30%)
&:focus
background lighten($color, 15%)
&.outline
color black
border 2px solid $color
background transparent
&:hover
color white
background rgba($color, .5)
&:focus
color white
background rgba($color, .7)
.warning
$color = $warning
color white
background $color
&:hover
background lighten($color, 30%)
&:focus
background lighten($color, 15%)
&.outline
color black
border 2px solid $color
background transparent
&:hover
color white
background rgba($color, .5)
&:focus
color white
background rgba($color, .7)

View File

@ -1,17 +0,0 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
export default {
title: 'DZEIO/Tag',
component: Component
} as Meta
export const Basic = (args: any) => {
const content = args.content
delete args.content
return (
<Component {...args}>{content}</Component>
)
}

View File

@ -1,42 +0,0 @@
import React from 'react'
import { ColorType } from '../interfaces'
import { buildClassName } from '../Util'
import Link from '../Link'
import css from './Tag.module.styl'
import Text from '../Text'
interface Props {
text: string
color?: ColorType
href?: string
outline?: boolean
}
export default class Tag extends React.Component<Props> {
public render() {
const classes = buildClassName(
css.tag,
[css[this.props.color as string], this.props.color],
[css.outline, this.props.outline]
)
if (!this.props.href) {
return (
<Text
className={classes}
>{this.props.text}</Text>
)
}
return (
<Link
href={this.props.href}
className={classes}
>
{this.props.text}
</Link>
)
}
}

View File

@ -1,6 +1,9 @@
.text .text
margin 0 margin 0
+ .text
margin-top 8px
.white .white
color white color white

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 Basic = (args: any) => (
<>
<Component {...args}>TExt</Component>
<Component {...args}>TExt</Component>
</>
)

View File

@ -12,7 +12,6 @@ interface Props {
export default class Text extends React.Component<Props> { export default class Text extends React.Component<Props> {
public render() { public render() {
const classes = buildClassName( const classes = buildClassName(
css.text, css.text,
@ -23,16 +22,10 @@ export default class Text extends React.Component<Props> {
this.props.className this.props.className
) )
switch (this.props.type || 'p') { if (this.props.type === 'em') {
case 'h1': return (<h1 className={classes}>{this.props.children}</h1>) return (<p className={classes}><em>{this.props.children}</em></p>)
case 'h2': return (<h2 className={classes}>{this.props.children}</h2>)
case 'h3': return (<h3 className={classes}>{this.props.children}</h3>)
case 'h4': return (<h4 className={classes}>{this.props.children}</h4>)
case 'h5': return (<h5 className={classes}>{this.props.children}</h5>)
case 'h6': return (<h6 className={classes}>{this.props.children}</h6>)
case 'em': return (<p className={classes}><em>{this.props.children}</em></p>)
case 'span': return (<span className={classes}>{this.props.children}</span>)
default: return (<p className={classes}>{this.props.children}</p>)
} }
return React.createElement(this.props.type || 'p', {className: classes, children: this.props.children})
} }
} }

View File

@ -24,13 +24,3 @@ export function buildClassName(...classes: Array<Array<any> | string | undefined
} }
return classesFinal.join(' ') return classesFinal.join(' ')
} }
export const colors = {
default: '#3949AB', // This color should never appear
primary: '#3949AB',
secondary: '#FCFCFC',
info: '#03A9F4',
success: '#2DCE89',
danger: '#F5365C',
warning: '#FB6340'
}

View File

@ -1,17 +1,18 @@
$main = #4285F4 $main = #4285F4
$textOnMain = white $textOnMain = white
$mainGradient = linear-gradient(to right, $main, lighten($main, 20%))
$infoDark = #304FFE $infoDark = #01579B
$infoLight = #29B6F6 $infoLight = #29B6F6
$successDark = #388E3C $successDark = #1B5E20
$successLight = #4CAF50 $successLight = #4CAF50
$errorDark = #D32F2F $errorDark = #7F0000
$errorLight = #F44336 $errorLight = #F44336
$warningDark = #F57C00 $warningDark = #C43E00
$warningLight = #FF9800 $warningLight = #FF9800
$backgroundDark = #161616 $backgroundDark = #161616
@ -44,11 +45,11 @@ rem($a)
($a / 16)rem ($a / 16)rem
// @deprecated colors // @deprecated colors
$primary = $main
$default = $main $default = $main
$secondary = $main
$info = $infoLight
$success = $successLight // See https://github.com/stylus/stylus/issues/1872#issuecomment-86553717
$danger = $errorLight use('stylusUtils.js')
$warning = $warningLight
$darkBackground = $backgroundDark // Import theme in the root folder of the project
@import '../../../../../../theme' if file-exists('../../../../../../theme.styl')

View File

@ -27,13 +27,16 @@ a
} }
/* Track */ /* Track */
::-webkit-scrollbar-corner
::-webkit-scrollbar-track ::-webkit-scrollbar-track
background $foregroundLight background $foregroundLight
transition $transition
@media (prefers-color-scheme dark) @media (prefers-color-scheme dark)
background $foregroundDark background $foregroundDark
::-webkit-scrollbar-thumb ::-webkit-scrollbar-thumb
background: darken($foregroundLight, 16%) background: darken($foregroundLight, 16%)
transition $transition
@media (prefers-color-scheme dark) @media (prefers-color-scheme dark)
background lighten($foregroundDark, 16%) background lighten($foregroundDark, 16%)
&:hover &:hover

View File

@ -1,6 +1,6 @@
import { SVGAttributes } from 'react' import { SVGAttributes } from 'react'
export type ColorType = 'primary' | 'info' | 'success' | 'error' | 'warning' export type ColorType = 'info' | 'success' | 'error' | 'warning'
export interface IconProps extends SVGAttributes<SVGElement> { export interface IconProps extends SVGAttributes<SVGElement> {
color?: string color?: string

11
src/dzeio/stylusUtils.js Normal file
View File

@ -0,0 +1,11 @@
// See https://github.com/stylus/stylus/issues/1872#issuecomment-86553717
var stylus = require('stylus');
module.exports = function() {
return function(style) {
style.define('file-exists', function(path) {
return !!stylus.utils.lookup(path.string, this.paths);
});
};
};

View File

@ -1,4 +1,12 @@
import Box, { BoxBody, BoxHeader, BoxWrapper } from './dzeio/Box' /**
* Copyright (c) 2021
*
* @summary DZEIO Component Library
*/
import './dzeio/general.styl'
import Box from './dzeio/Box'
import Button from './dzeio/Button' import Button from './dzeio/Button'
import Checkbox from './dzeio/Checkbox' import Checkbox from './dzeio/Checkbox'
import Code from './dzeio/Code' import Code from './dzeio/Code'
@ -7,28 +15,21 @@ import Container from './dzeio/Container'
import Fieldset from './dzeio/Fieldset' import Fieldset from './dzeio/Fieldset'
import Footer from './dzeio/Footer' import Footer from './dzeio/Footer'
import GradientBackground from './dzeio/GradientBackground' import GradientBackground from './dzeio/GradientBackground'
import Input from './dzeio/Input'
import Image from './dzeio/Image' import Image from './dzeio/Image'
import Input from './dzeio/Input'
import Link from './dzeio/Link' import Link from './dzeio/Link'
import Menu from './dzeio/Menu' import Loader from './dzeio/Loader'
import Navbar from './dzeio/Navbar' import Navbar from './dzeio/Navbar'
import NavbarSpace from './dzeio/Navbar/NavbarSpace' import NotificationManager from './dzeio/NotificationManager'
import Overflow from './dzeio/Overflow' import Overflow from './dzeio/Overflow'
import Popup from './dzeio/Popup' import Popup from './dzeio/Popup'
import Row from './dzeio/Row' import Row from './dzeio/Row'
import SidebarContainer from './dzeio/SidebarContainer'
import Table from './dzeio/Table' import Table from './dzeio/Table'
import Tag from './dzeio/Tag'
import Text from './dzeio/Text' import Text from './dzeio/Text'
import * as Util from './dzeio/Util' import * as Util from './dzeio/Util'
import './dzeio/general.styl'
export { export {
Box, Box,
BoxBody,
BoxHeader,
BoxWrapper,
Button, Button,
Checkbox, Checkbox,
Code, Code,
@ -40,15 +41,13 @@ export {
Image, Image,
Input, Input,
Link, Link,
Menu, Loader,
Navbar, Navbar,
NavbarSpace, NotificationManager,
Overflow, Overflow,
Popup, Popup,
Row, Row,
SidebarContainer,
Table, Table,
Tag,
Text, Text,
Util Util
} }

View File

@ -17,7 +17,7 @@
// "composite": true, /* Enable project compilation */ // "composite": true, /* Enable project compilation */
// "removeComments": true, /* Do not emit comments to output. */ // "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */ // "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */ "importHelpers": false, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
@ -64,6 +64,7 @@
], ],
"exclude": [ "exclude": [
"node_modules", "node_modules",
"build" "build",
"src/**/*.stories.tsx"
] ]
} }

11706
yarn.lock

File diff suppressed because it is too large Load Diff