Skip to content

Commit

Permalink
Merge pull request smartxworks#725 from smartxworks/feat/datasource-tag
Browse files Browse the repository at this point in the history
feat(editor): support use tags to filter data source
  • Loading branch information
tanbowensg authored Sep 7, 2023
2 parents 1d8287a ed0a1fd commit 3e97731
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 33 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/application.ts
Original file line number Diff line number Diff line change
@@ -1,12 1,12 @@
import { Metadata } from './metadata';
import { ApplicationMetadata } from './metadata';
import { parseVersion, Version } from './version';
import { type PropsBeforeEvaled } from './schema';
// spec

export type Application = {
version: string;
kind: 'Application';
metadata: Metadata;
metadata: ApplicationMetadata;
spec: {
components: ComponentSchema[];
};
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 10,10 @@ export type Metadata<
isDataSource?: boolean;
};

export type ApplicationMetadata = Metadata<{
componentsTagMap?: Record<string, string[]>;
}>;

type ComponentCategory =
| (string & {})
| 'Layout'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 12,7 @@ import ErrorBoundary from '../ErrorBoundary';
import { StyleTraitForm } from './StyleTraitForm';
import { EditorServices } from '../../types';
import { FormSection } from './FormSection';
import { TagForm } from './TagForm';

// avoid the expression tip would be covered
const ComponentFormStyle = css`
Expand Down Expand Up @@ -127,6 128,10 @@ export const ComponentForm: React.FC<Props> = observer(props => {
title: 'Traits',
node: <GeneralTraitFormList component={selectedComponent} services={services} />,
},
{
title: 'Tags',
node: <TagForm services={services} />,
},
];

return (
Expand Down
40 changes: 40 additions & 0 deletions packages/editor/src/components/ComponentForm/TagForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 1,40 @@
import React from 'react';
import { Text, VStack, Checkbox } from '@chakra-ui/react';
import { EditorServices } from '../../types';

type Props = {
services: EditorServices;
};

export const TagForm: React.FC<Props> = ({ services }) => {
const { editorStore } = services;
const tagMap = editorStore.app.metadata.annotations?.componentsTagMap;
const componentId = editorStore.selectedComponentId;

if (!tagMap) return null;

const tagItems = Object.keys(tagMap).map(tag => {
const checked = tagMap[tag].includes(editorStore.selectedComponentId);

const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newTagMap = tagMap;
const nextValue = e.target.checked;
if (nextValue) {
newTagMap[tag].push(componentId);
} else {
newTagMap[tag] = tagMap[tag].filter(id => id !== componentId);
}
services.editorStore.appStorage.saveAppAnnotations({
componentsTagMap: newTagMap,
});
};

return (
<Checkbox key={tag} defaultChecked={checked} onChange={onChange}>
<Text>{tag}</Text>
</Checkbox>
);
});

return <VStack align="flex-start">{tagItems}</VStack>;
};
34 changes: 20 additions & 14 deletions packages/editor/src/components/ComponentsList/ComponentFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,37 57,43 @@ const FilterIcon = createIcon({
});

type FilterProps = {
versions: string[];
checkedVersions: string[];
setCheckedVersions: React.Dispatch<React.SetStateAction<string[]>>;
options: string[];
checkedOptions: string[];
onChange: (v: string[]) => void;
};

export const ComponentFilter: React.FC<FilterProps> = ({
versions,
checkedVersions,
setCheckedVersions,
options,
checkedOptions,
onChange,
}) => {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const checked = e.target.checked;
const value = e.target.value;
const newCheckedVersions = [...checkedVersions];
const newCheckedOptions = [...checkedOptions];
if (checked) {
newCheckedVersions.push(value);
newCheckedOptions.push(value);
} else {
const idx = newCheckedVersions.findIndex(c => c === value);
newCheckedVersions.splice(idx, 1);
const idx = newCheckedOptions.findIndex(c => c === value);
newCheckedOptions.splice(idx, 1);
}
setCheckedVersions(newCheckedVersions);
onChange(newCheckedOptions);
};

const checkVersion = (version: string) => {
return checkedVersions.includes(version);
return checkedOptions.includes(version);
};

return (
<Popover isLazy closeOnBlur placement="bottom">
<PopoverTrigger>
<Button _focus={{ boxShadow: 'none' }} bg="transparent" h="1.75rem" size="sm">
<Button
_focus={{ boxShadow: 'none' }}
colorScheme="blue"
variant={checkedOptions.length > 0 ? 'solid' : 'ghost'}
h="1.75rem"
size="sm"
>
<FilterIcon />
</Button>
</PopoverTrigger>
Expand All @@ -110,7 116,7 @@ export const ComponentFilter: React.FC<FilterProps> = ({
width="200px"
_focus={{ boxShadow: 'none' }}
>
{versions.map(version => {
{options.map(version => {
return (
<Checkbox
key={version}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 113,9 @@ export const ComponentList: React.FC<Props> = ({ services }) => {
/>
<InputRightElement>
<ComponentFilter
versions={versions}
checkedVersions={checkedVersions}
setCheckedVersions={setCheckedVersions}
options={versions}
checkedOptions={checkedVersions}
onChange={setCheckedVersions}
/>
</InputRightElement>
</InputGroup>
Expand Down
25 changes: 22 additions & 3 deletions packages/editor/src/components/DataSource/DataSourceList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 1,4 @@
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import {
VStack,
Spacer,
Expand All @@ -21,6 21,7 @@ import { generateDefaultValueFromSpec } from '@sunmao-ui/shared';
import { JSONSchema7 } from 'json-schema';
import { ToolMenuTabs } from '../../constants/enum';
import { ComponentSearch } from '../StructureTree/ComponentSearch';
import { ComponentSchema } from '@sunmao-ui/core';

interface Props {
services: EditorServices;
Expand All @@ -30,8 31,23 @@ export const DataSourceList: React.FC<Props> = props => {
const { services } = props;
const { editorStore, eventBus, registry } = services;
const { dataSources, setSelectedComponentId, setToolMenuTab } = editorStore;
const tDataSources = dataSources.filter(ds => ds.type === 'core/v1/dummy');
const cDataSources = dataSources.filter(ds => ds.type !== 'core/v1/dummy');
const [checkedTags, setCheckedTags] = useState<string[]>([]);
const tagMap = services.editorStore.app.metadata.annotations?.componentsTagMap;
// filter datasources by tag
const filteredDataSources = useMemo(() => {
if (!tagMap || checkedTags.length === 0) {
return dataSources;
}

const dataSourceIds = checkedTags.reduce<string[]>((result, curr) => {
return result.concat(tagMap[curr]);
}, []);

return dataSources.filter(d => dataSourceIds.includes(d.id)) as ComponentSchema[];
}, [checkedTags, dataSources, tagMap]);

const tDataSources = filteredDataSources.filter(ds => ds.type === 'core/v1/dummy');
const cDataSources = filteredDataSources.filter(ds => ds.type !== 'core/v1/dummy');
const cdsMap = groupBy(cDataSources, c => c.type);
const tdsMap = groupBy(tDataSources, c => c.traits[0]?.type || '');
const cdsGroups = Object.keys(cdsMap).map(type => {
Expand Down Expand Up @@ -165,6 181,9 @@ export const DataSourceList: React.FC<Props> = props => {
components={dataSources}
onChange={id => setSelectedComponentId(id)}
services={props.services}
tags={tagMap ? Object.keys(tagMap) : []}
checkedTags={checkedTags}
onTagsChange={v => setCheckedTags(v)}
/>
<Accordion
reduceMotion
Expand Down
36 changes: 25 additions & 11 deletions packages/editor/src/components/StructureTree/ComponentSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 4,14 @@ import { css } from '@emotion/css';
import { observer } from 'mobx-react-lite';
import { ComponentSchema } from '@sunmao-ui/core';
import { Select } from '@sunmao-ui/editor-sdk';
import { ComponentFilter } from '../ComponentsList/ComponentFilter';
import { HStack } from '@chakra-ui/react';
type Props = {
components: ComponentSchema[];
onChange: (id: string) => void;
tags: string[];
checkedTags: string[];
onTagsChange: (v: string[]) => void;
services: EditorServices;
};

Expand All @@ -28,7 33,7 @@ const SelectStyle = css`
`;

export const ComponentSearch: React.FC<Props> = observer(props => {
const { components, onChange } = props;
const { components, onChange, tags, checkedTags, onTagsChange } = props;

const options = useMemo(() => {
return components.map(c => ({
Expand All @@ -38,15 43,24 @@ export const ComponentSearch: React.FC<Props> = observer(props => {
}, [components]);

return (
<Select
bordered={false}
className={SelectStyle}
placeholder="Search component"
onSelect={onChange}
showArrow={false}
showSearch
style={{ width: '100%' }}
options={options}
/>
<HStack width="full">
<Select
bordered={false}
className={SelectStyle}
placeholder="Search component"
onSelect={onChange}
showArrow={false}
showSearch
style={{ width: '100%' }}
options={options}
/>
{tags.length > 0 ? (
<ComponentFilter
options={tags}
checkedOptions={checkedTags}
onChange={v => onTagsChange(v)}
/>
) : null}
</HStack>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 102,9 @@ export const StructureTree: React.FC<Props> = observer(props => {
Components
</Text>
<ComponentSearch
tags={[]}
checkedTags={[]}
onTagsChange={() => null}
components={components}
onChange={id => setSelectedComponentId(id)}
services={props.services}
Expand Down
15 changes: 15 additions & 0 deletions packages/editor/src/services/AppStorage.ts
Original file line number Diff line number Diff line change
@@ -1,6 1,7 @@
import { observable, makeObservable, action, toJS } from 'mobx';
import {
Application,
ApplicationMetadata,
ComponentSchema,
Module,
ModuleMethodSpec,
Expand Down Expand Up @@ -159,6 160,20 @@ export class AppStorage {
this.saveApplication();
}

saveAppAnnotations(annotations: ApplicationMetadata['annotations']) {
if (!annotations) return;
const newApp = produce(toJS(this.app), draft => {
if (!draft.metadata.annotations) {
draft.metadata.annotations = {};
}
Object.keys(annotations).forEach(key => {
draft.metadata.annotations![key] = annotations[key];
});
});
this.setApp(newApp);
this.saveApplication();
}

saveModuleMetaData(
{ originName, originVersion }: { originName: string; originVersion: string },
{
Expand Down

0 comments on commit 3e97731

Please sign in to comment.