From 34220fd2ad9576229afc7681e78da980eb9cc436 Mon Sep 17 00:00:00 2001 From: Anna No Date: Mon, 18 Apr 2022 23:04:54 +0700 Subject: [PATCH] Board: Add create / edit card label popup Signed-off-by: Anna No --- models/board/src/index.ts | 35 +++- models/board/src/plugin.ts | 3 + packages/theme/styles/_layouts.scss | 14 +- packages/ui/lang/en.json | 2 +- packages/ui/src/colors.ts | 1 + plugins/board-assets/lang/en.json | 4 + plugins/board-assets/lang/ru.json | 4 + .../components/popups/CardLabelsEditor.svelte | 157 ++++++++++++++++++ .../components/popups/CardLabelsPicker.svelte | 45 +++-- .../components/popups/CardLabelsPopup.svelte | 40 +++++ .../presenters/ColorPresenter.svelte | 33 ++++ .../presenters/DatePresenter.svelte | 9 +- .../presenters/LabelPresenter.svelte | 14 +- plugins/board-resources/src/index.ts | 8 +- plugins/board-resources/src/plugin.ts | 7 +- .../board-resources/src/utils/BoardUtils.ts | 54 ++++-- plugins/board/src/index.ts | 6 +- 17 files changed, 385 insertions(+), 51 deletions(-) create mode 100644 plugins/board-resources/src/components/popups/CardLabelsEditor.svelte create mode 100644 plugins/board-resources/src/components/popups/CardLabelsPopup.svelte create mode 100644 plugins/board-resources/src/components/presenters/ColorPresenter.svelte diff --git a/models/board/src/index.ts b/models/board/src/index.ts index ca95ebf9f4..bf99ebb539 100644 --- a/models/board/src/index.ts +++ b/models/board/src/index.ts @@ -16,7 +16,7 @@ // To help typescript locate view plugin properly import type { Board, Card, CardAction, CardDate, CardLabel } from '@anticrm/board' import type { Employee } from '@anticrm/contact' -import { TxOperations as Client, Doc, DOMAIN_MODEL, FindOptions, IndexKind, Ref } from '@anticrm/core' +import { TxOperations as Client, Doc, DOMAIN_MODEL, FindOptions, IndexKind, Ref, Type, Timestamp } from '@anticrm/core' import { Builder, Collection, @@ -32,7 +32,7 @@ import { import attachment from '@anticrm/model-attachment' import chunter from '@anticrm/model-chunter' import contact from '@anticrm/model-contact' -import core, { TAttachedDoc, TDoc } from '@anticrm/model-core' +import core, { TAttachedDoc, TDoc, TObj } from '@anticrm/model-core' import task, { TSpaceWithStates, TTask } from '@anticrm/model-task' import view from '@anticrm/model-view' import workbench from '@anticrm/model-workbench' @@ -40,6 +40,13 @@ import { Asset, IntlString, Resource } from '@anticrm/platform' import type { AnyComponent } from '@anticrm/ui' import board from './plugin' +/** + * @public + */ +export function TypeCardDate (): Type { + return { _class: board.class.CardDate, label: board.string.Dates } +} + @Model(board.class.Board, task.class.SpaceWithStates) @UX(board.string.Board, board.icon.Board) export class TBoard extends TSpaceWithStates implements Board { @@ -47,11 +54,20 @@ export class TBoard extends TSpaceWithStates implements Board { background!: string } +@Model(board.class.CardDate, core.class.Obj, DOMAIN_MODEL) +@UX(board.string.Dates) +export class TCardDate extends TObj implements CardDate { + dueDate?: Timestamp + isChecked?: boolean + startDate?: Timestamp +} + @Model(board.class.CardLabel, core.class.AttachedDoc, DOMAIN_MODEL) @UX(board.string.Labels) export class TCardLabel extends TAttachedDoc implements CardLabel { - title!: string; - color!: number; + title!: string + color!: number + isHidden?: boolean } @Model(board.class.Card, task.class.Task) @@ -64,6 +80,7 @@ export class TCard extends TTask implements Card { @Prop(TypeBoolean(), board.string.IsArchived) isArchived?: boolean + @Prop(TypeCardDate(), board.string.Dates) date?: CardDate @Prop(TypeMarkup(), board.string.Description) @@ -105,7 +122,7 @@ export class TCardAction extends TDoc implements CardAction { } export function createModel (builder: Builder): void { - builder.createModel(TBoard, TCard, TCardLabel, TCardAction) + builder.createModel(TBoard, TCard, TCardLabel, TCardDate, TCardAction) builder.mixin(board.class.Board, core.class.Class, workbench.mixin.SpaceView, { view: { @@ -206,6 +223,14 @@ export function createModel (builder: Builder): void { presenter: board.component.CardPresenter }) + builder.mixin(board.class.CardLabel, core.class.Class, view.mixin.AttributePresenter, { + presenter: board.component.CardLabelPresenter + }) + + builder.mixin(board.class.CardDate, core.class.Class, view.mixin.AttributePresenter, { + presenter: board.component.CardDatePresenter + }) + builder.mixin(board.class.Board, core.class.Class, view.mixin.AttributePresenter, { presenter: board.component.BoardPresenter }) diff --git a/models/board/src/plugin.ts b/models/board/src/plugin.ts index 09d4385169..3eac1af135 100644 --- a/models/board/src/plugin.ts +++ b/models/board/src/plugin.ts @@ -28,6 +28,9 @@ export default mergeIds(boardId, board, { CreateCard: '' as AnyComponent, KanbanCard: '' as AnyComponent, CardPresenter: '' as AnyComponent, + CardLabelPresenter: '' as AnyComponent, + CardDatePresenter: '' as AnyComponent, + BoardPresenter: '' as AnyComponent, TemplatesIcon: '' as AnyComponent, Cards: '' as AnyComponent, KanbanView: '' as AnyComponent diff --git a/packages/theme/styles/_layouts.scss b/packages/theme/styles/_layouts.scss index e7356e791f..effed695cb 100644 --- a/packages/theme/styles/_layouts.scss +++ b/packages/theme/styles/_layouts.scss @@ -174,8 +174,9 @@ p:last-child { margin-block-end: 0; } .justify-center { justify-content: center; } .items-baseline { align-items: baseline; } -.flex-gap-3 {gap: .75rem;} -.flex-gap-1 {gap: .25rem;} +.flex-gap-3 { gap: .75rem; } +.flex-gap-2 { gap: .5rem; } +.flex-gap-1 { gap: .25rem; } .flex-presenter, .inline-presenter { flex-wrap: nowrap; @@ -325,6 +326,7 @@ p:last-child { margin-block-end: 0; } .mx-3 { margin: 0 .75rem; } .my-4 { margin: 1rem 0; } +.pl-1 { padding-left: .25rem; } .pl-2 { padding-left: .5rem; } .pl-4 { padding-left: 1rem; } .pl-8 { padding-left: 2rem; } @@ -383,6 +385,8 @@ p:last-child { margin-block-end: 0; } .h-full { height: 100%; } .h-2 { height: .5rem; } +.h-6 { height: 1.5rem; } +.h-7 { height: 1.75rem; } .h-8 { height: 2rem; } .h-9 { height: 2.25rem; } .h-16 { height: 4rem; } @@ -390,8 +394,10 @@ p:last-child { margin-block-end: 0; } .w-full { width: 100%; } .w-2 { width: .5rem; } .w-9 { width: 2.25rem; } -.w-85 {width: 21.25rem; } -.w-165 {width: 41.25rem; } +.w-14 { width: 3.5rem; } +.w-16 { width: 4rem; } +.w-85 { width: 21.25rem; } +.w-165 { width: 41.25rem; } .min-w-0 { min-width: 0; } .min-w-4 { min-width: 1rem; } .min-w-9 { min-width: 2.25rem; } diff --git a/packages/ui/lang/en.json b/packages/ui/lang/en.json index 5f61218aff..3db7d0aef8 100644 --- a/packages/ui/lang/en.json +++ b/packages/ui/lang/en.json @@ -1,6 +1,6 @@ { "string": { - "EditBoxPlaceholder": "placeholder", + "EditBoxPlaceholder": "Type text...", "Ok": "Ok", "Cancel": "Cancel", "Minutes": "{minutes, plural, =0 {less than a minute ago} =1 {a minute ago} other {# minutes ago}}", diff --git a/packages/ui/src/colors.ts b/packages/ui/src/colors.ts index ac4991fe26..69139435f6 100644 --- a/packages/ui/src/colors.ts +++ b/packages/ui/src/colors.ts @@ -12,6 +12,7 @@ export const ChetwodeBlueColor = '#6F7BC5' // dark blue export const SalmonColor = '#F28469' // salmon export const SeaBuckthornColor = '#F2994A' // orange (warning) export const FlamingoColor = '#EB5757' // red (error) +export const LinkWaterColor = '#C9CBCD' const blackColors = Object.freeze([ FeijoaColor, diff --git a/plugins/board-assets/lang/en.json b/plugins/board-assets/lang/en.json index 2814887b9d..2e7eccec9d 100644 --- a/plugins/board-assets/lang/en.json +++ b/plugins/board-assets/lang/en.json @@ -1,5 +1,6 @@ { "string": { + "Name": "Name", "CreateBoard": "Create board", "CreateCard": "Create card", "CardName": "Card name", @@ -32,6 +33,9 @@ "Labels": "Labels", "CreateLabel": "Create a new label", "SearchLabels": "Search labels...", + "SelectColor": "Select a color", + "NoColor": "No color.", + "NoColorInfo": "This won't show up on the front of cards.", "Checklist": "Checklist", "Dates": "Dates", "Attachments": "Attachments", diff --git a/plugins/board-assets/lang/ru.json b/plugins/board-assets/lang/ru.json index 924a5fb060..a55772cd32 100644 --- a/plugins/board-assets/lang/ru.json +++ b/plugins/board-assets/lang/ru.json @@ -1,5 +1,6 @@ { "string": { + "Name": "Название", "CreateBoard": "Создать", "CreateCard": "Создать", "CardName": "Название", @@ -32,6 +33,9 @@ "Labels": "Метки", "CreateLabel": "Создать", "SearchLabels": "Поиск...", + "SelectColor": "Выберите цвет", + "NoColor": "Без цвета.", + "NoColorInfo": "Не будет показываться на доске.", "Checklist": "Списки", "Dates": "Дата", "Attachments": "Прикрепленное", diff --git a/plugins/board-resources/src/components/popups/CardLabelsEditor.svelte b/plugins/board-resources/src/components/popups/CardLabelsEditor.svelte new file mode 100644 index 0000000000..33310d2e95 --- /dev/null +++ b/plugins/board-resources/src/components/popups/CardLabelsEditor.svelte @@ -0,0 +1,157 @@ + + +
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+ +
+ {#each colorGroups as colorGroup} +
+ {#each colorGroup as color} +
+ selectColor(color)}> + {#if selected.color === color} +
+ +
+ {/if} +
+
+ {/each} +
+ {/each} +
+
+ selectColor(hiddenColor, true)}> + {#if selected.isHidden} +
+ +
+ {/if} +
+
+
+
+
+
+
+
+
+ +
diff --git a/plugins/board-resources/src/components/popups/CardLabelsPicker.svelte b/plugins/board-resources/src/components/popups/CardLabelsPicker.svelte index 68fbb65fc2..36be6c6957 100644 --- a/plugins/board-resources/src/components/popups/CardLabelsPicker.svelte +++ b/plugins/board-resources/src/components/popups/CardLabelsPicker.svelte @@ -2,20 +2,35 @@ import { Card, CardLabel } from '@anticrm/board' import { Ref } from '@anticrm/core' import { getClient } from '@anticrm/presentation' - import { Button, EditBox, Icon, IconEdit, IconCheck, Label, numberToHexColor, numberToRGB } from '@anticrm/ui' + import { + Button, + EditBox, + Icon, + IconEdit, + IconCheck, + IconClose, + Label, + numberToHexColor, + numberToRGB + } from '@anticrm/ui' + import { createEventDispatcher } from 'svelte' import board from '../../plugin' import { getBoardLabels } from '../../utils/BoardUtils' import { updateCard } from '../../utils/CardUtils' export let object: Card + export let search: string | undefined = undefined + export let onEdit: (label: CardLabel) => void + export let onCreate: () => void const client = getClient() - let search: string | undefined = undefined + let labels = object.labels ?? [] let boardCardLabels: CardLabel[] = [] let filteredLabels: CardLabel[] = [] let hovered: Ref | undefined = undefined + const dispatch = createEventDispatcher() function applySearch() { if (!search || search.trim().length <= 0) { @@ -27,14 +42,14 @@ filteredLabels = boardCardLabels.filter((l) => l.title?.toUpperCase().includes(text) ?? false) } - async function fetchBoardLabels() { + async function fetchBoardLabels () { if (object.space) { boardCardLabels = await getBoardLabels(client, object.space) applySearch() } } - function toggle(label: CardLabel) { + function toggle (label: CardLabel) { if (!object) { return } @@ -52,13 +67,21 @@ -
-
-
+
+
-
+
toggle(label)}> {label.title ?? ''} {#if labels.includes(label._id)} @@ -96,10 +119,10 @@
{/if}
-
{/each}
-
diff --git a/plugins/board-resources/src/components/popups/CardLabelsPopup.svelte b/plugins/board-resources/src/components/popups/CardLabelsPopup.svelte new file mode 100644 index 0000000000..3fc5e2b610 --- /dev/null +++ b/plugins/board-resources/src/components/popups/CardLabelsPopup.svelte @@ -0,0 +1,40 @@ + + +{#if editMode.isEdit} + setEditMode(false, undefined)} /> +{:else} + setEditMode(true, undefined)} + onEdit={(o) => setEditMode(true, o)} /> +{/if} diff --git a/plugins/board-resources/src/components/presenters/ColorPresenter.svelte b/plugins/board-resources/src/components/presenters/ColorPresenter.svelte new file mode 100644 index 0000000000..3288fbe5c8 --- /dev/null +++ b/plugins/board-resources/src/components/presenters/ColorPresenter.svelte @@ -0,0 +1,33 @@ + + +{#if value} +
{ + isHovered = true + }} + on:focus={() => { + isHovered = true + }} + on:mouseout={() => { + isHovered = false + }} + on:blur={() => { + isHovered = false + }}> + +
+{/if} diff --git a/plugins/board-resources/src/components/presenters/DatePresenter.svelte b/plugins/board-resources/src/components/presenters/DatePresenter.svelte index 71c7d9a1d0..2d0561aac3 100644 --- a/plugins/board-resources/src/components/presenters/DatePresenter.svelte +++ b/plugins/board-resources/src/components/presenters/DatePresenter.svelte @@ -6,18 +6,19 @@ export let value: Card const client = getClient() - const {date} = value + const { date } = value let isChecked = date?.isChecked - function update(){ + function update() { if (isChecked === undefined) return - client.update(value, {date: {...date, isChecked}}) + client.update(value, { date: { ...date, isChecked } }) } + {#if date}
- +
{#if date.startDate} diff --git a/plugins/board-resources/src/components/presenters/LabelPresenter.svelte b/plugins/board-resources/src/components/presenters/LabelPresenter.svelte index cffe0cf669..ab19084877 100644 --- a/plugins/board-resources/src/components/presenters/LabelPresenter.svelte +++ b/plugins/board-resources/src/components/presenters/LabelPresenter.svelte @@ -1,18 +1,14 @@ {#if value} -
- -
+ +
{value.title ?? ''}
+
{/if} diff --git a/plugins/board-resources/src/index.ts b/plugins/board-resources/src/index.ts index ac45156a4e..8f2239870a 100644 --- a/plugins/board-resources/src/index.ts +++ b/plugins/board-resources/src/index.ts @@ -25,9 +25,11 @@ import EditCard from './components/EditCard.svelte' import KanbanCard from './components/KanbanCard.svelte' import TemplatesIcon from './components/TemplatesIcon.svelte' import KanbanView from './components/KanbanView.svelte' -import CardLabelsPicker from './components/popups/CardLabelsPicker.svelte' +import CardLabelsPopup from './components/popups/CardLabelsPopup.svelte' import MoveView from './components/popups/MoveCard.svelte' import DateRangePicker from './components/popups/DateRangePicker.svelte' +import CardLabelPresenter from './components/presenters/LabelPresenter.svelte' +import CardDatePresenter from './components/presenters/DatePresenter.svelte' import { addCurrentUser, canAddCurrentUser, isArchived, isUnarchived } from './utils/CardUtils' async function showMoveCardPopup (object: Card): Promise { @@ -39,7 +41,7 @@ async function showDatePickerPopup (object: Card): Promise { } async function showCardLabelsPopup (object: Card): Promise { - showPopup(CardLabelsPicker, { object }) + showPopup(CardLabelsPopup, { object }) } export default async (): Promise => ({ @@ -49,6 +51,8 @@ export default async (): Promise => ({ EditCard, KanbanCard, CardPresenter, + CardDatePresenter, + CardLabelPresenter, TemplatesIcon, KanbanView, BoardPresenter diff --git a/plugins/board-resources/src/plugin.ts b/plugins/board-resources/src/plugin.ts index a6370bd2a0..6415c83e5f 100644 --- a/plugins/board-resources/src/plugin.ts +++ b/plugins/board-resources/src/plugin.ts @@ -19,6 +19,7 @@ import type { AnyComponent } from '@anticrm/ui' export default mergeIds(boardId, board, { string: { + Name: '' as IntlString, BoardName: '' as IntlString, MakePrivate: '' as IntlString, MakePrivateDescription: '' as IntlString, @@ -53,6 +54,9 @@ export default mergeIds(boardId, board, { Labels: '' as IntlString, CreateLabel: '' as IntlString, SearchLabels: '' as IntlString, + SelectColor: '' as IntlString, + NoColor: '' as IntlString, + NoColorInfo: '' as IntlString, Checklist: '' as IntlString, Dates: '' as IntlString, Attachments: '' as IntlString, @@ -96,9 +100,6 @@ export default mergeIds(boardId, board, { NullDate: '' as IntlString }, component: { - CreateCustomer: '' as AnyComponent, - CardsPresenter: '' as AnyComponent, - BoardPresenter: '' as AnyComponent, Boards: '' as AnyComponent, EditCard: '' as AnyComponent, Members: '' as AnyComponent, diff --git a/plugins/board-resources/src/utils/BoardUtils.ts b/plugins/board-resources/src/utils/BoardUtils.ts index 3f59c19626..0e3c72143e 100644 --- a/plugins/board-resources/src/utils/BoardUtils.ts +++ b/plugins/board-resources/src/utils/BoardUtils.ts @@ -2,7 +2,19 @@ import core, { Ref, TxOperations } from '@anticrm/core' import board, { Board, CardLabel } from '@anticrm/board' import type { KanbanTemplate } from '@anticrm/task' import { createKanban } from '@anticrm/task' -import { hexColorToNumber, FernColor, FlamingoColor, MalibuColor, MoodyBlueColor, SeaBuckthornColor } from '@anticrm/ui' +import { + hexColorToNumber, + FernColor, + FlamingoColor, + MalibuColor, + MediumTurquoiseColor, + MoodyBlueColor, + SeaBuckthornColor, + FeijoaColor, + EastSideColor, + SalmonColor, + SeagullColor +} from '@anticrm/ui' export async function createBoard ( client: TxOperations, @@ -26,22 +38,44 @@ export async function getBoardLabels (client: TxOperations, boardRef: Ref return await client.findAll(board.class.CardLabel, { attachedTo: boardRef }) } +export function getBoardAvailableColors (): string[] { + return [ + FernColor, + SeaBuckthornColor, + FlamingoColor, + MalibuColor, + MoodyBlueColor, + FeijoaColor, + EastSideColor, + MediumTurquoiseColor, + SalmonColor, + SeagullColor + ] +} + export async function createBoardLabels (client: TxOperations, boardRef: Ref): Promise { await Promise.all([ - createCardLabel(client, boardRef, FernColor), - createCardLabel(client, boardRef, SeaBuckthornColor), - createCardLabel(client, boardRef, FlamingoColor), - createCardLabel(client, boardRef, MalibuColor), - createCardLabel(client, boardRef, MoodyBlueColor) + createCardLabel(client, boardRef, hexColorToNumber(FernColor)), + createCardLabel(client, boardRef, hexColorToNumber(SeaBuckthornColor)), + createCardLabel(client, boardRef, hexColorToNumber(FlamingoColor)), + createCardLabel(client, boardRef, hexColorToNumber(MalibuColor)), + createCardLabel(client, boardRef, hexColorToNumber(MoodyBlueColor)) ]) } -export async function createCardLabel (client: TxOperations, boardRef: Ref, color: string, title?: string): Promise { +export async function createCardLabel ( + client: TxOperations, + boardRef: Ref, + color: number, + title?: string, + isHidden?: boolean +): Promise { await client.createDoc(board.class.CardLabel, core.space.Model, { attachedTo: boardRef, attachedToClass: board.class.Board, - collection: '', - color: hexColorToNumber(color), - title: title ?? '' + collection: 'labels', + color, + title: title ?? '', + isHidden: isHidden ?? false }) } diff --git a/plugins/board/src/index.ts b/plugins/board/src/index.ts index b44cddb399..122d2266f4 100644 --- a/plugins/board/src/index.ts +++ b/plugins/board/src/index.ts @@ -15,7 +15,7 @@ // import { Employee } from '@anticrm/contact' -import type { AttachedDoc, Class, TxOperations as Client, Doc, Markup, Ref, Timestamp } from '@anticrm/core' +import type { AttachedDoc, Class, TxOperations as Client, Doc, Markup, Ref, Timestamp, Obj } from '@anticrm/core' import type { Asset, IntlString, Plugin, Resource } from '@anticrm/platform' import { plugin } from '@anticrm/platform' import type { KanbanTemplateSpace, SpaceWithStates, Task } from '@anticrm/task' @@ -44,12 +44,13 @@ export interface BoardView extends SpaceWithStates { export interface CardLabel extends AttachedDoc { title: string color: number + isHidden?: boolean } /** * @public */ -export interface CardDate { +export interface CardDate extends Obj { dueDate?: Timestamp isChecked?: boolean startDate?: Timestamp @@ -110,6 +111,7 @@ const boards = plugin(boardId, { Board: '' as Ref>, Card: '' as Ref>, CardAction: '' as Ref>, + CardDate: '' as Ref>, CardLabel: '' as Ref> }, icon: {