1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-08-08 23:15:31 +02:00

Merge branch 'master' into new-features/context-menu

This commit is contained in:
Rafly Maulana 2022-11-21 01:00:11 +07:00
commit ad78fc5e23
No known key found for this signature in database
GPG key ID: 9AADAF05ED276842
11 changed files with 269 additions and 83 deletions

141
client/package-lock.json generated
View file

@ -11,6 +11,7 @@
"connected-react-router": "^6.9.3", "connected-react-router": "^6.9.3",
"date-fns": "^2.29.1", "date-fns": "^2.29.1",
"dequal": "^2.0.3", "dequal": "^2.0.3",
"easymde": "^2.18.0",
"history": "^4.10.1", "history": "^4.10.1",
"i18next": "^21.8.14", "i18next": "^21.8.14",
"i18next-browser-languagedetector": "^6.1.4", "i18next-browser-languagedetector": "^6.1.4",
@ -33,6 +34,7 @@
"react-redux": "^7.2.8", "react-redux": "^7.2.8",
"react-router-dom": "^5.3.1", "react-router-dom": "^5.3.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-simplemde-editor": "^5.2.0",
"react-textarea-autosize": "^8.3.4", "react-textarea-autosize": "^8.3.4",
"redux": "^4.2.0", "redux": "^4.2.0",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",
@ -3695,6 +3697,14 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/codemirror": {
"version": "5.60.5",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.5.tgz",
"integrity": "sha512-TiECZmm8St5YxjFUp64LK0c8WU5bxMDt9YaAek1UqUb9swrSCoJhh92fWu1p3mTEqlHjhB5sY7OFBhWroJXZVg==",
"dependencies": {
"@types/tern": "*"
}
},
"node_modules/@types/connect": { "node_modules/@types/connect": {
"version": "3.4.35", "version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
@ -3833,6 +3843,11 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
}, },
"node_modules/@types/marked": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.7.tgz",
"integrity": "sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw=="
},
"node_modules/@types/mdast": { "node_modules/@types/mdast": {
"version": "3.0.10", "version": "3.0.10",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",
@ -3970,6 +3985,14 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
}, },
"node_modules/@types/tern": {
"version": "0.23.4",
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.4.tgz",
"integrity": "sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg==",
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/@types/trusted-types": { "node_modules/@types/trusted-types": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
@ -6172,6 +6195,19 @@
"node": ">= 4.0" "node": ">= 4.0"
} }
}, },
"node_modules/codemirror": {
"version": "5.65.9",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.9.tgz",
"integrity": "sha512-19Jox5sAKpusTDgqgKB5dawPpQcY+ipQK7xoEI+MVucEF9qqFaXpeqY1KaoyGBso/wHQoDa4HMMxMjdsS3Zzzw=="
},
"node_modules/codemirror-spell-checker": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz",
"integrity": "sha512-2Tl6n0v+GJRsC9K3MLCdLaMOmvWL0uukajNJseorZJsslaxZyZMgENocPU8R0DyoTAiKsyqiemSOZo7kjGV0LQ==",
"dependencies": {
"typo-js": "*"
}
},
"node_modules/collect-v8-coverage": { "node_modules/collect-v8-coverage": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
@ -7419,6 +7455,18 @@
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
}, },
"node_modules/easymde": {
"version": "2.18.0",
"resolved": "https://registry.npmjs.org/easymde/-/easymde-2.18.0.tgz",
"integrity": "sha512-IxVVUxNWIoXLeqtBU4BLc+eS/ScYhT1Dcb6yF5Wchoj1iXAV+TIIDWx+NCaZhY7RcSHqDPKllbYq7nwGKILnoA==",
"dependencies": {
"@types/codemirror": "^5.60.4",
"@types/marked": "^4.0.7",
"codemirror": "^5.63.1",
"codemirror-spell-checker": "1.1.2",
"marked": "^4.1.0"
}
},
"node_modules/ecc-jsbn": { "node_modules/ecc-jsbn": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@ -14763,6 +14811,17 @@
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
}, },
"node_modules/marked": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.2.2.tgz",
"integrity": "sha512-JjBTFTAvuTgANXx82a5vzK9JLSMoV6V3LBVn4Uhdso6t7vXrGx7g1Cd2r6NYSsxrYbQGFCMqBDhFHyK5q2UvcQ==",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/mdast-util-definitions": { "node_modules/mdast-util-definitions": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.1.tgz", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.1.tgz",
@ -20516,6 +20575,19 @@
"react": "^16.0.0 || ^17.0.0 || ^18.0.0" "react": "^16.0.0 || ^17.0.0 || ^18.0.0"
} }
}, },
"node_modules/react-simplemde-editor": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-simplemde-editor/-/react-simplemde-editor-5.2.0.tgz",
"integrity": "sha512-GkTg1MlQHVK2Rks++7sjuQr/GVS/xm6y+HchZ4GPBWrhcgLieh4CjK04GTKbsfYorSRYKa0n37rtNSJmOzEDkQ==",
"dependencies": {
"@types/codemirror": "~5.60.5"
},
"peerDependencies": {
"easymde": ">= 2.0.0 < 3.0.0",
"react": ">=16.8.2",
"react-dom": ">=16.8.2"
}
},
"node_modules/react-test-renderer": { "node_modules/react-test-renderer": {
"version": "17.0.2", "version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz",
@ -23873,6 +23945,11 @@
"typescript-compare": "^0.0.2" "typescript-compare": "^0.0.2"
} }
}, },
"node_modules/typo-js": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.2.tgz",
"integrity": "sha512-C7pYBQK17EjSg8tVNY91KHdUt5Nf6FMJ+c3js076quPmBML57PmNMzAcIq/2kf/hSYtFABNDIYNYlJRl5BJhGw=="
},
"node_modules/unbox-primitive": { "node_modules/unbox-primitive": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
@ -27814,6 +27891,14 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/codemirror": {
"version": "5.60.5",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.5.tgz",
"integrity": "sha512-TiECZmm8St5YxjFUp64LK0c8WU5bxMDt9YaAek1UqUb9swrSCoJhh92fWu1p3mTEqlHjhB5sY7OFBhWroJXZVg==",
"requires": {
"@types/tern": "*"
}
},
"@types/connect": { "@types/connect": {
"version": "3.4.35", "version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
@ -27952,6 +28037,11 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
}, },
"@types/marked": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.7.tgz",
"integrity": "sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw=="
},
"@types/mdast": { "@types/mdast": {
"version": "3.0.10", "version": "3.0.10",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",
@ -28089,6 +28179,14 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
}, },
"@types/tern": {
"version": "0.23.4",
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.4.tgz",
"integrity": "sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg==",
"requires": {
"@types/estree": "*"
}
},
"@types/trusted-types": { "@types/trusted-types": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
@ -29733,6 +29831,19 @@
"q": "^1.1.2" "q": "^1.1.2"
} }
}, },
"codemirror": {
"version": "5.65.9",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.9.tgz",
"integrity": "sha512-19Jox5sAKpusTDgqgKB5dawPpQcY+ipQK7xoEI+MVucEF9qqFaXpeqY1KaoyGBso/wHQoDa4HMMxMjdsS3Zzzw=="
},
"codemirror-spell-checker": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz",
"integrity": "sha512-2Tl6n0v+GJRsC9K3MLCdLaMOmvWL0uukajNJseorZJsslaxZyZMgENocPU8R0DyoTAiKsyqiemSOZo7kjGV0LQ==",
"requires": {
"typo-js": "*"
}
},
"collect-v8-coverage": { "collect-v8-coverage": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
@ -30645,6 +30756,18 @@
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
}, },
"easymde": {
"version": "2.18.0",
"resolved": "https://registry.npmjs.org/easymde/-/easymde-2.18.0.tgz",
"integrity": "sha512-IxVVUxNWIoXLeqtBU4BLc+eS/ScYhT1Dcb6yF5Wchoj1iXAV+TIIDWx+NCaZhY7RcSHqDPKllbYq7nwGKILnoA==",
"requires": {
"@types/codemirror": "^5.60.4",
"@types/marked": "^4.0.7",
"codemirror": "^5.63.1",
"codemirror-spell-checker": "1.1.2",
"marked": "^4.1.0"
}
},
"ecc-jsbn": { "ecc-jsbn": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@ -36172,6 +36295,11 @@
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.2.tgz", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.2.tgz",
"integrity": "sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==" "integrity": "sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA=="
}, },
"marked": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.2.2.tgz",
"integrity": "sha512-JjBTFTAvuTgANXx82a5vzK9JLSMoV6V3LBVn4Uhdso6t7vXrGx7g1Cd2r6NYSsxrYbQGFCMqBDhFHyK5q2UvcQ=="
},
"mdast-util-definitions": { "mdast-util-definitions": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.1.tgz", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.1.tgz",
@ -40120,6 +40248,14 @@
"react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0"
} }
}, },
"react-simplemde-editor": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-simplemde-editor/-/react-simplemde-editor-5.2.0.tgz",
"integrity": "sha512-GkTg1MlQHVK2Rks++7sjuQr/GVS/xm6y+HchZ4GPBWrhcgLieh4CjK04GTKbsfYorSRYKa0n37rtNSJmOzEDkQ==",
"requires": {
"@types/codemirror": "~5.60.5"
}
},
"react-test-renderer": { "react-test-renderer": {
"version": "17.0.2", "version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz",
@ -42736,6 +42872,11 @@
"typescript-compare": "^0.0.2" "typescript-compare": "^0.0.2"
} }
}, },
"typo-js": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.2.tgz",
"integrity": "sha512-C7pYBQK17EjSg8tVNY91KHdUt5Nf6FMJ+c3js076quPmBML57PmNMzAcIq/2kf/hSYtFABNDIYNYlJRl5BJhGw=="
},
"unbox-primitive": { "unbox-primitive": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",

View file

@ -69,6 +69,7 @@
"connected-react-router": "^6.9.3", "connected-react-router": "^6.9.3",
"date-fns": "^2.29.1", "date-fns": "^2.29.1",
"dequal": "^2.0.3", "dequal": "^2.0.3",
"easymde": "^2.18.0",
"history": "^4.10.1", "history": "^4.10.1",
"i18next": "^21.8.14", "i18next": "^21.8.14",
"i18next-browser-languagedetector": "^6.1.4", "i18next-browser-languagedetector": "^6.1.4",
@ -91,6 +92,7 @@
"react-redux": "^7.2.8", "react-redux": "^7.2.8",
"react-router-dom": "^5.3.1", "react-router-dom": "^5.3.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-simplemde-editor": "^5.2.0",
"react-textarea-autosize": "^8.3.4", "react-textarea-autosize": "^8.3.4",
"redux": "^4.2.0", "redux": "^4.2.0",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",

View file

@ -28,33 +28,35 @@ const BoardActions = React.memo(
onLabelDelete, onLabelDelete,
}) => { }) => {
return ( return (
<div className={styles.actions}> <div className={styles.wrapper}>
<div className={styles.action}> <div className={styles.actions}>
<Memberships <div className={styles.action}>
items={memberships} <Memberships
allUsers={allUsers} items={memberships}
permissionsSelectStep={BoardMembershipPermissionsSelectStep} allUsers={allUsers}
canEdit={canEditMemberships} permissionsSelectStep={BoardMembershipPermissionsSelectStep}
onCreate={onMembershipCreate} canEdit={canEditMemberships}
onUpdate={onMembershipUpdate} onCreate={onMembershipCreate}
onDelete={onMembershipDelete} onUpdate={onMembershipUpdate}
/> onDelete={onMembershipDelete}
</div> />
<div className={styles.action}> </div>
<Filters <div className={styles.action}>
users={filterUsers} <Filters
labels={filterLabels} users={filterUsers}
allBoardMemberships={memberships} labels={filterLabels}
allLabels={labels} allBoardMemberships={memberships}
canEdit={canEdit} allLabels={labels}
onUserAdd={onUserToFilterAdd} canEdit={canEdit}
onUserRemove={onUserFromFilterRemove} onUserAdd={onUserToFilterAdd}
onLabelAdd={onLabelToFilterAdd} onUserRemove={onUserFromFilterRemove}
onLabelRemove={onLabelFromFilterRemove} onLabelAdd={onLabelToFilterAdd}
onLabelCreate={onLabelCreate} onLabelRemove={onLabelFromFilterRemove}
onLabelUpdate={onLabelUpdate} onLabelCreate={onLabelCreate}
onLabelDelete={onLabelDelete} onLabelUpdate={onLabelUpdate}
/> onLabelDelete={onLabelDelete}
/>
</div>
</div> </div>
</div> </div>
); );

View file

@ -1,5 +1,6 @@
:global(#app) { :global(#app) {
.action { .action {
flex: 0 0 auto;
margin-right: 20px; margin-right: 20px;
} }
@ -8,4 +9,15 @@
display: flex; display: flex;
margin: 20px 20px; margin: 20px 20px;
} }
.wrapper {
overflow-x: auto;
overflow-y: hidden;
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
} }

