Moving select-tree component to a new package called ohif:select-tree
This commit is contained in:
1 parent
749c863183
commit
1b4a2f9051
14 files changed
+39
-9
No files matched your search
@@ -0,0 +1,89 @@
|
||||
<template name="selectTree">
|
||||
{{#group (extend this
|
||||
class=(concat
|
||||
'select-tree '
|
||||
(valueIf this.root '' 'select-tree-root ')
|
||||
this.class
|
||||
)
|
||||
mixins=(concat 'selectTree ' this.mixins)
|
||||
)}}
|
||||
{{>UI.contentBlock}}
|
||||
<div class="tree-content">
|
||||
{{#let currentNode=this.currentNode.get}}
|
||||
{{#if (and (not this.root) (ne this.search false))}}
|
||||
{{#inputText
|
||||
label=this.label
|
||||
labelClass='tree-search'
|
||||
placeholder=(choose this.searchPlaceholder 'Search')
|
||||
}}
|
||||
<i class="fa fa-search"></i>
|
||||
{{/inputText}}
|
||||
{{/if}}
|
||||
<div class="tree-options {{#if currentNode}}collapsed{{/if}}">
|
||||
{{#if this.root}}
|
||||
{{>selectTreeBreadcrumb component=this.component}}
|
||||
{{/if}}
|
||||
<div class="tree-inputs clearfix">
|
||||
{{#each item in treeItems}}
|
||||
{{>inputRadio
|
||||
id=(concat this.storageKey '_' (encodeId item.value))
|
||||
class=this.radioClass
|
||||
name=this.key
|
||||
itemData=item
|
||||
value=item.value
|
||||
label=item.label
|
||||
labelClass=(valueIf (isArray item.items) 'tree-node' 'tree-leaf')
|
||||
checked=(eq item.value (reactive this.value))
|
||||
}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{#if (and currentNode (isArray currentNode.items))}}
|
||||
{{>selectTree (clone this
|
||||
items=currentNode.items
|
||||
root=(choose this.root this.component)
|
||||
class='select-tree-node'
|
||||
)}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/let}}
|
||||
</div>
|
||||
{{#if (and (not this.root) (or this.storageKey this.commonItems))}}
|
||||
{{>selectTreeCommon (clone this component=this.component treeInstance=instance)}}
|
||||
{{/if}}
|
||||
{{/group}}
|
||||
</template>
|
||||
|
||||
<template name="selectTreeBreadcrumb">
|
||||
<div class="tree-breadcrumb clearfix">
|
||||
{{>selectTreeBreadcrumbParent
|
||||
component=this.component.parent
|
||||
index=1
|
||||
}}
|
||||
{{#let currentNode=this.component.parent.templateInstance.data.currentNode.get}}
|
||||
<span class="tree-current-node">{{currentNode.label}}</span>
|
||||
{{/let}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="selectTreeBreadcrumbParent">
|
||||
{{#let parent=this.component.parent}}
|
||||
{{#let parentNode=(reactive parent.templateInstance.data.currentNode)}}
|
||||
{{#if parentNode}}
|
||||
{{>selectTreeBreadcrumbParent
|
||||
component=parent
|
||||
index=(sum this.index 1)
|
||||
}}
|
||||
{{/if}}
|
||||
<span class="path-link">
|
||||
<a href="#" class="tree-back" data-index="{{this.index}}">
|
||||
{{#if parentNode}}
|
||||
<span>{{parentNode.label}}</span>
|
||||
{{else}}
|
||||
<i class="fa fa-fast-backward"></i>
|
||||
{{/if}}
|
||||
</a>
|
||||
{{#if parentNode}}<span>/</span>{{/if}}
|
||||
</span>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</template>
|
||||
@@ -0,0 +1,337 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Tracker } from 'meteor/tracker';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { $ } from 'meteor/jquery';
|
||||
|
||||
// TODO: use npm dependency
|
||||
import transition from 'meteor/ohif:core/client/lib/third-party/transition-to-from-auto';
|
||||
|
||||
Template.selectTree.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
// Return the common items
|
||||
instance.getCommonItems = () => {
|
||||
const instance = Template.instance();
|
||||
|
||||
// Return the common items if given
|
||||
if (instance.data.commonItems) {
|
||||
return instance.data.commonItems;
|
||||
}
|
||||
|
||||
// Get all the tree leaves
|
||||
const leaves = instance.data.component.getLeaves();
|
||||
|
||||
// Generate an object with encoded keys from the tree leaves
|
||||
const leavesObject = {};
|
||||
_.each(leaves, leaf => {
|
||||
leavesObject[OHIF.string.encodeId(leaf.value)] = leaf;
|
||||
});
|
||||
|
||||
// Get the current items ranking
|
||||
const ranking = OHIF.user.getData(instance.data.storageKey);
|
||||
|
||||
// Sort the items based on how many times each one was used
|
||||
const sorted = [];
|
||||
_.each(ranking, (count, key) => sorted.push([key, count]));
|
||||
sorted.sort((a, b) => b[1] - a[1]);
|
||||
|
||||
// Create the result and push every item respecting the ranking order
|
||||
const result = [];
|
||||
_.each(sorted, item => {
|
||||
const current = leavesObject[item[0]];
|
||||
if (current) {
|
||||
result.push(current);
|
||||
}
|
||||
});
|
||||
|
||||
// Return the resulting array
|
||||
return result;
|
||||
};
|
||||
|
||||
// Create a reactive variable to control the search
|
||||
instance.searchTerm = new ReactiveVar('');
|
||||
});
|
||||
|
||||
Template.selectTree.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
const component = instance.$('.select-tree:first').data('component');
|
||||
const rootComponent = instance.data.root || component;
|
||||
const rootInstance = rootComponent.templateInstance;
|
||||
const $treeRoot = rootInstance.$('.select-tree-root:first').first();
|
||||
|
||||
instance.component = component;
|
||||
|
||||
// Set the margin to display the common section
|
||||
const isthreeColumns = instance.data.threeColumns;
|
||||
const marginProperty = isthreeColumns ? 'margin-left' : 'margin-right';
|
||||
const marginWidth = isthreeColumns ? $treeRoot.width() / 2 : $treeRoot.width();
|
||||
$treeRoot.children('.tree-content').css(marginProperty, marginWidth);
|
||||
|
||||
// Make the component respect the window boundaries
|
||||
$treeRoot.bounded();
|
||||
|
||||
// Check if the component will be rendered on a specific position
|
||||
const position = instance.data.position;
|
||||
if (component === rootComponent && position) {
|
||||
// Get the dimensions and move it with the given position as its center
|
||||
const width = $treeRoot.outerWidth();
|
||||
const height = $treeRoot.outerHeight();
|
||||
position.left -= Math.round(width / 2);
|
||||
position.top -= Math.round(height / 2);
|
||||
|
||||
// Change the component's position and trigger the bounded event
|
||||
$treeRoot.css(_.extend({}, position, {
|
||||
position: 'fixed'
|
||||
})).trigger('spatialChanged');
|
||||
}
|
||||
|
||||
Meteor.defer(() => {
|
||||
// Start the component transitions
|
||||
$treeRoot.addClass('started');
|
||||
|
||||
// Focus the search box
|
||||
$treeRoot.find('.tree-search input').focus();
|
||||
});
|
||||
|
||||
// Update the component's viewport height
|
||||
instance.updateHeight = searchTerm => {
|
||||
let height;
|
||||
|
||||
// Check if there's a search term
|
||||
if (searchTerm) {
|
||||
// Set the viewport height as same as listed options height
|
||||
height = rootInstance.$('.tree-inputs').height();
|
||||
} else {
|
||||
// Set the viewport height as same as selected node's options height
|
||||
height = rootInstance.$('.tree-options:last').data('height');
|
||||
}
|
||||
|
||||
// Update the viewport's height and trigger the bounded event
|
||||
rootInstance.$('.tree-options:first').first().height(height)
|
||||
.on('transitionend', () => $treeRoot.trigger('spatialChanged'));
|
||||
};
|
||||
|
||||
// Update the opened node
|
||||
instance.updateOpen = () => {
|
||||
rootInstance.$('.select-tree').removeClass('open');
|
||||
rootInstance.$('.select-tree:last').addClass('open');
|
||||
};
|
||||
|
||||
// Put the component in selected state
|
||||
instance.setSelected = (flag) => {
|
||||
if (flag) {
|
||||
$treeRoot.addClass('selected');
|
||||
} else {
|
||||
$treeRoot.removeClass('selected');
|
||||
}
|
||||
};
|
||||
|
||||
// Store the element's initial height
|
||||
const $treeOptions = instance.$('.tree-options:first').first();
|
||||
const height = $treeOptions.height();
|
||||
$treeOptions.data('height', height);
|
||||
|
||||
instance.autorun(() => {
|
||||
// Run this computation everytime the current node is changed
|
||||
instance.data.currentNode.dep.depend();
|
||||
|
||||
// Run this computation everytime the search term is changed
|
||||
const searchTerm = instance.searchTerm.get();
|
||||
|
||||
// Clean the current node selection if the user started the search
|
||||
if (searchTerm) {
|
||||
rootInstance.data.currentNode.set(null);
|
||||
}
|
||||
|
||||
// Update the viewport height
|
||||
Tracker.afterFlush(() => {
|
||||
instance.updateHeight(searchTerm);
|
||||
instance.updateOpen();
|
||||
});
|
||||
});
|
||||
|
||||
const selector = '.select-tree:first>.tree-content>.tree-options>.tree-breadcrumb .path-link';
|
||||
instance.$(selector).css('width', 0).each((index, element) => {
|
||||
transition({
|
||||
element: element,
|
||||
prop: 'width',
|
||||
style: 'width 0.3s ease',
|
||||
val: 'auto'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Template.selectTree.events({
|
||||
'click .select-tree-root>.tree-content'(event, instance) {
|
||||
// Get the tree root
|
||||
const $treeRoot = $(event.currentTarget).closest('.select-tree-root');
|
||||
|
||||
// Detect the first interaction with the component and do the animation
|
||||
$treeRoot.addClass('interacted');
|
||||
|
||||
// Remove the margin after the common section is closed
|
||||
$treeRoot.children('.tree-content').css('margin-right', '');
|
||||
},
|
||||
|
||||
'input .tree-search input'(event, instance) {
|
||||
// Get the tree root
|
||||
const $treeRoot = $(event.currentTarget).closest('.select-tree-root');
|
||||
|
||||
// Change the search term to update the tree items
|
||||
instance.searchTerm.set($(event.currentTarget).val());
|
||||
|
||||
// Change the component state
|
||||
$treeRoot.addClass('interacted');
|
||||
},
|
||||
|
||||
'keydown .tree-search input'(event, instance) {
|
||||
// Get the search term
|
||||
const searchTerm = $(event.currentTarget).val().trim();
|
||||
|
||||
const $labels = instance.$('.tree-inputs:first>label');
|
||||
|
||||
// Select the label if ENTER was pressed and there is only one result
|
||||
if (searchTerm.length && $labels.length === 1 && event.which === 13) {
|
||||
$labels.find('input').click();
|
||||
}
|
||||
},
|
||||
|
||||
'change .select-tree:first>.tree-content>.tree-options>.tree-inputs>label>input'(event, instance) {
|
||||
const component = instance.component;
|
||||
const $target = $(event.currentTarget);
|
||||
const $label = $target.closest('label');
|
||||
const eventComponent = $target.data('component');
|
||||
const rootComponent = instance.data.root || component;
|
||||
const rootInstance = rootComponent.templateInstance;
|
||||
|
||||
// Change the component's node
|
||||
component.node(eventComponent.value());
|
||||
|
||||
// Unset the active leaf
|
||||
rootInstance.$('label').removeClass('active').css('width', '');
|
||||
|
||||
// Unset the selected state
|
||||
instance.setSelected(false);
|
||||
|
||||
// Check if the clicked element is a node or a leaf
|
||||
const isLeaf = $label.hasClass('tree-leaf');
|
||||
if (isLeaf) {
|
||||
// Change the active item
|
||||
$label.css('width', $label.outerWidth()).addClass('active');
|
||||
|
||||
// Ckeck if there's a storageKey defined
|
||||
const storageKey = instance.data.storageKey;
|
||||
if (storageKey) {
|
||||
// Get the current stored data
|
||||
const storedData = _.extend({}, OHIF.user.getData(storageKey));
|
||||
|
||||
// Increment or create a counter for the clicked leaf
|
||||
const itemKey = OHIF.string.encodeId($target.val());
|
||||
if (storedData[itemKey]) {
|
||||
storedData[itemKey]++;
|
||||
} else {
|
||||
storedData[itemKey] = 1;
|
||||
}
|
||||
|
||||
// Update the stored data with the new count
|
||||
OHIF.user.setData(storageKey, storedData);
|
||||
}
|
||||
|
||||
// Set the selected leaf value in the root component
|
||||
const itemData = $target.data('component').templateInstance.data.itemData;
|
||||
rootComponent.value(itemData);
|
||||
|
||||
// Mark the component as selected
|
||||
instance.setSelected(true);
|
||||
} else {
|
||||
// Get the position of the clicked element
|
||||
const position = $label.position();
|
||||
|
||||
Meteor.defer(() => {
|
||||
const $treeNode = $label.parent().siblings('.select-tree');
|
||||
|
||||
// Do the transition from the clicked position to top
|
||||
$treeNode.css(position);
|
||||
setTimeout(() => $treeNode.css('top', 0));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
'click .select-tree:first>.tree-content>.tree-options>.tree-breadcrumb .tree-back'(event, instance) {
|
||||
const rootComponent = instance.data.root || instance.component;
|
||||
const rootInstance = rootComponent.templateInstance;
|
||||
|
||||
// Prevent the default element action
|
||||
event.preventDefault();
|
||||
|
||||
// Reset the checked inputs to unchecked state
|
||||
rootInstance.$('input').removeAttr('checked');
|
||||
|
||||
// Unset the selected state
|
||||
instance.setSelected(false);
|
||||
|
||||
// Get the index of the breadcrumb's clicked option
|
||||
const index = parseInt($(event.currentTarget).attr('data-index'));
|
||||
|
||||
// Set the current instance
|
||||
let currentInstance = instance.component.parent.templateInstance;
|
||||
|
||||
// Iterate over all parents until gets into the clicked node
|
||||
for (let i = 1; i <= index; i++) {
|
||||
// Unset the selected node
|
||||
currentInstance.data.currentNode.set(null);
|
||||
|
||||
// Check if it's not the root node
|
||||
if (i < index) {
|
||||
// Change the current instance
|
||||
currentInstance = currentInstance.component.parent.templateInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Template.selectTree.helpers({
|
||||
treeItems() {
|
||||
const instance = Template.instance();
|
||||
|
||||
// Run this computation everytime the search term is changed
|
||||
const searchTerm = instance.searchTerm.get();
|
||||
|
||||
// Get all the items
|
||||
let items = instance.data.items;
|
||||
|
||||
// Check if the search term was informed
|
||||
if (searchTerm) {
|
||||
// Search the items by the give search term
|
||||
items = OHIF.string.search(items, searchTerm, 'label');
|
||||
|
||||
// Filter only the tree leaves
|
||||
items = _.filter(items, item => !item.items);
|
||||
} else if (instance.data.hideCommon) {
|
||||
// Get the values of common items
|
||||
const commonValues = _.pluck(instance.getCommonItems(), 'value');
|
||||
|
||||
// Filter only the items that are not common
|
||||
items = _.filter(items, item => !_.contains(commonValues, item.value));
|
||||
}
|
||||
|
||||
// Return the items sorted for tree columns
|
||||
if (instance.data.threeColumns) {
|
||||
const begin = items.splice(0, Math.ceil(items.length / 2));
|
||||
const sortedItems = [];
|
||||
_.each(begin, (item, index) => {
|
||||
sortedItems.push(item);
|
||||
if (items[index]) {
|
||||
sortedItems.push(items[index]);
|
||||
}
|
||||
});
|
||||
items = sortedItems;
|
||||
}
|
||||
|
||||
// Return the tree items
|
||||
return items;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,289 @@
|
||||
@import "{ohif:design}/app"
|
||||
|
||||
// TODO: [design] can't we use colors that are already in common pallete?
|
||||
$gray1 = #A3A3A3
|
||||
$gray2 = #A0A0A0
|
||||
$gray3 = #C4C4C4
|
||||
$gray4 = #C0C0C0
|
||||
$gray5 = #9B9B9B
|
||||
$gray6 = #303030
|
||||
|
||||
.select-tree-root
|
||||
|
||||
.tree-content
|
||||
background-color: white
|
||||
position: relative
|
||||
transition(transform 0.3s ease\, background-color 0.3s ease\, border-color 0.3s ease\, border-radius 0.3s ease)
|
||||
|
||||
.tree-search
|
||||
a.tree-back
|
||||
.tree-breadcrumb span
|
||||
.tree-node
|
||||
.tree-leaf
|
||||
transition(all 0.3s ease)
|
||||
|
||||
&.selected
|
||||
|
||||
label.tree-leaf.active
|
||||
box-shadow: 0 0 0 10px white, inset 0 0 0 200px white
|
||||
|
||||
.tree-content
|
||||
background-color: transparent
|
||||
|
||||
&>.tree-content
|
||||
border-color: transparent
|
||||
border-radius: 0
|
||||
|
||||
.tree-search
|
||||
background-color: transparent
|
||||
border-color: transparent
|
||||
color: transparent
|
||||
|
||||
input,
|
||||
i
|
||||
opacity: 0
|
||||
|
||||
&, .select-tree
|
||||
|
||||
&:not(.open)>.tree-content
|
||||
|
||||
&>.tree-inputs
|
||||
&>.tree-breadcrumb
|
||||
display: none
|
||||
|
||||
&>.tree-content>.tree-options
|
||||
|
||||
&>.tree-inputs>label:not(.active)
|
||||
&>.tree-breadcrumb a
|
||||
&>.tree-breadcrumb span
|
||||
color: transparent
|
||||
|
||||
&>.tree-inputs>label.tree-node:not(.active)
|
||||
border-color: transparent
|
||||
|
||||
&.common-clicked .tree-leaf.active
|
||||
background-color: transparent
|
||||
box-shadow: none
|
||||
color: transparent
|
||||
|
||||
.select-tree-root
|
||||
display: inline-block
|
||||
position: relative
|
||||
z-index: 0
|
||||
|
||||
&>.tree-content
|
||||
border: 1px solid $gray3
|
||||
border-radius: 5px
|
||||
overflow: hidden
|
||||
|
||||
&>.tree-content>.tree-options
|
||||
transform(scale(1))
|
||||
transition(height 0.3s ease)
|
||||
|
||||
.tree-search
|
||||
background-color: $gray4
|
||||
border-bottom: 1px solid $gray3
|
||||
color: $gray6
|
||||
display: flex
|
||||
margin: 0
|
||||
padding: 5px
|
||||
position: relative
|
||||
text-align: center
|
||||
|
||||
span
|
||||
font-weight: bold
|
||||
line-height: 24px
|
||||
|
||||
input
|
||||
border-color: $gray5
|
||||
font-weight: normal
|
||||
padding-left: 34px
|
||||
transition(opacity 0.3s ease)
|
||||
|
||||
& ~ i
|
||||
color: $gray1
|
||||
font-size: 18px
|
||||
left: 16px
|
||||
position: absolute
|
||||
top: 37px
|
||||
transition(opacity 0.3s ease)
|
||||
|
||||
.tree-breadcrumb
|
||||
line-height: 32px
|
||||
padding: 5px 12px
|
||||
|
||||
.path-link, .tree-current-node
|
||||
display: inline-block
|
||||
font-weight: normal
|
||||
float: left
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
white-space: nowrap
|
||||
|
||||
i
|
||||
margin-right: 5px
|
||||
|
||||
.select-tree-node
|
||||
left: 0
|
||||
overflow: hidden
|
||||
position: fixed
|
||||
width: 100%
|
||||
z-index: 2
|
||||
|
||||
&
|
||||
.tree-content
|
||||
.tree-inputs
|
||||
height: 100%
|
||||
|
||||
.tree-options
|
||||
overflow: hidden
|
||||
position: relative
|
||||
z-index: 2
|
||||
|
||||
.tree-node, .tree-leaf
|
||||
cursor: pointer
|
||||
display: block
|
||||
font-weight: normal
|
||||
margin-bottom: 0
|
||||
padding: 0 12px
|
||||
position: relative
|
||||
z-index: 1
|
||||
|
||||
&>input
|
||||
display: none
|
||||
|
||||
.tree-node
|
||||
height: 41px
|
||||
border-bottom: 1px solid $gray3
|
||||
border-top: 1px solid $gray3
|
||||
line-height: 41px
|
||||
margin-top: -1px
|
||||
|
||||
&:last-child
|
||||
border-bottom: 0
|
||||
|
||||
.tree-leaf
|
||||
box-shadow: 0 0 0 200px transparent, inset 0 0 0 200px transparent
|
||||
line-height: 26px
|
||||
|
||||
&.active
|
||||
box-shadow: 0 0 0 200px white, inset 0 0 0 200px white
|
||||
transition(box-shadow 0.3s ease)
|
||||
z-index: 0
|
||||
|
||||
&:last-child:after
|
||||
display: block
|
||||
content: ''
|
||||
height: 10px
|
||||
|
||||
&.collapsed
|
||||
z-index: 1
|
||||
|
||||
.select-tree-common
|
||||
height: 100%
|
||||
padding-left: 6px
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
width: 50%
|
||||
|
||||
.content
|
||||
background-color: $gray6
|
||||
border: 1px solid $gray5
|
||||
border-radius: 5px
|
||||
color: white
|
||||
opacity: 0.9
|
||||
padding: 0 15px 12px
|
||||
position: absolute
|
||||
top: 50%
|
||||
transform(translateY(-50%))
|
||||
width: calc(100% - 6px)
|
||||
|
||||
h5.title
|
||||
color: $gray5
|
||||
font-weight: normal
|
||||
line-height: 20px
|
||||
margin: 12px 0 16px
|
||||
|
||||
div.labels
|
||||
position: relative
|
||||
|
||||
label
|
||||
cursor: pointer
|
||||
display: block
|
||||
font-weight: 300
|
||||
line-height: 26px
|
||||
margin: 0
|
||||
padding-left: 3px
|
||||
transition(padding 0.3s ease)
|
||||
|
||||
&:hover
|
||||
padding-left: 0
|
||||
|
||||
.select-tree-root
|
||||
|
||||
&>.tree-content
|
||||
transform(scale(0))
|
||||
transform-origin(100% 50%)
|
||||
|
||||
.select-tree-common
|
||||
z-index: -1
|
||||
|
||||
.content
|
||||
margin-left: calc(-100% - 6px)
|
||||
transition(all 0.3s ease 0.3s)
|
||||
|
||||
&.started
|
||||
|
||||
&>.tree-content
|
||||
transform(scale(1))
|
||||
|
||||
.select-tree-common
|
||||
animation-name: selectTreeCommonOverflow
|
||||
animation-delay: 0.3s
|
||||
animation-duration: 0.3s
|
||||
animation-iteration-count: 1
|
||||
animation-direction: alternate
|
||||
animation-fill-mode: forwards
|
||||
|
||||
.content
|
||||
margin-left: 0
|
||||
|
||||
&.interacted, &.selected
|
||||
|
||||
.select-tree-common
|
||||
z-index: 1
|
||||
|
||||
.content
|
||||
animation-name: selectTreeCommonCloseLeft
|
||||
animation-duration: 0.3s
|
||||
animation-iteration-count: 1
|
||||
animation-direction: alternate
|
||||
animation-timing-function: ease-out
|
||||
animation-fill-mode: forwards
|
||||
|
||||
@keyframes selectTreeCommonOverflow {
|
||||
from {
|
||||
overflow: hidden
|
||||
}
|
||||
to {
|
||||
overflow: visible
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes selectTreeCommonCloseLeft {
|
||||
from {
|
||||
height: 37px
|
||||
display: table
|
||||
margin-left: 0
|
||||
opacity: 1
|
||||
visibility: visible
|
||||
}
|
||||
to {
|
||||
height: 100%
|
||||
margin-left: calc(-100% - 6px)
|
||||
opacity: 0
|
||||
visibility: hidden
|
||||
width: calc(100% - 6px)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user