From be58e5b0bd14b4f5268712f00dd14469ab92ff74 Mon Sep 17 00:00:00 2001 From: Lukas Corona Date: Tue, 18 Feb 2025 21:46:42 +0100 Subject: [PATCH] feat: add option to edit the color of a list --- .../components/ColorPicker/ColorPicker.jsx | 52 +++++ .../ColorPicker/ColorPicker.module.scss | 40 ++++ client/src/components/ColorPicker/index.js | 3 + client/src/components/LabelsStep/Editor.jsx | 23 +-- .../components/LabelsStep/Editor.module.scss | 39 ---- client/src/components/List/ActionsStep.jsx | 194 ++++++++++-------- client/src/components/List/List.jsx | 40 +++- client/src/components/List/List.module.scss | 5 + .../ProjectSettingsModal/BackgroundPane.jsx | 27 +-- client/src/constants/ListColors.js | 12 ++ client/src/containers/ListContainer.js | 3 +- client/src/locales/de-DE/core.js | 2 + client/src/locales/en-GB/core.js | 2 + client/src/locales/en-US/core.js | 2 + client/src/models/List.js | 1 + server/api/controllers/lists/update.js | 6 +- server/api/models/List.js | 18 ++ .../20250131202710_add_list_color.js | 17 ++ 18 files changed, 317 insertions(+), 169 deletions(-) create mode 100644 client/src/components/ColorPicker/ColorPicker.jsx create mode 100644 client/src/components/ColorPicker/ColorPicker.module.scss create mode 100644 client/src/components/ColorPicker/index.js create mode 100644 client/src/constants/ListColors.js create mode 100644 server/db/migrations/20250131202710_add_list_color.js diff --git a/client/src/components/ColorPicker/ColorPicker.jsx b/client/src/components/ColorPicker/ColorPicker.jsx new file mode 100644 index 00000000..a824b2da --- /dev/null +++ b/client/src/components/ColorPicker/ColorPicker.jsx @@ -0,0 +1,52 @@ +import classNames from 'classnames'; +import camelCase from 'lodash/camelCase'; +import upperFirst from 'lodash/upperFirst'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { Button } from 'semantic-ui-react'; +import { useTranslation } from 'react-i18next'; + +import globalStyles from '../../styles.module.scss'; +import styles from './ColorPicker.module.scss'; + +const ColorPicker = React.memo(({ current, onChange, colors, allowDeletion }) => { + const { t } = useTranslation(); + return ( + <> +
+ {colors.map((color) => ( +
+ {allowDeletion && ( + @@ -161,6 +188,7 @@ List.propTypes = { id: PropTypes.string.isRequired, index: PropTypes.number.isRequired, name: PropTypes.string.isRequired, + color: PropTypes.string.isRequired, isPersisted: PropTypes.bool.isRequired, cardIds: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types canEdit: PropTypes.bool.isRequired, diff --git a/client/src/components/List/List.module.scss b/client/src/components/List/List.module.scss index 6de525cf..f0ebda39 100644 --- a/client/src/components/List/List.module.scss +++ b/client/src/components/List/List.module.scss @@ -127,6 +127,11 @@ word-break: break-word; } + .whiteText { + color: #ffffff; + fill: #ffffff; + } + .innerWrapper { margin-right: 8px; width: 272px; diff --git a/client/src/components/ProjectSettingsModal/BackgroundPane.jsx b/client/src/components/ProjectSettingsModal/BackgroundPane.jsx index ce154a42..4552891a 100644 --- a/client/src/components/ProjectSettingsModal/BackgroundPane.jsx +++ b/client/src/components/ProjectSettingsModal/BackgroundPane.jsx @@ -1,18 +1,15 @@ import { dequal } from 'dequal'; -import upperFirst from 'lodash/upperFirst'; -import camelCase from 'lodash/camelCase'; import React, { useCallback, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; -import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import { Button, Image } from 'semantic-ui-react'; import { FilePicker } from '../../lib/custom-ui'; import ProjectBackgroundGradients from '../../constants/ProjectBackgroundGradients'; import { ProjectBackgroundTypes } from '../../constants/Enums'; +import ColorPicker from '../ColorPicker'; import styles from './BackgroundPane.module.scss'; -import globalStyles from '../../styles.module.scss'; const BackgroundPane = React.memo( ({ item, imageCoverUrl, isImageUpdating, onUpdate, onImageUpdate, onImageDelete }) => { @@ -68,23 +65,11 @@ const BackgroundPane = React.memo( return ( <>
- {ProjectBackgroundGradients.map((gradient) => ( -
{imageCoverUrl && ( // TODO: wrap in button diff --git a/client/src/constants/ListColors.js b/client/src/constants/ListColors.js new file mode 100644 index 00000000..bb9c41e0 --- /dev/null +++ b/client/src/constants/ListColors.js @@ -0,0 +1,12 @@ +export default [ + 'berry-red', + 'pumpkin-orange', + 'lagoon-blue', + 'pink-tulip', + 'light-mud', + 'orange-peel', + 'bright-moss', + 'antique-blue', + 'dark-granite', + 'lagune-blue', +]; diff --git a/client/src/containers/ListContainer.js b/client/src/containers/ListContainer.js index 6651d2c4..946e8c01 100755 --- a/client/src/containers/ListContainer.js +++ b/client/src/containers/ListContainer.js @@ -11,7 +11,7 @@ const makeMapStateToProps = () => { const selectCardIdsByListId = selectors.makeSelectCardIdsByListId(); return (state, { id, index }) => { - const { name, isPersisted } = selectListById(state, id); + const { name, color, isPersisted } = selectListById(state, id); const cardIds = selectCardIdsByListId(state, id); const currentUserMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state); @@ -22,6 +22,7 @@ const makeMapStateToProps = () => { id, index, name, + color, isPersisted, cardIds, canEdit: isCurrentUserEditor, diff --git a/client/src/locales/de-DE/core.js b/client/src/locales/de-DE/core.js index 127637a5..8a20f94a 100644 --- a/client/src/locales/de-DE/core.js +++ b/client/src/locales/de-DE/core.js @@ -183,6 +183,7 @@ export default { deleteCard: 'Karte löschen', deleteCard_title: 'Karte löschen', deleteComment: 'Kommentar löschen', + deleteColor: 'Farbe löschen', deleteImage: 'Bild löschen', deleteLabel: 'Label löschen', deleteList: 'Liste löschen', @@ -196,6 +197,7 @@ export default { duplicate: 'Kopieren', duplicateCard_title: 'Karte kopieren', edit: 'Bearbeiten', + editColor_title: 'Farbe ändern', editDueDate_title: 'Fälligkeitsdatum bearbeiten', editDescription_title: 'Beschreibung ändern', editEmail_title: 'E-Mail-Adresse bearbeiten', diff --git a/client/src/locales/en-GB/core.js b/client/src/locales/en-GB/core.js index 51279c72..1530c561 100644 --- a/client/src/locales/en-GB/core.js +++ b/client/src/locales/en-GB/core.js @@ -201,6 +201,7 @@ export default { deleteCard: 'Delete card', deleteCard_title: 'Delete Card', deleteComment: 'Delete comment', + deleteColor: 'Delete color', deleteImage: 'Delete image', deleteLabel: 'Delete label', deleteList: 'Delete list', @@ -214,6 +215,7 @@ export default { duplicate: 'Duplicate', duplicateCard_title: 'Duplicate Card', edit: 'Edit', + editColor_title: 'Edit Color', editDueDate_title: 'Edit Due Date', editDescription_title: 'Edit Description', editEmail_title: 'Edit E-mail', diff --git a/client/src/locales/en-US/core.js b/client/src/locales/en-US/core.js index e6b38429..35a74e3d 100644 --- a/client/src/locales/en-US/core.js +++ b/client/src/locales/en-US/core.js @@ -197,6 +197,7 @@ export default { deleteCard: 'Delete card', deleteCard_title: 'Delete Card', deleteComment: 'Delete comment', + deleteColor: 'Delete color', deleteImage: 'Delete image', deleteLabel: 'Delete label', deleteList: 'Delete list', @@ -210,6 +211,7 @@ export default { duplicate: 'Duplicate', duplicateCard_title: 'Duplicate Card', edit: 'Edit', + editColor_title: 'Edit Color', editDueDate_title: 'Edit Due Date', editDescription_title: 'Edit Description', editEmail_title: 'Edit E-mail', diff --git a/client/src/models/List.js b/client/src/models/List.js index 09d641f5..d42fcbe4 100755 --- a/client/src/models/List.js +++ b/client/src/models/List.js @@ -11,6 +11,7 @@ export default class extends BaseModel { id: attr(), position: attr(), name: attr(), + color: attr(), boardId: fk({ to: 'Board', as: 'board', diff --git a/server/api/controllers/lists/update.js b/server/api/controllers/lists/update.js index e1988b93..c96f8d74 100755 --- a/server/api/controllers/lists/update.js +++ b/server/api/controllers/lists/update.js @@ -21,6 +21,10 @@ module.exports = { type: 'string', isNotEmptyString: true, }, + color: { + type: 'string', + allowNull: true, + }, }, exits: { @@ -55,7 +59,7 @@ module.exports = { throw Errors.NOT_ENOUGH_RIGHTS; } - const values = _.pick(inputs, ['position', 'name']); + const values = _.pick(inputs, ['position', 'name', 'color']); list = await sails.helpers.lists.updateOne.with({ values, diff --git a/server/api/models/List.js b/server/api/models/List.js index 84f02c20..ab4f18ef 100755 --- a/server/api/models/List.js +++ b/server/api/models/List.js @@ -12,6 +12,19 @@ const SortTypes = { CREATED_AT_DESC: 'createdAt_desc', }; +const COLORS = [ + 'berry-red', + 'pumpkin-orange', + 'lagoon-blue', + 'pink-tulip', + 'light-mud', + 'orange-peel', + 'bright-moss', + 'antique-blue', + 'dark-granite', + 'lagune-blue', +]; + module.exports = { SortTypes, @@ -28,6 +41,11 @@ module.exports = { type: 'string', required: true, }, + color: { + type: 'string', + allowNull: true, + isIn: COLORS, + }, // ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗ // ║╣ ║║║╠╩╗║╣ ║║╚═╗ diff --git a/server/db/migrations/20250131202710_add_list_color.js b/server/db/migrations/20250131202710_add_list_color.js new file mode 100644 index 00000000..07283376 --- /dev/null +++ b/server/db/migrations/20250131202710_add_list_color.js @@ -0,0 +1,17 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = (knex) => + knex.schema.alterTable('list', (table) => { + table.text('color'); + }); + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = (knex) => + knex.schema.alterTable('list', (table) => { + table.dropColumn('color'); + });