View file

@ -1,19 +1,15 @@
import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'; import React, { useCallback, useImperativeHandle, useMemo, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import TextareaAutosize from 'react-textarea-autosize'; import { Button, Form } from 'semantic-ui-react';
import { Button, Form, TextArea } from 'semantic-ui-react'; import SimpleMDE from 'react-simplemde-editor';
import { useClosableForm, useField } from '../../hooks';
import styles from './DescriptionEdit.module.scss'; import styles from './DescriptionEdit.module.scss';
const DescriptionEdit = React.forwardRef(({ children, defaultValue, onUpdate }, ref) => { const DescriptionEdit = React.forwardRef(({ children, defaultValue, onUpdate }, ref) => {
const [t] = useTranslation(); const [t] = useTranslation();
const [isOpened, setIsOpened] = useState(false); const [isOpened, setIsOpened] = useState(false);
const [value, handleFieldChange, setValue] = useField(null); const [value, setValue] = useState(null);
const field = useRef(null);
const open = useCallback(() => { const open = useCallback(() => {
setIsOpened(true); setIsOpened(true);
@ -55,20 +51,37 @@ const DescriptionEdit = React.forwardRef(({ children, defaultValue, onUpdate },
[close], [close],
); );
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
close,
isOpened,
);
const handleSubmit = useCallback(() => { const handleSubmit = useCallback(() => {
close(); close();
}, [close]); }, [close]);
useEffect(() => { const mdEditorOptions = useMemo(
if (isOpened) { () => ({
field.current.ref.current.focus(); autofocus: true,
} spellChecker: false,
}, [isOpened]); status: false,
toolbar: [
'bold',
'italic',
'heading',
'strikethrough',
'|',
'quote',
'unordered-list',
'ordered-list',
'table',
'|',
'link',
'image',
'|',
'undo',
'redo',
'|',
'guide',
],
}),
[],
);
if (!isOpened) { if (!isOpened) {
return React.cloneElement(children, { return React.cloneElement(children, {
@ -78,26 +91,17 @@ const DescriptionEdit = React.forwardRef(({ children, defaultValue, onUpdate },
return ( return (
<Form onSubmit={handleSubmit}> <Form onSubmit={handleSubmit}>
<TextArea <SimpleMDE
ref={field}
as={TextareaAutosize}
value={value} value={value}
options={mdEditorOptions}
placeholder={t('common.enterDescription')} placeholder={t('common.enterDescription')}
minRows={3}
spellCheck={false}
className={styles.field} className={styles.field}
onKeyDown={handleFieldKeyDown} onKeyDown={handleFieldKeyDown}
onChange={handleFieldChange} onChange={setValue}
onBlur={handleFieldBlur}
/> />
<div className={styles.controls}> <div className={styles.controls}>
{/* eslint-disable-next-line jsx-a11y/mouse-events-have-key-events */} {/* eslint-disable-next-line jsx-a11y/mouse-events-have-key-events */}
<Button <Button positive content={t('action.save')} />
positive
content={t('action.save')}
onMouseOver={handleControlMouseOver}
onMouseOut={handleControlMouseOut}
/>
</div> </div>
</Form> </Form>
); );

View file

@ -6,15 +6,12 @@
.field { .field {
background: #fff; background: #fff;
border: 1px solid rgba(9, 30, 66, 0.13);
border-radius: 3px;
color: #17394d; color: #17394d;
display: block; display: block;
font-size: 14px; font-size: 14px;
line-height: 1.5; line-height: 1.5;
margin-bottom: 4px; margin-bottom: 4px;
overflow: hidden; overflow: hidden;
padding: 8px 12px;
resize: none; resize: none;
&:focus { &:focus {

View file

@ -1,3 +1,4 @@
import truncate from 'lodash/truncate';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation, Trans } from 'react-i18next'; import { useTranslation, Trans } from 'react-i18next';
@ -47,23 +48,26 @@ const NotificationsStep = React.memo(({ items, onDelete, onClose }) => {
{activity.data.toList.name} {activity.data.toList.name}
</Trans> </Trans>
); );
case ActivityTypes.COMMENT_CARD: case ActivityTypes.COMMENT_CARD: {
const commentText = truncate(activity.data.text);
return ( return (
<Trans <Trans
i18nKey="common.userLeftNewCommentToCard" i18nKey="common.userLeftNewCommentToCard"
values={{ values={{
user: activity.user.name, user: activity.user.name,
comment: activity.data.text, comment: commentText,
card: card.name, card: card.name,
}} }}
> >
{activity.user.name} {activity.user.name}
{` left a new comment «${activity.data.text}» to `} {` left a new comment «${commentText}» to `}
<Link to={Paths.CARDS.replace(':id', card.id)} onClick={onClose}> <Link to={Paths.CARDS.replace(':id', card.id)} onClick={onClose}>
{card.name} {card.name}
</Link> </Link>
</Trans> </Trans>
); );
}
default: default:
} }
@ -80,9 +84,10 @@ const NotificationsStep = React.memo(({ items, onDelete, onClose }) => {
})} })}
</Popup.Header> </Popup.Header>
<Popup.Content> <Popup.Content>
{items.length > 0 {items.length > 0 ? (
? items.map((item) => ( <div className={styles.wrapper}>
<div key={item.id} className={styles.wrapper}> {items.map((item) => (
<div key={item.id} className={styles.item}>
{item.card && item.activity ? ( {item.card && item.activity ? (
<> <>
<User <User
@ -90,20 +95,23 @@ const NotificationsStep = React.memo(({ items, onDelete, onClose }) => {
avatarUrl={item.activity.user.avatarUrl} avatarUrl={item.activity.user.avatarUrl}
size="large" size="large"
/> />
<span className={styles.content}>{renderItemContent(item)}</span> <span className={styles.itemContent}>{renderItemContent(item)}</span>
</> </>
) : ( ) : (
<div className={styles.deletedContent}>{t('common.cardOrActionAreDeleted')}</div> <div className={styles.itemDeleted}>{t('common.cardOrActionAreDeleted')}</div>
)} )}
<Button <Button
type="button" type="button"
icon="close" icon="trash alternate outline"
className={styles.button} className={styles.itemButton}
onClick={() => handleDelete(item.id)} onClick={() => handleDelete(item.id)}
/> />
</div> </div>
)) ))}
: t('common.noUnreadNotifications')} </div>
) : (
t('common.noUnreadNotifications')
)}
</Popup.Content> </Popup.Content>
</> </>
); );

View file

@ -1,5 +1,13 @@
:global(#app) { :global(#app) {
.button { .item {
padding: 12px;
&:hover {
background: #f0f0f0;
}
}
.itemButton {
background: transparent; background: transparent;
box-shadow: none; box-shadow: none;
float: right; float: right;
@ -7,7 +15,7 @@
line-height: 20px; line-height: 20px;
margin: 0; margin: 0;
min-height: auto; min-height: auto;
padding: 5px 0; padding: 0;
transition: background 0.3s ease; transition: background 0.3s ease;
width: 20px; width: 20px;
@ -16,7 +24,7 @@
} }
} }
.content { .itemContent {
display: inline-block; display: inline-block;
font-size: 12px; font-size: 12px;
min-height: 36px; min-height: 36px;
@ -27,7 +35,7 @@
word-break: break-word; word-break: break-word;
} }
.deletedContent { .itemDeleted {
display: inline-block; display: inline-block;
line-height: 20px; line-height: 20px;
min-height: 20px; min-height: 20px;
@ -38,10 +46,21 @@
.wrapper { .wrapper {
margin: 0 -12px; margin: 0 -12px;
padding: 12px; max-height: 60vh;
overflow-x: hidden;
overflow-y: auto;
scrollbar-width: thin;
&:hover { &::-webkit-scrollbar {
background: #f0f0f0; width: 5px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
border-radius: 3px;
} }
} }
} }

View file

@ -11,6 +11,7 @@ import NotFound from './NotFound';
import 'react-datepicker/dist/react-datepicker.css'; import 'react-datepicker/dist/react-datepicker.css';
import 'photoswipe/dist/photoswipe.css'; import 'photoswipe/dist/photoswipe.css';
import 'easymde/dist/easymde.min.css';
import '../lib/custom-ui/styles.css'; import '../lib/custom-ui/styles.css';
import '../styles.module.scss'; import '../styles.module.scss';

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "planka", "name": "planka",
"version": "1.8.4", "version": "1.8.5",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "planka", "name": "planka",
"version": "1.8.4", "version": "1.8.5",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View file

@ -1,6 +1,6 @@
{ {
"name": "planka", "name": "planka",
"version": "1.8.4", "version": "1.8.5",
"private": true, "private": true,
"homepage": "https://plankanban.github.io/planka", "homepage": "https://plankanban.github.io/planka",
"repository": { "repository": {