feat: support group by status in kanban mode, resolved #40

This commit is contained in:
QiShaoXuan
2022-08-02 19:10:51 +08:00
parent 418e260ade
commit f18f51ba7c
10 changed files with 76 additions and 41 deletions

View File

@@ -41,6 +41,7 @@ const getKanbanColor = (
return DEFAULT_COLOR; return DEFAULT_COLOR;
} }
if ( if (
group.type === PropertyType.Status ||
group.type === PropertyType.Select || group.type === PropertyType.Select ||
group.type === PropertyType.MultiSelect || group.type === PropertyType.MultiSelect ||
group.type === DEFAULT_GROUP_ID group.type === DEFAULT_GROUP_ID

View File

@@ -4,7 +4,7 @@ import { ModifyPanelContentProps } from './types';
import { StyledDivider, StyledPopoverSubTitle } from '../StyledComponent'; import { StyledDivider, StyledPopoverSubTitle } from '../StyledComponent';
import { BasicSelect } from './Select'; import { BasicSelect } from './Select';
import { InformationProperty, InformationValue } from '../../recast-block'; import { InformationProperty, InformationValue } from '../../recast-block';
import { genInitialOptions, getPendantIconsConfigByName } from '../utils'; import { generateInitialOptions, getPendantIconsConfigByName } from '../utils';
export default (props: ModifyPanelContentProps) => { export default (props: ModifyPanelContentProps) => {
const { onPropertyChange, onValueChange, initialValue, property } = props; const { onPropertyChange, onValueChange, initialValue, property } = props;
@@ -38,7 +38,7 @@ export default (props: ModifyPanelContentProps) => {
}} }}
initialOptions={ initialOptions={
propProperty?.emailOptions || propProperty?.emailOptions ||
genInitialOptions( generateInitialOptions(
property?.type, property?.type,
getPendantIconsConfigByName('Email') getPendantIconsConfigByName('Email')
) )
@@ -66,7 +66,7 @@ export default (props: ModifyPanelContentProps) => {
}} }}
initialOptions={ initialOptions={
propProperty?.phoneOptions || propProperty?.phoneOptions ||
genInitialOptions( generateInitialOptions(
property?.type, property?.type,
getPendantIconsConfigByName('Phone') getPendantIconsConfigByName('Phone')
) )
@@ -94,7 +94,7 @@ export default (props: ModifyPanelContentProps) => {
}} }}
initialOptions={ initialOptions={
propProperty?.locationOptions || propProperty?.locationOptions ||
genInitialOptions( generateInitialOptions(
property?.type, property?.type,
getPendantIconsConfigByName('Location') getPendantIconsConfigByName('Location')
) )

View File

@@ -21,7 +21,7 @@ import {
} from '@toeverything/components/ui'; } from '@toeverything/components/ui';
import { HighLightIconInput } from './IconInput'; import { HighLightIconInput } from './IconInput';
import { PendantConfig, IconNames, OptionIdType, OptionType } from '../types'; import { PendantConfig, IconNames, OptionIdType, OptionType } from '../types';
import { genBasicOption } from '../utils'; import { generateBasicOption } from '../utils';
type OptionItemType = { type OptionItemType = {
option: OptionType; option: OptionType;
@@ -66,7 +66,7 @@ export const BasicSelect = ({
const [selectIds, setSelectIds] = useState<OptionIdType[]>(initialValue); const [selectIds, setSelectIds] = useState<OptionIdType[]>(initialValue);
const insertOption = (insertId: OptionIdType) => { const insertOption = (insertId: OptionIdType) => {
const newOption = genBasicOption({ const newOption = generateBasicOption({
index: options.length + 1, index: options.length + 1,
iconConfig, iconConfig,
}); });

View File

@@ -1,5 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { nanoid } from 'nanoid';
import { Input, Option, Select, Tooltip } from '@toeverything/components/ui'; import { Input, Option, Select, Tooltip } from '@toeverything/components/ui';
import { HelpCenterIcon } from '@toeverything/components/icons'; import { HelpCenterIcon } from '@toeverything/components/icons';
import { AsyncBlock } from '../../editor'; import { AsyncBlock } from '../../editor';
@@ -15,13 +14,13 @@ import {
StyledPopoverSubTitle, StyledPopoverSubTitle,
StyledPopoverWrapper, StyledPopoverWrapper,
} from '../StyledComponent'; } from '../StyledComponent';
import { genInitialOptions, getPendantConfigByType } from '../utils'; import {
generateRandomFieldName,
generateInitialOptions,
getPendantConfigByType,
} from '../utils';
import { useOnCreateSure } from './hooks'; import { useOnCreateSure } from './hooks';
const upperFirst = (str: string) => {
return `${str[0].toUpperCase()}${str.slice(1)}`;
};
export const CreatePendantPanel = ({ export const CreatePendantPanel = ({
block, block,
onSure, onSure,
@@ -35,7 +34,7 @@ export const CreatePendantPanel = ({
useEffect(() => { useEffect(() => {
selectedOption && selectedOption &&
setFieldName(upperFirst(`${selectedOption.type}#${nanoid(4)}`)); setFieldName(generateRandomFieldName(selectedOption.type));
}, [selectedOption]); }, [selectedOption]);
return ( return (
@@ -93,7 +92,7 @@ export const CreatePendantPanel = ({
<PendantModifyPanel <PendantModifyPanel
type={selectedOption.type} type={selectedOption.type}
// Select, MultiSelect, Status use this props as initial property // Select, MultiSelect, Status use this props as initial property
initialOptions={genInitialOptions( initialOptions={generateInitialOptions(
selectedOption.type, selectedOption.type,
getPendantConfigByType(selectedOption.type) getPendantConfigByType(selectedOption.type)
)} )}

View File

@@ -7,6 +7,7 @@ import {
import { OptionIdType, OptionType } from './types'; import { OptionIdType, OptionType } from './types';
import { pendantConfig } from './config'; import { pendantConfig } from './config';
import { PendantConfig, PendantTypes } from './types'; import { PendantConfig, PendantTypes } from './types';
import { nanoid } from 'nanoid';
type Props = { type Props = {
recastBlockId: string; recastBlockId: string;
blockId: string; blockId: string;
@@ -60,7 +61,7 @@ export const getPendantHistory = ({
return data[recastBlockId] ?? {}; return data[recastBlockId] ?? {};
}; };
export const removePropertyValueRecord = ({ export const removePendantHistory = ({
recastBlockId, recastBlockId,
propertyId, propertyId,
}: { }: {
@@ -107,7 +108,7 @@ export const getOfficialSelected = ({
.map(id => { .map(id => {
return tempOptions.findIndex((o: OptionType) => o.id === id); return tempOptions.findIndex((o: OptionType) => o.id === id);
}) })
.filter(index => index != -1); .filter(index => index !== -1);
selectedId = selectedIndex.map((index: number) => { selectedId = selectedIndex.map((index: number) => {
return options[index].id; return options[index].id;
}); });
@@ -130,7 +131,7 @@ export const getPendantIconsConfigByName = (
return pendantConfig[pendantName]; return pendantConfig[pendantName];
}; };
export const genBasicOption = ({ export const generateBasicOption = ({
index, index,
iconConfig, iconConfig,
name = '', name = '',
@@ -159,22 +160,22 @@ export const genBasicOption = ({
/** /**
* Status Pendant is a Select Pendant built-in some options * Status Pendant is a Select Pendant built-in some options
* **/ * **/
export const genInitialOptions = ( export const generateInitialOptions = (
type: PendantTypes, type: PendantTypes,
iconConfig: PendantConfig iconConfig: PendantConfig
) => { ) => {
if (type === PendantTypes.Status) { if (type === PendantTypes.Status) {
return [ return [
genBasicOption({ index: 0, iconConfig, name: 'No Started' }), generateBasicOption({ index: 0, iconConfig, name: 'No Started' }),
genBasicOption({ generateBasicOption({
index: 1, index: 1,
iconConfig, iconConfig,
name: 'In Progress', name: 'In Progress',
}), }),
genBasicOption({ index: 2, iconConfig, name: 'Complete' }), generateBasicOption({ index: 2, iconConfig, name: 'Complete' }),
]; ];
} }
return [genBasicOption({ index: 0, iconConfig })]; return [generateBasicOption({ index: 0, iconConfig })];
}; };
export const checkPendantForm = ( export const checkPendantForm = (
@@ -222,3 +223,10 @@ export const checkPendantForm = (
return { passed: true, message: 'Check passed !' }; return { passed: true, message: 'Check passed !' };
}; };
const upperFirst = (str: string) => {
return `${str[0].toUpperCase()}${str.slice(1)}`;
};
export const generateRandomFieldName = (type: PendantTypes) =>
upperFirst(`${type}#${nanoid(4)}`);

View File

@@ -10,6 +10,12 @@ import {
} from '../recast-block/types'; } from '../recast-block/types';
import type { DefaultGroup, KanbanGroup } from './types'; import type { DefaultGroup, KanbanGroup } from './types';
import { DEFAULT_GROUP_ID } from './types'; import { DEFAULT_GROUP_ID } from './types';
import {
generateInitialOptions,
generateRandomFieldName,
getPendantIconsConfigByName,
} from '../block-pendant/utils';
import { SelectOption } from '../recast-block';
/** /**
* - If the `groupBy` is `SelectProperty` or `MultiSelectProperty`, return `(Multi)SelectProperty.options`. * - If the `groupBy` is `SelectProperty` or `MultiSelectProperty`, return `(Multi)SelectProperty.options`.
@@ -23,6 +29,7 @@ export const getGroupOptions = async (
return []; return [];
} }
switch (groupBy.type) { switch (groupBy.type) {
case PropertyType.Status:
case PropertyType.Select: case PropertyType.Select:
case PropertyType.MultiSelect: { case PropertyType.MultiSelect: {
return groupBy.options.map(option => ({ return groupBy.options.map(option => ({
@@ -57,6 +64,9 @@ const isValueBelongOption = (
case PropertyType.MultiSelect: { case PropertyType.MultiSelect: {
return propertyValue.value.some(i => i === option.id); return propertyValue.value.some(i => i === option.id);
} }
case PropertyType.Status: {
return propertyValue.value === option.id;
}
// case PropertyType.Text: { // case PropertyType.Text: {
// TOTODO:DO support this type // TOTODO:DO support this type
// } // }
@@ -107,6 +117,7 @@ export const moveCardToGroup = async (
success = await removeValue(groupById); success = await removeValue(groupById);
return false; return false;
} }
switch (group.type) { switch (group.type) {
case PropertyType.Select: { case PropertyType.Select: {
success = await setValue({ success = await setValue({
@@ -116,6 +127,14 @@ export const moveCardToGroup = async (
}); });
break; break;
} }
case PropertyType.Status: {
success = await setValue({
id: groupById,
type: group.type,
value: group.id,
});
break;
}
case PropertyType.MultiSelect: { case PropertyType.MultiSelect: {
success = await setValue({ success = await setValue({
id: groupById, id: groupById,
@@ -194,14 +213,18 @@ export const genDefaultGroup = (groupBy: RecastMetaProperty): DefaultGroup => ({
items: [], items: [],
}); });
export const DEFAULT_GROUP_BY_PROPERTY = { export const generateDefaultGroupByProperty = (): {
name: 'Status', name: string;
options: [ options: Omit<SelectOption, 'id'>[];
{ name: 'No Started', color: '#E53535', background: '#FFCECE' }, type: PropertyType.Status;
{ name: 'In Progress', color: '#A77F1A', background: '#FFF5AB' }, } => ({
{ name: 'Complete', color: '#3C8867', background: '#C5FBE0' }, name: generateRandomFieldName(PropertyType.Status),
], type: PropertyType.Status,
}; options: generateInitialOptions(
PropertyType.Status,
getPendantIconsConfigByName(PropertyType.Status)
),
});
/** /**
* Unwrap blocks from the grid recursively. * Unwrap blocks from the grid recursively.

View File

@@ -7,6 +7,7 @@ export const useKanbanGroup = (groupBy: RecastMetaProperty) => {
const { updateSelect } = useSelectProperty(); const { updateSelect } = useSelectProperty();
switch (groupBy.type) { switch (groupBy.type) {
case PropertyType.Status:
case PropertyType.MultiSelect: case PropertyType.MultiSelect:
case PropertyType.Select: { case PropertyType.Select: {
const { const {

View File

@@ -18,8 +18,8 @@ import {
import { supportChildren } from '../utils'; import { supportChildren } from '../utils';
import { import {
calcCardGroup, calcCardGroup,
DEFAULT_GROUP_BY_PROPERTY,
genDefaultGroup, genDefaultGroup,
generateDefaultGroupByProperty,
getCardGroup, getCardGroup,
getGroupOptions, getGroupOptions,
moveCardToAfter, moveCardToAfter,
@@ -48,6 +48,7 @@ export const useRecastKanbanGroupBy = () => {
// Add other type groupBy support // Add other type groupBy support
const supportedGroupBy = getProperties().filter( const supportedGroupBy = getProperties().filter(
prop => prop =>
prop.type === PropertyType.Status ||
prop.type === PropertyType.Select || prop.type === PropertyType.Select ||
prop.type === PropertyType.MultiSelect prop.type === PropertyType.MultiSelect
); );
@@ -88,7 +89,8 @@ export const useRecastKanbanGroupBy = () => {
// TODO: support other property type // TODO: support other property type
if ( if (
groupByProperty.type !== PropertyType.Select && groupByProperty.type !== PropertyType.Select &&
groupByProperty.type !== PropertyType.MultiSelect groupByProperty.type !== PropertyType.MultiSelect &&
groupByProperty.type !== PropertyType.Status
) { ) {
console.warn('Not support groupBy type', groupByProperty); console.warn('Not support groupBy type', groupByProperty);
@@ -134,7 +136,7 @@ export const useInitKanbanEffect = ():
} }
// 3. no group by, no properties // 3. no group by, no properties
// create a new property and set it as group by // create a new property and set it as group by
const prop = await createSelect(DEFAULT_GROUP_BY_PROPERTY); const prop = await createSelect(generateDefaultGroupByProperty());
await setGroupBy(prop.id); await setGroupBy(prop.id);
}; };

View File

@@ -46,7 +46,10 @@ export type DefaultGroup = KanbanGroupBase & {
type SelectGroup = KanbanGroupBase & type SelectGroup = KanbanGroupBase &
SelectOption & { SelectOption & {
type: PropertyType.Select | PropertyType.MultiSelect; type:
| PropertyType.Select
| PropertyType.MultiSelect
| PropertyType.Status;
}; };
type TextGroup = KanbanGroupBase & { type TextGroup = KanbanGroupBase & {

View File

@@ -257,14 +257,12 @@ export const getRecastItemValue = (block: RecastItem | AsyncBlock) => {
const isSelectLikeProperty = ( const isSelectLikeProperty = (
metaProperty?: RecastMetaProperty metaProperty?: RecastMetaProperty
): metaProperty is SelectProperty | MultiSelectProperty => { ): metaProperty is SelectProperty | MultiSelectProperty => {
if ( return !(
!metaProperty || !metaProperty ||
(metaProperty.type !== PropertyType.Select && (metaProperty.type !== PropertyType.Status &&
metaProperty.type !== PropertyType.Select &&
metaProperty.type !== PropertyType.MultiSelect) metaProperty.type !== PropertyType.MultiSelect)
) { );
return false;
}
return true;
}; };
/** /**
@@ -312,7 +310,7 @@ export const useSelectProperty = () => {
}; };
const updateSelect = ( const updateSelect = (
selectProperty: SelectProperty | MultiSelectProperty selectProperty: StatusProperty | SelectProperty | MultiSelectProperty
) => { ) => {
// if (typeof selectProperty === 'string') { // if (typeof selectProperty === 'string') {
// const maybeSelectProperty = getProperty(selectProperty); // const maybeSelectProperty = getProperty(selectProperty);