@spaceone/design-system
Version:
SpaceONE Design System
339 lines (321 loc) • 11.3 kB
text/mdx
import { Meta, Canvas, Story, ArgsTable } from '@storybook/addon-docs/blocks';
import PTree from './PTree.vue';
import PI from '@/foundation/icons/PI.vue'
import PIconButton from '@/inputs/buttons/icon-button/PIconButton.vue'
import PButton from '@/inputs/buttons/button/PButton.vue'
import PTextInput from '@/inputs/input/PTextInput.vue';
import PRadio from '@/inputs/radio/PRadio.vue'
import { reactive, toRefs, computed } from '@vue/composition-api';
import {getTreeList, getTreeData, getTreeNode, getRecursiveInfo, getPermissionInfo} from './mock';
import faker from 'faker';
import './PTree.stories.pcss'
<Meta title='Data Display/Tree' component={PTree}
argTypes={{
editOptions: {
name: 'editOptions',
type: {name: `object`},
description: `Options for inline edit.`,
defaultValue: {},
table: {
type: {
summary: 'object'
},
defaultValue: {
summary: '{}'
},
category: 'props'
},
control: {
type: 'object'
}
},
}}
/>
export const ExpertTemplate = (args, { argTypes }) => ({
components: { PTree, PButton, PTextInput, PI, PIconButton, PRadio },
template: `
<div class="h-full w-full p-4 overflow-auto">
<div class="mb-4">
<p-button style-type="primary1" ="refresh">Refresh</p-button>
<p-button style-type="secondary" ="add">Add node to root</p-button>
<p-button style-type="safe" ="onClickEdit">
{{editMode ? 'Done' : 'Edit' }}
</p-button>
<p-button style-type="primary" ="find">Find node</p-button>
<div class="mt-4">
<p-text-input v-model="target"/>
<p-button style-type="secondary1" ="fetchAndFind">Fetch and find node</p-button>
</div>
<div class="mt-4">
<p-text-input v-model="editText"/>
<p-button style-type="alert" ="updateData">Update selected node</p-button>
</div>
</div>
<p-tree :edit-options="editOptions"
:drag-options="dragOptions"
:select-options="selectOptions"
:toggle-options="toggleOptions"
:data-getter="dataGetter"
:data-setter="dataSetter"
:data-fetcher="dataFetcher"
:get-class-names="getClassNames"
="onInit"
-select="onChangeSelect"
-drag="onStartDrag"
-drag="onUpdateDrag"
-drag="onEndDrag"
>
<template #data="{node}">
{{ node.data.name }} ({{node.data.id}})
</template>
<template #toggle="{node, path, selected}">
<p-radio v-if="node.data.item_type === 'PROJECT'"
:selected="selected"
:value="true"
.stop="changeSelectState(node, path)"
/>
</template>
<template #toggle-right>
<p-i name="ic_bookmark"
width="1rem" height="1rem"
color="inherit"
/>
</template>
<template #icon="{node}">
<template v-if="editMode">
<p-i v-if="node.data.item_type === 'PROJECT_GROUP'"
:name="permissionInfo[node.data.id] ? 'ic_tree_project-group' : 'ic_tree_project-group_locked'"
width="1rem" height="1rem" color="inherit"
/>
</template>
<p-i v-else :name="node.data.item_type === 'PROJECT' ? 'ic_tree_project' : 'ic_tree_project-group'"
width="1rem" height="1rem" color="inherit transparent"
/>
</template>
<template #right-extra="{node, path}">
<p-icon-button v-if="editMode && permissionInfo[node.data.id]" name="ic_minus"
color="inherit transparent"
size="sm"
.stop="deleteNode(path)"
/>
<p-icon-button v-else name="ic_plus"
class="add-btn"
size="sm"
.stop="addNode(path, getTreeData())"
/>
</template>
</p-tree>
</div>
`,
setup() {
const state = reactive({
editOptions: computed(() => ({
disabled: !state.editMode,
editStartValidator: ({ data }) => {
return !!state.permissionInfo[data.id]
},
validator: (text) => (text && text.length > 2 && text.length < 40),
})),
dragOptions: computed(() => ({
disabled: !state.editMode,
dragValidator(node, parent) {
return !!state.permissionInfo[node.data.id]
},
dropValidator(node, parent) {
if (state.dragParentNode === parent) return true;
return !!state.permissionInfo[parent?.data?.id]
}
})),
editMode: false,
permissionInfo: {},
target: '',
findMode: false,
lists: [],
listIdx: 0,
editText: '',
selectedItem: {},
dragNode: null,
dragParentNode: null
})
const selectOptions = {
validator({data}) {
if (data.item_type !== 'PROJECT') return false;
return state.editMode ? !!state.permissionInfo[data.id] : true
},
visibleRado: true
}
const toggleOptions = {
validator: (node) => {
return node.data.has_child || node.children.length > 0
}
}
const getClassNames = ({ data }) => {
return {'no-permission': state.editMode && !state.permissionInfo[data.id]}
}
const dataSetter = (text, node) => {
node.data.name = text
}
const dataGetter = (node) => {
return node.data.name
}
const dataFetcher = (node) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
let res;
if (state.findMode) {
res = state.lists[state.listIdx++]
} else {
res = getTreeList()
}
if (state.editMode) {
res.forEach(d => {state.permissionInfo[d.id] = faker.random.boolean();})
}
resolve(res)
}, 500)
})
}
const refresh = async () => {
if (state.root) {
state.root.resetSelect()
await state.root.fetchData()
}
}
const onClickEdit = () => {
const all = state.root.getAllNodes()
state.permissionInfo = {}
all.forEach((d) => {state.permissionInfo[d.data.id] = faker.random.boolean();})
state.editMode = !state.editMode;
}
const onInit = async (root) => {
state.root = root
}
const add = () => {
if (state.root) state.root.addNode(getTreeData())
}
const find = () => {
if (state.root) state.root.findNode((data) => data.name === state.target)
}
const fetchAndFind = async () => {
const {lists, names} = getRecursiveInfo()
state.findMode = true;
state.lists = lists;
state.listIdx = 0;
const predicates = names.map(d => (data) => {
return data.name === d
})
state.target = names[names.length - 1];
if (state.root) {
await state.root.fetchAndFindNode(predicates);
}
state.findMode = false;
}
const deleteNode = (path) => {
state.root.deleteNodeByPath(path)
}
const addNode = (path, data) => {
state.root.addChildNodeByPath(path, data)
}
const updateData = () => {
if (state.root && state.selectedItem.node) {
state.root.updateNodeByPath(state.selectedItem.path, {
...state.selectedItem.node.data,
name: state.editText
})
}
}
const onChangeSelect = (node, path) => {
state.selectedItem = node ? {node, path} : {}
state.editText = node? node.data.name : ''
}
const onStartDrag = (node, parent) => {
console.debug('start drag', node, parent)
state.dragNode = node
state.dragParentNode = parent
}
const onUpdateDrag = (node, parent) => {
console.debug('update drag', node, parent)
}
const onEndDrag = (node, parent) => {
console.debug('end drag', node, parent)
state.dragNode = null
state.dragParentNode = null
}
const changeSelectState = (node, path) => {
state.root.changeSelectState(node, path)
}
return {
...toRefs(state),
selectOptions,
toggleOptions,
getClassNames,
dataSetter,
dataGetter,
dataFetcher,
refresh,
onClickEdit,
onInit,
add,
find,
fetchAndFind,
getTreeData,
deleteNode,
addNode,
updateData,
onChangeSelect,
onStartDrag,
onUpdateDrag,
onEndDrag,
changeSelectState
}
}
});
export const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { PTree },
template: `
<div class="h-full w-full p-4 overflow-auto">
<div class="mb-4">
</div>
<p-tree :data-fetcher="treeDataFetcher">
</p-tree>
</div>
`,
setup() {
const treeDataFetcher = () => {
return ['A1', 'A2', 'A3']
}
return {
treeDataFetcher,
}
}
});
# Tree
<br/>
<br/>
## Basic
<br/>
<br/>
<Canvas>
<Story name="Basic">
{Template.bind({})}
</Story>
</Canvas>
<br/>
<br/>
## Expert
<br/>
<br/>
<Canvas>
<Story name="Expert">
{ExpertTemplate.bind({})}
</Story>
</Canvas>
<br/>
<br/>
## Playground
<Canvas>
<Story name="Playground">
{Template.bind({})}
</Story>
</Canvas>
<ArgsTable story="Playground"/>