diff --git a/lib/app/communications/lib.js b/lib/app/communications/lib.js index 34cc3f3..bfdef7e 100644 --- a/lib/app/communications/lib.js +++ b/lib/app/communications/lib.js @@ -205,7 +205,7 @@ const SymbolDropdown = Symbol.for('ui-dropdown'); class DropdownColumn { static create(col, trigger, parent) { const drop = new Dropdown({ ...col.dropOptions, parent }); - drop.onselected = trigger; + drop.onSelected = trigger; return drop.create(); } diff --git a/lib/element/schedule.js b/lib/element/schedule.js index d96edd6..3e58465 100644 --- a/lib/element/schedule.js +++ b/lib/element/schedule.js @@ -121,7 +121,7 @@ export default class ScheduleItem { { value: '1', text: 'Weekly' }, { value: '2', text: 'Monthly' } ]; - drop.onselected = item => { + drop.onSelected = item => { container.querySelector('.schedule-item-weekly').style.display = item.value === '1' ? '' : 'none'; const monthly = item.value === '2'; container.querySelector('.schedule-item-monthly').style.display = monthly ? '' : 'none'; diff --git a/lib/element/style.css b/lib/element/style.css deleted file mode 100644 index 6e4356d..0000000 --- a/lib/element/style.css +++ /dev/null @@ -1 +0,0 @@ -.schedule-item-container fieldset{margin-top:10px;border-width:1px;border-radius:4px;border-color:var(--border-color)}.schedule-item-container fieldset legend,.schedule-item-container fieldset span{font-weight:400;font-size:var(--font-size);padding-left:8px;padding-right:6px;color:var(--color)}.schedule-item-container fieldset .ui-input{line-height:20px}.schedule-item-container fieldset .schedule-item-monthly{margin-top:5px}.schedule-item-container fieldset .schedule-item-monthly .ui-input{width:40px}.schedule-item-container fieldset.schedule-item-daily-frequency .ui-input{vertical-align:top;margin-top:5px}.schedule-item-container fieldset .schedule-item-table{width:100%}.schedule-item-container fieldset .schedule-item-line-occur-every{display:flex;align-items:flex-start}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line{display:flex;align-items:center;margin-top:5px}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line>span{flex:1 1 auto}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line>.ui-input{margin-top:0}.schedule-item-container fieldset .schedule-item-line-occur-every>span{line-height:36px}.schedule-item-container fieldset .schedule-item-line-occur-every .ui-input{width:70px}.schedule-item-container fieldset .schedule-item-line-duration{display:flex;align-items:center;height:36px}.schedule-item-container fieldset .schedule-item-line>.schedule-item-placeholder{flex:1 1 auto}.schedule-item-container .schedule-item-frequency{margin-top:0} \ No newline at end of file diff --git a/lib/ui/css/dropdown.css b/lib/ui/css/dropdown.css deleted file mode 100644 index 235bc92..0000000 --- a/lib/ui/css/dropdown.css +++ /dev/null @@ -1 +0,0 @@ -.ui-drop-wrapper{display:inline-block;border:none;border-radius:unset;-webkit-user-select:none;-moz-user-select:none;user-select:none;position:relative;font-size:var(--font-size);font-family:var(--font-family)}.ui-drop-wrapper>.ui-drop-header{background-color:var(--bg-color);display:flex;height:26px;border:1px solid var(--border-color);border-radius:var(--border-radius);transition:border-color .12s ease}.ui-drop-wrapper>.ui-drop-header:focus,.ui-drop-wrapper>.ui-drop-header:focus-visible{outline:none}.ui-drop-wrapper>.ui-drop-header:focus,.ui-drop-wrapper>.ui-drop-header:hover{border-color:var(--focus-border-color)}.ui-drop-wrapper>.ui-drop-header:disabled{border-color:var(--disabled-border-color);color:var(--disabled-color);background-color:var(--disabled-bg-color)}.ui-drop-wrapper>.ui-drop-header>.ui-drop-text{flex:1 1 auto;cursor:pointer;font-size:var(--font-size);padding:0 6px;overflow:hidden;text-overflow:ellipsis;border:none;white-space:nowrap}.ui-drop-wrapper>.ui-drop-header>.ui-drop-text:focus,.ui-drop-wrapper>.ui-drop-header>.ui-drop-text:focus-visible{outline:none}.ui-drop-wrapper>.ui-drop-header>input.ui-drop-text{cursor:initial}.ui-drop-wrapper>.ui-drop-header>input.ui-drop-text::-moz-placeholder{font-size:var(--font-smaller-size);font-style:italic}.ui-drop-wrapper>.ui-drop-header>input.ui-drop-text::placeholder{font-size:var(--font-smaller-size);font-style:italic}.ui-drop-wrapper>.ui-drop-header>.ui-drop-caret{flex:0 0 auto;width:26px;display:flex;justify-content:center;align-items:center;cursor:pointer}.ui-drop-wrapper>.ui-drop-header>.ui-drop-caret::after{display:block;content:"";border-top:4px solid;border-left:4px solid rgba(0,0,0,0);border-right:4px solid rgba(0,0,0,0);height:0;width:0}.ui-drop-wrapper>.ui-drop-header.disabled{border-color:var(--disabled-border-color);background-color:var(--disabled-bg-color);color:var(--disabled-color)}.ui-drop-wrapper>.ui-drop-header.disabled:focus{border-color:var(--disabled-border-color)}.ui-drop-wrapper>.ui-drop-header.disabled>.ui-drop-text,.ui-drop-wrapper>.ui-drop-header.disabled>.ui-drop-caret{cursor:default}.ui-drop-box{position:absolute;visibility:hidden;opacity:0;transform:scaleY(0);transform-origin:top;background-color:var(--bg-color);top:28px;z-index:2;transition:transform 120ms ease,opacity 120ms ease,visibility 120ms ease;min-width:calc(100% + 2px);box-sizing:border-box;box-shadow:0 3px 6px -4px rgba(0,0,0,.12),0 6px 16px 0 rgba(0,0,0,.08),0 9px 28px 8px rgba(0,0,0,.05);left:-1px}.ui-drop-box.slide-up{transform-origin:bottom;top:unset;bottom:28px}.ui-drop-box.active{visibility:visible;opacity:1;transform:scaleY(1)}.ui-drop-box>.ui-drop-search{box-sizing:border-box;height:36px;line-height:36px;padding:0 8px;position:relative;display:flex;align-items:center}.ui-drop-box>.ui-drop-search>input[type=text]{box-sizing:border-box;width:100%;height:26px;padding:0 6px 0 22px;color:var(--color);border:1px solid var(--border-color);border-radius:var(--border-radius);transition:border-color .12s ease}.ui-drop-box>.ui-drop-search>input[type=text]:focus,.ui-drop-box>.ui-drop-search>input[type=text]:focus-visible{outline:none}.ui-drop-box>.ui-drop-search>input[type=text]:focus,.ui-drop-box>.ui-drop-search>input[type=text]:hover{border-color:var(--focus-border-color)}.ui-drop-box>.ui-drop-search>input[type=text]:disabled{border-color:var(--disabled-border-color);color:var(--disabled-color);background-color:var(--disabled-bg-color)}.ui-drop-box>.ui-drop-search>input[type=text]::-moz-placeholder{font-style:italic}.ui-drop-box>.ui-drop-search>input[type=text]::placeholder{font-style:italic}.ui-drop-box>.ui-drop-search>svg{position:absolute;left:14px;width:13px;height:100%;cursor:text}.ui-drop-box>.ui-drop-list{margin:0;padding:0;list-style:none;max-height:210px;overflow-y:auto;font-size:var(--font-size)}.ui-drop-box>.ui-drop-list::-webkit-scrollbar{width:8px;height:8px}.ui-drop-box>.ui-drop-list::-webkit-scrollbar-thumb{background-color:rgba(168,168,168,.9);border-radius:4px}.ui-drop-box>.ui-drop-list.filtered>li:first-child{background-color:var(--hover-bg-color)}.ui-drop-box>.ui-drop-list>li{line-height:30px;height:30px;padding:0 10px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ui-drop-box>.ui-drop-list>li:hover,.ui-drop-box>.ui-drop-list>li.selected{background-color:var(--hover-bg-color)}.ui-drop-box>.ui-drop-list>li>.ui-check-wrapper{height:30px;display:flex} \ No newline at end of file diff --git a/lib/ui/css/grid.css b/lib/ui/css/grid.css deleted file mode 100644 index ed3072c..0000000 --- a/lib/ui/css/grid.css +++ /dev/null @@ -1 +0,0 @@ -.ui-grid{position:relative;box-sizing:border-box;overflow:auto}.ui-grid{--cell-hover-bg-color: lightyellow;--header-border-color: #adaba9;--header-bg-color: #fafafa;--header-fore-color: #000;--cell-border-color: #f0f0f0;--cell-fore-color: #333;--dark-border-color: #666;--split-border-color: #b3b3b3;--dragger-bg-color: #fff;--dragger-cursor-color: #333;--row-bg-color: #fff;--row-active-bg-color: #fafafa;--row-selected-bg-color: #e6f2fb;--text-disabled-color: gray;--filter-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05);--filter-transition: transform .12s ease, opacity .24s ease;--row-height: 36px;--header-line-height: 26px;--text-indent: 8px;--loading-size: 40px;--loading-border-radius: 20px;--arrow-size: 4px;--filter-size: 10px;--split-width: 8px;--dragger-size: 20px;--dragger-opacity: .6;--dragger-cursor-size: 4px;--dragger-cursor-pos: -4px;--dragger-cursor-opacity: .3;--header-padding: 4px 12px 4px 8px;--header-filter-padding: 4px 26px 4px 8px;--spacing-s: 4px;--spacing-cell: 6px 4px 6px 8px;--filter-line-height: 30px;--filter-item-padding: 0 4px}.ui-grid:focus,.ui-grid:focus-visible{outline:none}.ui-grid::-webkit-scrollbar{width:8px;height:8px}.ui-grid::-webkit-scrollbar-thumb{background-color:rgba(168,168,168,.9);border-radius:4px}.ui-grid,.ui-grid input[type=text],.ui-grid textarea{font-size:var(--font-size);font-family:var(--font-family)}.ui-grid>.ui-grid-sizer{position:absolute;white-space:nowrap;font-weight:bold;visibility:hidden}.ui-grid>.ui-grid-wrapper{position:relative}.ui-grid>.ui-grid-wrapper>.ui-grid-table{position:absolute;width:100%;min-width:100%;margin:0;border-collapse:collapse;border-spacing:0;table-layout:fixed}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr{color:var(--header-fore-color);position:sticky;top:0;z-index:2}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th{background-color:var(--header-bg-color);-webkit-user-select:none;-moz-user-select:none;user-select:none;padding:0;margin:0;word-wrap:break-word;white-space:normal;position:relative}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th.sticky{position:sticky;z-index:2}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>div{border-bottom:1px solid var(--header-border-color);line-height:var(--header-line-height);min-height:var(--row-height);display:flex;align-items:center;padding:var(--header-padding);box-sizing:border-box}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>div>span{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>div>.ui-check-wrapper{height:20px}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.arrow{width:0;height:0;top:50%;margin-top:calc(0px - var(--arrow-size)/2);right:calc(var(--arrow-size)/2);position:absolute}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.arrow.asc{border-bottom:var(--arrow-size) solid var(--dark-border-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.arrow.desc{border-top:var(--arrow-size) solid var(--dark-border-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.arrow.asc,.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.arrow.desc{border-left:var(--arrow-size) solid rgba(0,0,0,0);border-right:var(--arrow-size) solid rgba(0,0,0,0)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.filter{width:var(--filter-size);height:var(--filter-size);top:50%;margin-top:calc(0px - var(--filter-size)/2);right:calc(var(--arrow-size)*2 + 4px);position:absolute;display:flex}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.filter>svg{width:100%;height:100%;fill:var(--color);opacity:.2;transition:opacity .12s ease}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.filter>svg:hover{opacity:.8}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.filter.hover>svg{opacity:.8}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.filter.active>svg{opacity:1}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.spliter{position:absolute;height:100%;top:0;right:calc(0px - var(--split-width)/2);width:var(--split-width);cursor:ew-resize;z-index:2}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.spliter::after{content:"";height:100%;width:1px;display:block;margin:0 auto;transition:background-color .12s ease}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.spliter:hover::after{background-color:var(--split-border-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger{position:absolute;left:0;top:0;min-width:var(--dragger-size);height:100%;background-color:var(--dragger-bg-color);opacity:var(--dragger-opacity);display:none}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger-cursor{position:absolute;top:0;height:100%;border:1px solid var(--dragger-cursor-color);box-sizing:border-box;margin-left:0;opacity:var(--dragger-cursor-opacity);display:none;transition:left .12s ease}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger-cursor::before{top:-1px;border-top:var(--dragger-cursor-size) solid}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger-cursor::after{bottom:-1px;border-bottom:var(--dragger-cursor-size) solid}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger-cursor::before,.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger-cursor::after{content:"";position:absolute;left:var(--dragger-cursor-pos);border-left:var(--dragger-cursor-size) solid rgba(0,0,0,0);border-right:var(--dragger-cursor-size) solid rgba(0,0,0,0)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th.header-filter>div{padding:var(--header-filter-padding)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody{color:var(--cell-fore-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row{line-height:var(--line-height);white-space:nowrap;background-color:var(--row-bg-color);border-bottom:1px solid var(--cell-border-color);box-sizing:border-box}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row:hover{background-color:var(--row-active-bg-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row:hover>td.sticky{background-color:var(--row-active-bg-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row.selected{background-color:var(--row-selected-bg-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row.selected>td.sticky{background-color:var(--row-selected-bg-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td{padding:0}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td.sticky{position:sticky;z-index:1;background-color:var(--row-bg-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>span{padding:var(--spacing-cell);display:block;overflow:hidden;text-overflow:ellipsis;white-space:pre}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>input[type=text],.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea{border:none;box-sizing:border-box;width:100%;padding:0}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>input[type=text]:focus,.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>input[type=text]:focus-visible,.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea:focus,.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea:focus-visible{outline:none}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>input[type=text]:disabled,.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea:disabled{color:var(--text-disabled-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>input[type=text]{height:var(--row-height);text-indent:var(--text-indent)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea{resize:none;line-height:var(--line-height);display:block;padding:var(--spacing-cell);white-space:nowrap}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea::-webkit-scrollbar{width:8px;height:8px}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea::-webkit-scrollbar-thumb{background-color:rgba(168,168,168,.9);border-radius:4px}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-check-wrapper{display:flex;justify-content:center}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-check-wrapper .ui-check-inner,.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-check-wrapper .ui-check-inner>svg{transition:none}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-drop-wrapper{height:var(--row-height);width:100%;display:flex;flex-direction:column}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-drop-wrapper>.ui-drop-header{border:none;height:100%}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-drop-wrapper>.ui-drop-header>.ui-drop-text{padding:var(--spacing-cell)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-grid-date-cell{line-height:22px;box-sizing:border-box;padding:var(--spacing-cell);border:none;font-size:var(--font-size);width:100%}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-grid-date-cell:invalid{color:rgba(0,0,0,.3)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .col-icon{display:flex;cursor:pointer;justify-content:center;align-items:center;position:relative}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .col-icon>svg{width:16px;height:16px;fill:var(--primary-color);transition:opacity .12s ease}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .col-icon:hover>svg{opacity:.4}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .col-icon.disabled{cursor:unset}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .col-icon.disabled>svg{fill:var(--header-border-color);opacity:unset}.ui-grid>.ui-grid-wrapper .ui-grid-hover-holder{box-sizing:border-box;position:absolute;line-height:var(--line-height);padding:var(--spacing-cell);background-color:var(--cell-hover-bg-color);white-space:pre;display:flex;align-items:center;visibility:hidden;opacity:0;transition:visibility 0s linear .12s,opacity .12s ease;z-index:3}.ui-grid>.ui-grid-wrapper .ui-grid-hover-holder.active{visibility:visible;opacity:1}.ui-grid>.ui-grid-loading{position:absolute;top:0;right:0;bottom:0;left:0;visibility:hidden;opacity:0;transition:visibility 0s linear .12s,opacity .12s ease;background-color:var(--loading-bg-color);display:flex;justify-content:center;align-items:center;z-index:3}.ui-grid>.ui-grid-loading>div{background-color:var(--loading-fore-color);border-radius:var(--loading-border-radius)}.ui-grid>.ui-grid-loading>div>svg{width:var(--loading-size);height:var(--loading-size);padding:20px;animation:loading-spinner 1.2s infinite linear}.ui-grid>.ui-drop-box{max-width:300px}.ui-grid>.filter-panel{position:absolute;width:200px;height:300px;box-shadow:var(--filter-shadow);transition:var(--filter-transition);background-color:var(--bg-color);transform:scaleY(0);transform-origin:top;opacity:0;display:flex;flex-direction:column;z-index:3}.ui-grid>.filter-panel.active{transform:scaleY(1);opacity:1}.ui-grid>.filter-panel>.filter-search-holder{position:relative;margin:8px 8px 4px}.ui-grid>.filter-panel>.filter-search-holder>.filter-search-box{box-sizing:border-box;text-indent:16px;width:100%;font-size:var(--font-smaller-size);height:var(--line-height);line-height:var(--line-height)}.ui-grid>.filter-panel>.filter-search-holder>svg{position:absolute;width:12px;height:12px;top:calc(50% - 6px);left:4px;fill:var(--color);cursor:text}.ui-grid>.filter-panel>.filter-item-list{flex:1 1 auto;overflow-y:auto;overflow-x:hidden;position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none}.ui-grid>.filter-panel>.filter-item-list::-webkit-scrollbar{width:8px;height:8px}.ui-grid>.filter-panel>.filter-item-list::-webkit-scrollbar-thumb{background-color:rgba(168,168,168,.9);border-radius:4px}.ui-grid>.filter-panel>.filter-item-list>.filter-content{position:absolute;width:100%}.ui-grid>.filter-panel>.filter-item-list .filter-item{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-sizing:border-box;padding:var(--filter-item-padding)}.ui-grid>.filter-panel>.filter-item-list .filter-item:hover{background-color:var(--hover-bg-color)}.ui-grid>.filter-panel>.filter-item-list .filter-item .ui-check-wrapper{height:var(--filter-line-height);display:flex}.ui-grid>.filter-panel>.filter-item-list .filter-item .ui-check-wrapper .ui-check-inner+*{font-size:var(--font-smaller-size)}.ui-grid>.filter-panel>.filter-function{display:flex;justify-content:flex-end;padding:4px}.ui-grid>.filter-panel>.filter-function>button{box-sizing:border-box;margin-right:10px;min-width:40px;height:var(--filter-line-height);border:none;background-color:rgba(0,0,0,0);cursor:pointer;border-radius:0;transition:background-color .12s ease}.ui-grid>.filter-panel>.filter-function>button:focus,.ui-grid>.filter-panel>.filter-function>button:focus-visible{outline:none}.ui-grid>.filter-panel>.filter-function>button:hover{background-color:var(--hover-bg-color)} \ No newline at end of file diff --git a/lib/ui/css/grid.scss b/lib/ui/css/grid.scss index 8507e5a..9cc4962 100644 --- a/lib/ui/css/grid.scss +++ b/lib/ui/css/grid.scss @@ -95,7 +95,7 @@ &.sticky { position: sticky; - z-index: 2; + z-index: 3; } >div { @@ -190,9 +190,9 @@ transition: background-color .12s ease; } - &:hover::after { - background-color: var(--split-border-color); - } + // &:hover::after { + // background-color: var(--split-border-color); + // } } >.dragger { @@ -241,6 +241,10 @@ padding: var(--header-filter-padding); } } + + &:hover>th>.spliter::after { + background-color: var(--split-border-color); + } } } @@ -435,7 +439,7 @@ } } - >.ui-drop-box { + ~.ui-drop-box { max-width: 300px; } diff --git a/lib/ui/dropdown.d.ts b/lib/ui/dropdown.d.ts index 6d2ec86..93d1bc5 100644 --- a/lib/ui/dropdown.d.ts +++ b/lib/ui/dropdown.d.ts @@ -1,48 +1,118 @@ -interface DropdownItem { +/** 下拉项接口 */ +export interface DropdownItem { + /** 值 */ value: string; + /** 显示文本 */ text: string; + /** 源码显示内容 */ html?: HTMLElement } +/** 下拉框选项接口 */ export interface DropdownOptions { - textkey?: string; - valuekey?: string; - htmlkey?: string; - maxlength?: Number; - multiselect?: boolean; + /** 文本关键字,默认值 text */ + textKey?: string; + /** 值关键字,默认值 value */ + valueKey?: string; + /** 源码显示的关键字,默认值 html */ + htmlKey?: string; + /** 最大输入长度,默认值 500 */ + maxLength?: Number; + /** 是否允许多选 */ + multiSelect?: boolean; + /** 选中值 */ selected?: string; - selectedlist?: Array; + /** 选中的数组 */ + selectedList?: Array; + /** 是否禁用 */ disabled?: boolean; + /** 是否支持输入 */ input?: boolean; + /** 是否支持搜索 */ search?: boolean; - searchkeys?: Array; - searchplaceholder?: string; - tabindex?: Number; + /** 搜索的关键字数组 */ + searchKeys?: Array; + /** 搜索提示文本,默认值取语言资源 searchHolder "Search..." */ + searchPlaceholder?: string; + /** 焦点索引 */ + tabIndex?: Number; + /** 输入框的提示文本 */ placeholder?: string; - slidefixed?: boolean; - parent?: HTMLElement; + /** 是否固定为向下展开 */ + slideFixed?: boolean; + /** 父元素,默认添加到头元素之后 */ + wrapper?: HTMLElement; } +/** 下拉框类 */ export class Dropdown { + /** + * 把父元素下的所有 select 元素修改为统一下拉框组件 + * @param dom 父元素 + * @returns 返回该父元素 + */ static resolve(dom?: HTMLElement): HTMLElement; + /** + * 下拉框的构造函数 + * @param options 下拉框选项 + */ constructor(options?: DropdownOptions); + /** 根据该函数返回数据源 */ sourceFilter: () => Array; - onselected: (item: DropdownItem | any) => void; - onselectedlist: (list: Array) => void; - onexpanded: () => void; + /** + * 选中时触发 + * @param item 选中的条目 + */ + onSelected: (item: DropdownItem | any) => void; + /** + * 选中多个时触发 + * @param list 选中的条目数组 + */ + onSelectedList: (list: Array) => void; + /** 下拉框展开时触发 */ + onExpanded: () => void; + /** 下拉框收缩时触发 */ + onCollapsed: () => void; + /** 获取下拉框是否禁用 */ get disabled(): boolean; + /** + * 设置下拉框禁用状态 + * @param flag 禁用状态 + */ set disabled(flag: boolean); + /** 获取数据源 */ get source(): Array; + /** + * 设置数据源 + * @param list 数据源 + */ set source(list: Array); - get multiselect(): boolean; + /** 获取是否允许多选 */ + get multiSelect(): boolean; + /** 获取选中的条目 */ get selected(): DropdownItem | any; - get selectedlist(): Array; + /** 获取选中的条目列表 */ + get selectedList(): Array; + /** + * 创建下拉框元素 + * @returns 返回创建的下拉框元素 + */ create(): HTMLElement; + /** + * 选中某个条目 + * @param selected 选中的值 + * @param silence 是否静默选中,即不触发 {@linkcode onSelected} 事件 + */ select(selected: string, silence?: boolean): void; + /** + * 选中条目列表 + * @param selectedlist 选中的值的列表 + * @param silence 是否静默选中,即不触发 {@linkcode onSelected} 事件 + */ selectlist(selectedlist: Array, silence?: boolean): void; } diff --git a/lib/ui/dropdown.js b/lib/ui/dropdown.js index 2ee73e6..26e93e3 100644 --- a/lib/ui/dropdown.js +++ b/lib/ui/dropdown.js @@ -31,8 +31,8 @@ if (dropdownGlobal == null) { continue; } const dropdown = this[dropId]; - if (dropdown?.multiselect && typeof dropdown.oncollapsed === 'function') { - dropdown.oncollapsed(); + if (dropdown?.multiSelect && typeof dropdown.onCollapsed === 'function') { + dropdown.onCollapsed(); } } } @@ -97,9 +97,10 @@ export class Dropdown { // _var.selectedList; sourceFilter; - onselectedlist; - onselected; - onexpanded; + onSelectedList; + onSelected; + onExpanded; + onCollapsed; constructor(options = {}) { options.searchPlaceholder ??= r('searchHolder', 'Search...'); @@ -173,8 +174,8 @@ export class Dropdown { return; } this._dropdown(!active); - if (!active && typeof this.onexpanded === 'function') { - setTimeout(() => this.onexpanded(), 120); + if (!active && typeof this.onExpanded === 'function') { + setTimeout(() => this.onExpanded(), 120); } }); @@ -216,7 +217,7 @@ export class Dropdown { return wrapper; } - get multiselect() { return this._var.options.multiSelect } + get multiSelect() { return this._var.options.multiSelect } get disabled() { return this._var.wrapper == null || this._var.wrapper.querySelector('.ui-drop-header.disabled') != null } @@ -257,9 +258,12 @@ export class Dropdown { get selected() { return this._var.selected } - get selectedlist() { return this._var.selectedList || [] } + get selectedList() { return this._var.selectedList || [] } select(selected, silence) { + if (typeof selected !== 'string') { + selected = String(selected); + } if (this._var.lastSelected === selected) { return false; } @@ -267,7 +271,7 @@ export class Dropdown { const valuekey = this._var.options.valueKey; const textkey = this._var.options.textKey; const htmlkey = this._var.options.htmlKey; - let item = this.source.find(it => it[valuekey] === selected); + let item = this.source.find(it => String(it[valuekey]) === selected); if (this._var.options.input) { if (item == null) { item = { [valuekey]: selected }; @@ -304,8 +308,8 @@ export class Dropdown { } } this._var.selected = item; - if (!silence && typeof this.onselected === 'function') { - this.onselected(item); + if (!silence && typeof this.onSelected === 'function') { + this.onSelected(item); } } @@ -314,10 +318,14 @@ export class Dropdown { const valuekey = this._var.options.valueKey; const textkey = this._var.options.textKey; const htmlkey = this._var.options.htmlKey; - const itemlist = selectedlist.map(v => { - let item = source.find(it => it[valuekey] === v); + const itemlist = selectedlist.map(a => { + const v = typeof a === 'string' ? a : String(a); + let item = source.find(it => String(it[valuekey]) === v); if (item == null) { - item = { [valuekey]: v, [textkey]: v }; + item = { + [valuekey]: v, + [textkey]: v + }; } return item; }); @@ -328,8 +336,8 @@ export class Dropdown { } selectItems(this._var.label, itemlist, htmlkey, textkey); this._var.selectedList = itemlist; - if (!silence && typeof this.onselectedlist === 'function') { - this.onselectedlist(itemlist); + if (!silence && typeof this.onSelectedList === 'function') { + this.onSelectedList(itemlist); } } @@ -357,7 +365,7 @@ export class Dropdown { } // list const list = createElement('ul', 'ui-drop-list'); - if (!this.multiselect) { + if (!this.multiSelect) { list.addEventListener('click', e => { let li = e.target; while (li.tagName !== 'LI') { @@ -436,7 +444,7 @@ export class Dropdown { _filllist(source) { const list = this._var.container.querySelector('.ui-drop-list'); list.replaceChildren(); - const multiselect = this.multiselect; + const multiselect = this.multiSelect; const allchecked = this._var.allChecked; if (multiselect) { list.appendChild( @@ -455,10 +463,13 @@ export class Dropdown { const textkey = this._var.options.textKey; const htmlkey = this._var.options.htmlKey; const selected = this.selected; - const selectedlist = this.selectedlist; + const selectedlist = this.selectedList; let scrolled; source.slice(0, 200).forEach((item, i) => { - const val = item[valuekey]; + let val = item[valuekey]; + if (typeof val !== 'string') { + val = String(val); + } const li = createElement('li'); li.dataset.value = val; li.setAttribute('title', item[textkey]); @@ -471,7 +482,7 @@ export class Dropdown { label.innerHTML = html; } if (multiselect) { - const selected = selectedlist.some(s => s[valuekey] === val); + const selected = selectedlist.some(s => String(s[valuekey]) === val); if (label == null) { label = createElement('span'); label.innerText = item[textkey]; @@ -492,7 +503,7 @@ export class Dropdown { } else { li.appendChild(label); } - if (selected != null && selected[valuekey] === val) { + if (selected != null && String(selected[valuekey]) === val) { scrolled = DropdownItemHeight * i; li.classList.add('selected'); } @@ -522,7 +533,10 @@ export class Dropdown { } else { const source = this.source; list = [...this._var.container.querySelectorAll('input.dataitem:checked')] - .map(c => source.find(it => it[valuekey] === c.dataset.value)) + .map(c => { + const v = c.dataset.value; + return source.find(it => String(it[valuekey]) === v); + }) .filter(it => it != null); } } else { @@ -530,9 +544,9 @@ export class Dropdown { if (this._var.allChecked) { this._var.allChecked = false; this._var.container.querySelector('input[isall="1"]').checked = false; - list = this.source.filter(it => it[valuekey] !== val); + list = this.source.filter(it => String(it[valuekey]) !== val); } else { - list = this.selectedlist.filter(it => it[valuekey] !== val); + list = this.selectedList.filter(it => String(it[valuekey]) !== val); } } if (this._var.allChecked) { @@ -541,8 +555,8 @@ export class Dropdown { selectItems(this._var.label, list, htmlkey, textkey); } this._var.selectedList = list; - if (typeof this.onselectedlist === 'function') { - this.onselectedlist(itemlist); + if (typeof this.onSelectedList === 'function') { + this.onSelectedList(itemlist); } } diff --git a/lib/ui/grid/column.d.ts b/lib/ui/grid/column.d.ts index 7e1e761..599e1a1 100644 --- a/lib/ui/grid/column.d.ts +++ b/lib/ui/grid/column.d.ts @@ -1,69 +1,220 @@ import { Grid, GridItem, GridItemWrapper, GridSourceItem } from "./grid"; -import { DropdownOptions } from "../dropdown"; +import { Dropdown, DropdownOptions } from "../dropdown"; +/** 列类型枚举 */ interface GridColumnType { + /** 通用列 */ 0: "Common"; + /** 单行文本框列 */ 1: "Input"; + /** 下拉选择列 */ 2: "Dropdown"; + /** 复选框列 */ 3: "Checkbox"; + /** 图标列 */ 4: "Icon"; + /** 多行文本列 */ 5: "Text"; + /** 日期选择列 */ + 6: "Date"; } +/** 列定义接口 */ export interface GridColumnDefinition { + /** 列关键字,默认以该关键字从行数据中提取单元格值,行数据的关键字属性值里包含 DisplayValue 则优先显示此值 */ key?: string; + /** 列的类型,可以为 {@linkcode GridColumn} 的子类,或者内置类型 {@linkcode GridColumnType} */ type?: keyof GridColumnType | typeof GridColumn; + /** 列标题文本 */ caption?: string; + /** 列标题的元素样式 */ captionStyle?: { [key: string]: string }; + /** 大于 0 则设置为该宽度,否则根据列内容自动调整列宽 */ width?: Number; + /** 列对齐方式 */ align?: "left" | "center" | "right"; + /** + * 列是否可用(可编辑),允许以下类型

+ * boolean 则直接使用该值

+ * string 则以该值为关键字从行数据中取值作为判断条件

+ * (item: GridItem | any) => boolean 则调用该函数(上下文为列定义对象),以返回值作为判断条件

+ */ enabled?: boolean | string | ((item: GridItem | any) => boolean); - css?: { [key: string]: string }; - styleFilter?: (item: GridItem | any) => { [key: string]: string }; - visible?: boolean; - resizable?: boolean; - sortable?: boolean; - orderable?: boolean; - allcheck?: boolean; - events?: { [event: string]: any }; - attrs?: { [key: string]: string } | ((item: GridItem | any) => { [key: string]: string }); - allowFilter?: boolean; + /** 单元格取值采用该方法返回的值 */ filter?: (item: GridItem | any) => any; - sortFilter?: (a: GridItem | any, b: GridItem | any) => -1 | 0 | 1; - bgFilter?: (item: GridItem | any) => string; - dropOptions?: DropdownOptions; - source?: Array | ((item: GridItem | any) => Array | Promise>); - iconType?: string; - iconClassName?: string | ((item: GridItem | any) => string); + /** 单元格以该值填充内容,忽略filter与关键字属性 */ text?: string; + /** 列是否可见 */ + visible?: boolean; + /** 列是否允许调整宽度 */ + resizable?: boolean; + /** 列是否允许排序 */ + sortable?: boolean; + /** 列是否允许重排顺序 */ + orderable?: boolean; + /** 列为复选框类型时是否在列头增加全选复选框 */ + allcheck?: boolean; + /** 单元格css样式对象(仅在重建行元素时读取) */ + css?: { [key: string]: string }; + /** 根据返回值填充单元格样式(填充行列数据时读取) */ + styleFilter?: (item: GridItem | any) => { [key: string]: string }; + /** 根据返回值设置单元格背景色 */ + bgFilter?: (item: GridItem | any) => string; + /** 给单元格元素附加事件(事件函数上下文为数据行对象) */ + events?: { [event: string]: any }; + /** 根据返回值设置单元格元素的附加属性,允许直接设置对象也支持函数返回对象 */ + attrs?: { [key: string]: string } | ((item: GridItem | any) => { [key: string]: string }); + /** 是否允许进行列头过滤 */ + allowFilter?: boolean; + /** 自定义列过滤器的数据源(函数上下文为Grid) */ + filterSource?: Array | ((col: GridColumnDefinition) => Array); + /** 自定义列排序函数 */ + sortFilter?: (a: GridItem | any, b: GridItem | any) => -1 | 0 | 1; + /** 列为下拉列表类型时以该值设置下拉框的参数 */ + dropOptions?: DropdownOptions; + /** 列为下拉列表类型时以该值设置下拉列表数据源,支持函数返回,也支持返回异步对象 */ + source?: Array | ((item: GridItem | any) => Array | Promise>); + /** 下拉列表数据源是否缓存结果(即行数据未发生变化时仅从source属性获取一次值) */ + sourceCache?: boolean; + /** 列为图标类型时以该值设置图标样式(函数上下文为列定义对象),允许值为 fa-lightfa-regularfa-solid */ + iconType?: string; + /** 列为图标类型时以该值作为单元格元素的额外样式类型(函数上下文为列定义对象) */ + iconClassName?: string | ((item: GridItem | any) => string); + /** 列为日期类型时以该值作为最小可选日期值 */ + dateMin?: string; + /** 列为日期类型时以该值作为最大可选日期值 */ + dateMax?: string; + /** 以返回值额外设置单元格的tooltip(函数上下文为列定义对象) */ tooltip?: string | ((item: GridItem | any) => string); + + /** + * 列头复选框改变时触发 + * @param this 上下文为 Grid 对象 + * @param col 列定义对象 + * @param flag 是否选中 + */ onAllChecked?: (this: Grid, col: GridColumnDefinition, flag: boolean) => void; + /** + * 单元格发生变化时触发 + * @param this 上下文为 Grid 对象 + * @param item 数据行对象 + * @param value 修改后的值 + * @param oldValue 修改前的值 + * @param e 列修改事件传递过来的任意对象 + */ onChanged?: (this: Grid, item: GridItem | any, value: boolean | any, oldValue: any, e?: any) => void; + /** + * 文本单元格在输入完成时触发的事件 + * @param this 上下文为 Grid 对象 + * @param item 数据行对象 + * @param value 修改后的文本框值 + */ + onInputEnded?: (this: Grid, item: GridItem | any, value: string) => void; + /** + * 列过滤点击OK时触发的事件 + * @param this 上下文为 Grid 对象 + * @param col 列定义对象 + * @param selected 选中的过滤项 + */ + onFilterOk?: (this: Grid, col: GridColumnDefinition, selected: Array) => void; + /** + * 列过滤后触发的事件 + * @param this 上下文为 Grid 对象 + * @param col 列定义对象 + */ + onFiltered?: (this: Grid, col: GridColumnDefinition) => void; + /** + * 列为下拉框类型时在下拉列表展开时触发的事件 + * @param this 上下文为列定义对象 + * @param item 数据行对象 + * @param drop 下拉框对象 + */ + onDropExpanded?: (this: GridColumnDefinition, item: GridItem | any, drop: Dropdown) => void; } +/** 列定义基类 */ export class GridColumn { + /** + * 创建显示单元格时调用的方法 + * @param col 列定义对象 + * @returns 返回创建的单元格元素 + */ static create(col: GridColumnDefinition): HTMLElement; + /** + * 创建编辑单元格时调用的方法

+ * 元素修改后设置行包装对象的 __editing 后,支持在离开编辑状态时及时触发 {@linkcode leaveEdit} 方法
+ * 更多例子参考代码中 {@linkcode GridDropdownColumn} 的实现。 + * @param trigger 编辑事件回调函数,e 参数会传递给 {@linkcode getValue} 方法 + * @param col 列定义对象 + * @param container 父容器元素 + * @param vals 行包装对象,其 values 属性为行数据对象 + * @returns 返回创建的编辑状态的单元格元素 + */ static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement; - static setValue(element: HTMLElement, val: any, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void; - static getValue(e: any, col: GridColumnDefinition): any; + /** + * 设置单元格值时调用的方法 + * @param element 单元格元素 + * @param val 待设置的单元格值 + * @param vals 行包装对象 + * @param col 列定义对象 + * @param grid {@linkcode Grid} 对象 + */ + static setValue(element: HTMLElement, val: string | boolean | any, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void; + /** + * 获取编辑状态单元格值时调用的方法 + * @param e 由 {@linkcode createEdit} 方法中 trigger 函数传递来的对象 + * @param col 列定义对象 + * @returns 返回单元格的值 + */ + static getValue(e: any, col: GridColumnDefinition): string | boolean | any; + /** + * 设置单元格样式时调用的方法 + * @param element 单元格元素 + * @param style 样式对象 + */ static setStyle(element: HTMLElement, style: { [key: string]: string }): void; + /** + * 设置单元格可用性时调用的方法 + * @param element 单元格元素 + * @param enabled 启用值,为false时代表禁用 + */ static setEnabled(element: HTMLElement, enabled?: boolean): void; -} - -export class GridInputColumn extends GridColumn { - static get editing(): boolean; -} - -export class GridTextColumn extends GridInputColumn { } - -export class GridDropdownColumn extends GridColumn { + /** + * 单元格离开编辑元素时触发,需要由行包装对象的 __editing 来确定是否触发。 + * @param element 单元格元素 + * @param container 父容器元素 + */ static leaveEdit(element: HTMLElement, container: HTMLElement): void; } +/** 单行文本列 */ +export class GridInputColumn extends GridColumn { + /** + * 设置该类型是否支持触发 {@linkcode GridColumnDefinition.onInputEnded} 方法
+ * 该属性返回 true 后,在任意事件中修改行包装对象的 __editing 值,则会在行列元素变动时及时触发 {@linkcode GridColumnDefinition.onInputEnded} 方法,避免例如文本框还未触发 onchange 事件就被移除元素而导致的问题
+ * 更多例子参考代码中 {@linkcode GridInputColumn} 的实现 + */ + static get editing(): boolean; +} + +/** 多行文本列 */ +export class GridTextColumn extends GridInputColumn { } + +/** 下拉选择列 */ +export class GridDropdownColumn extends GridColumn { } + +/** 复选框列 */ export class GridCheckboxColumn extends GridColumn { } +/** 图标列 */ export class GridIconColumn extends GridColumn { } +/** 日期选择列 */ export class GridDateColumn extends GridColumn { + /** + * 格式化日期对象为 M/d/yyyy 格式的字符串 + * @param date 日期对象 + * @returns 返回格式化后的字符串 + */ static formatDate(date: Date): string; } \ No newline at end of file diff --git a/lib/ui/grid/column.js b/lib/ui/grid/column.js index e9d5c94..ad8dbc9 100644 --- a/lib/ui/grid/column.js +++ b/lib/ui/grid/column.js @@ -105,13 +105,13 @@ export class GridTextColumn extends GridInputColumn { const SymbolDropdown = Symbol.for('ui-dropdown'); export class GridDropdownColumn extends GridColumn { - static createEdit(trigger, col, wrapper, it) { + static createEdit(trigger, col, container, it) { const drop = new Dropdown({ ...col.dropOptions, - wrapper + wrapper: container.parentElement }); - drop.onselected = trigger; - drop.onexpanded = function () { + drop.onSelected = trigger; + drop.onExpanded = function () { if (it.__editing == null) { it.__editing = { [col.key]: true @@ -211,8 +211,8 @@ export class GridDropdownColumn extends GridColumn { drop.disabled = enabled === false; } - static leaveEdit(element, wrapper) { - wrapper.querySelectorAll('.ui-drop-box.active').forEach(e => { + static leaveEdit(element, container) { + container.parentElement.querySelectorAll('.ui-drop-box.active').forEach(e => { if (e != null) { e.classList.remove('active'); } @@ -221,8 +221,8 @@ export class GridDropdownColumn extends GridColumn { if (drop == null) { return; } - if (drop?.multiselect && typeof drop.oncollapsed === 'function') { - drop.oncollapsed(); + if (drop?.multiSelect && typeof drop.onCollapsed === 'function') { + drop.onCollapsed(); } } } @@ -286,7 +286,16 @@ export class GridIconColumn extends GridColumn { } export class GridDateColumn extends GridColumn { - static createEdit(trigger, col) { + static createEdit(trigger, col, _container, vals) { + let enabled = col.enabled; + if (typeof enabled === 'string') { + enabled = vals.values[enabled]; + } else if (typeof enabled === 'function') { + enabled = col.enabled(vals.values); + } + if (enabled === false) { + return super.create(); + } const date = createElement('input', 'ui-grid-date-cell'); date.required = true; date.type = 'date'; @@ -306,25 +315,45 @@ export class GridDateColumn extends GridColumn { if (isNaN(val) || /^\d{4}-\d{2}-\d{2}$/.test(val)) { element.value = val; } else { - val = new Date((val - 621355968e9) / 10000); + val = new Date((val - 621355968e9) / 1e4); const month = String(val.getMonth() + 1).padStart(2, '0'); const date = String(val.getDate()).padStart(2, '0'); element.value = `${val.getFullYear()}-${month}-${date}`; } } else { - element.innerText = val; + element.innerText = this.formatDate(val); } } static getValue(e) { - return e.target?.value; + const date = e.target?.valueAsDate; + if (date instanceof Date && !isNaN(date)) { + const year = date.getFullYear(); + if (year < 1900 || year > 9999) { + return ''; + } + return String(date.getTime() * 1e4 + 621355968e9); + } + return ''; } static setEnabled(element, enabled) { element.disabled = enabled === false; } + static _resolveDate(s) { + if (s instanceof Date) { + return s; + } + const ticks = Number(s); + if (!isNaN(ticks) && ticks > 0) { + return new Date((ticks - 621355968e9) / 1e4); + } + return new Date(s); + } + static formatDate(date) { + date = this._resolveDate(date); if (date instanceof Date && !isNaN(date)) { return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`; } diff --git a/lib/ui/grid/grid.d.ts b/lib/ui/grid/grid.d.ts index 2107942..70b7b33 100644 --- a/lib/ui/grid/grid.d.ts +++ b/lib/ui/grid/grid.d.ts @@ -1,93 +1,244 @@ import { GridColumnDefinition } from "./column" +/** 列数据接口 */ export interface GridItem { + /** 值 */ Value: any; + /** 显示值 */ DisplayValue: string; } +/** 列数据行包装接口 */ export interface GridItemWrapper { - values: Array; + /** 真实数据对象 */ + values: { [key: string]: GridItem | any }; + /** 下拉数据源缓存对象 */ source: { [key: string]: Array }; } +/** 下拉框列数据源接口 */ export interface GridSourceItem { + /** 值 */ value: string; + /** 显示文本 */ text: string; } +/** 列排序枚举 */ interface GridColumnDirection { + /** 倒序 */ [-1]: Number, + /** 升序 */ 1: Number } +/** 列事件枚举 */ interface GridColumnColumnEventMap { + /** 重排事件 */ "reorder": string, + /** 宽调整事件 */ "resize": string, + /** 排序事件 */ "sort": string } +/** Grid 控件基础类 */ export class Grid { + /** 列类型枚举 */ static ColumnTypes: { + /** 通用列(只读) */ Common: 0, + /** 单行文本列 */ Input: 1, + /** 下拉选择列 */ Dropdown: 2, + /** 复选框列 */ Checkbox: 3, + /** 图标列 */ Icon: 4, + /** 多行文本列 */ Text: 5, + /** 日期选择列 */ Date: 6, + /** + * 判断列是否为复选框列 + * @param type 列类型 + */ isCheckbox(type: Number): boolean; }; + /** 列定义的数组 */ columns: Array; + /** 多语言资源对象 */ langs?: { all: string, ok: string, reset: string }; + /** 行数大于等于该值则启用虚模式,默认值 100 */ virtualCount?: Number; + /** 表格行高,默认值 36 */ rowHeight?: Number; + /** 文本行高,默认值 24 */ lineHeight?: Number; + /** 列表底部留出额外行的空白,默认值 0 */ extraRows?: Number; + /** 过滤条件列表的行高,默认值 30 */ filterRowHeight?: Number; + /** 列表高度值,为 0 时列表始终显示全部内容(自增高),为非数字或者小于 0 则根据容器高度来确定虚模式的渲染行数,默认值 null */ height?: Number; + /** 列表是否为只读,默认值 false */ readonly?: boolean; + /** 是否允许多选,默认值 false */ multiSelect?: boolean; + /** 为 false 时只有点击在单元格内才会选中行,默认值 true */ fullrowClick?: boolean; + /** 单元格 tooltip 是否禁用,默认值 false */ tooltipDisabled?: boolean; + /** 列头是否显示,默认值 true */ headerVisible?: boolean; + /** 监听事件的窗口载体,默认值 window */ window?: Window + /** 排序列的索引,默认值 -1 */ sortIndex?: Number; + /** 排序方式,正数升序,负数倒序,默认值 1 */ sortDirection?: keyof GridColumnDirection; - constructor(container: HTMLElement, getText?: (id: string, def?: string) => string); + /** + * Grid 控件构造函数 + * @param container Grid 控件所在的父容器,可以是 string 表示选择器,也可以是 HTMLElement 对象

+ * 构造时可以不进行赋值,但是调用 init 函数时则必须进行赋值 + * @param getText (可选参数)获取多语言文本的函数代理 + */ + constructor(container: string | HTMLElement, getText?: (id: string, def?: string) => string); + /** + * 即将选中行时触发,返回 false、null、undefined、0 等则取消选中动作 + * @param index 即将选中的行索引 + * @param colIndex 即将选中的列索引 + */ willSelect?: (index: Number, colIndex: Number) => boolean; + /** + * 单元格单击时触发,colIndex 为 -1 则表示点击的是行的空白处,返回 false 则取消事件冒泡 + * @param index 点击的行索引 + * @param colIndex 点击的列索引 + */ cellClicked?: (index: Number, colIndex: Number) => boolean; + /** + * 选中行发生变化时触发的事件 + * @param index 选中的行索引 + */ onSelectedRowChanged?: (index?: Number) => void; + /** + * 单元格双击时触发的事件,colIndex 为 -1 则表示点击的是行的空白处 + * @param index 双击的行索引 + * @param colIndex 双击的列索引 + */ onCellDblClicked?: (index: Number, colIndex: Number) => void; + /** + * 行双击时触发的事件 + * @param index 双击的行索引 + */ onRowDblClicked?: (index: Number) => void; - onColumnChanged?: (type: K, index: Number, value: Number | keyof GridColumnDirection) => void; + /** + * 列发生变化时触发的事件 + * @param type 事件类型

+ * "reorder" 为发生列重排事件,此时 value 为目标列索引
+ * "resize" 为发生列宽调整事件,此时 value 为列宽度值
+ * "sort" 为发生列排序事件,此时 value 为 1(升序)或 -1(倒序) + * @param colIndex 发生变化事件的列索引 + * @param value 变化的值 + */ + onColumnChanged?: (type: keyof GridColumnColumnEventMap, colIndex: Number, value: Number | keyof GridColumnDirection) => void; + /** + * 列滚动时触发的事件 + * @param e 滚动事件对象 + */ onBodyScrolled?: (e: Event) => void; + /** 获取数据数组 */ get source(): Array; + /** 设置数据,并刷新列表 */ set source(list: Array); + /** 获取当前选中的行索引的数组 */ get selectedIndexes(): Array; + /** 设置当前选中的行索引的数组,并刷新列表 */ set selectedIndexes(indexes: Array); + /** 获取 Grid 当前是否处于加载状态 */ get loading(): boolean; + /** 使 Grid 进入加载状态 */ set loading(flag: boolean); + /** 获取 Grid 当前滚动的偏移量 */ get scrollTop(): Number; + /** 设置 Grid 滚动偏移量 */ set scrollTop(top: Number); - get element(): HTMLElement + /** 获取 Grid 的页面元素 */ + get element(): HTMLElement; + /** 获取当前 Grid 是否已发生改变 */ + get changed(): boolean; + /** 获取当前是否为虚模式状态 */ get virtual(): boolean; + /** 获取当前排序的列关键字,为 null 则当前无排序列 */ get sortKey(): string | undefined; + /** 获取当前选中行的索引,为 -1 则当前没有选中行 */ get selectedIndex(): Number | -1; + /** + * 初始化Grid控件 + * @param container 父容器元素,若未传值则采用构造方法中传入的父容器元素 + */ init(container?: HTMLElement): void; + /** + * 设置数据列表,该方法为 set source 属性的语法糖 + * @param source 待设置的数据列表 + */ setData(source: Array): void; + /** + * 设置单行数据 + * @param index 行索引 + * @param item 待设置的行数据值 + */ setItem(index: Number, item: GridItem | any): void; + /** + * 添加行数据 + * @param item 待添加的行数据值 + * @param index 待添加的行索引 + */ + addItem(item: GridItem | any, index?: Number): void; + /** + * 删除行数据 + * @param index 待删除的行索引 + */ + removeItem(index: Number): void; + /** + * 滚动到指定行的位置 + * @param index 待滚动至的行索引 + */ scrollToIndex(index: Number): void; - resize(force?: boolean): void; + /** + * 调整 Grid 元素的大小,一般需要在宽度变化时(如页面大小发生变化时)调用 + * @param force 是否强制reload,默认只有待渲染的行数发生变化时才会调用reload + * @param keep 是否保持当前滚动位置 + */ + resize(force?: boolean, keep?: boolean): void; + /** + * 重新计算需要渲染的行,并载入元素,一般需要在高度变化时调用 + * @param keep 是否保持当前滚动位置 + */ reload(keep?: boolean): void; + /** + * 重新填充Grid单元格数据 + */ refresh(): void; + /** + * 把所有行重置为未修改的状态 + */ resetChange(): void; + /** + * 根据当前排序字段进行列排序 + * @param reload 为 true 则在列排序后调用 reload 方法 + */ sortColumn(reload?: boolean): void; + /** + * 清除列头复选框的选中状态 + */ clearHeaderCheckbox(): void; } \ No newline at end of file diff --git a/lib/ui/grid/grid.js b/lib/ui/grid/grid.js index d80390a..b7e36a0 100644 --- a/lib/ui/grid/grid.js +++ b/lib/ui/grid/grid.js @@ -133,6 +133,14 @@ export class Grid { get element() { return this._var.el } + get changed() { + const source = this._var.source; + if (source == null) { + return false; + } + return source.filter(r => r.__changed).length > 0; + } + get source() { return this._var.source?.map(s => s.values) } set source(list) { if (this._var.el == null) { @@ -148,11 +156,32 @@ export class Grid { setItem(index, item) { if (this._var.source == null) { - return; + throw new Error('no source'); } const it = this._var.source[index]; delete it.source; it.values = item; + this.refresh(); + } + + addItem(item, index) { + if (this._var.source == null) { + throw new Error('no source'); + } + if (index >= 0) { + this._var.source.splice(index, 0, { values: item }); + } else { + this._var.source.push({ values: item }); + } + this.reload(); + } + + removeItem(index) { + if (this._var.source == null) { + throw new Error('no source'); + } + this._var.source.splice(index, 1); + this.reload(); } _refreshSource(list) { @@ -344,7 +373,7 @@ export class Grid { this._var.el.scrollTop = top; } - resize(force) { + resize(force, keep) { if (this._var.rendering || this._var.el == null) { return; } @@ -360,7 +389,7 @@ export class Grid { const count = truncate((height - 1) / (this.rowHeight + 1)) + (RedumCount * 2) + 1; if (force || count !== this._var.rowCount) { this._var.rowCount = count; - this.reload(true); + this.reload(keep); } this._var.bodyClientWidth = body.clientWidth; } @@ -760,11 +789,11 @@ export class Grid { } if (type.editing) { val = type.getValue({ target: cell.children[0] }, col); - this._onRowChanged(null, startIndex + i, col, val, cell, true); + this._onRowChanged(null, i, col, val, cell, true); } } element = selected ? - type.createEdit(e => this._onRowChanged(e, startIndex + i, col, type.getValue(e, col), cell), col, this._var.el, vals) : + type.createEdit(e => this._onRowChanged(e, i, col, type.getValue(e, col), cell), col, this._var.el, vals) : type.create(col); cell.replaceChildren(element); } else { @@ -1179,7 +1208,7 @@ export class Grid { const key = e.currentTarget.value.toLowerCase(); const items = key.length === 0 ? array : array.filter(i => { const displayValue = i?.DisplayValue ?? i; - return String(displayValue ?? '').includes(key); + return String(displayValue ?? '').toLowerCase().includes(key); }); this._fillFilterList(col, itemlist, items, itemall); }); @@ -1288,7 +1317,7 @@ export class Grid { } const content = list.querySelector('.filter-content'); content.replaceChildren(); - this._doFillFilterList(content, array, list.querySelector('.filter-all>input')); + this._doFillFilterList(content, array, list.querySelector('.filter-all')); content.style.top = `${top + rowHeight}px`; } } diff --git a/lib/ui/icon.js b/lib/ui/icon.js index a5d0eb0..a5298ff 100644 --- a/lib/ui/icon.js +++ b/lib/ui/icon.js @@ -2,7 +2,8 @@ const svgns = 'http://www.w3.org/2000/svg'; function createUse(type, id) { const c = typeof consts !== 'undefined' ? consts : {}; - const path = c.path || ''; + const netroot = typeof _network !== 'undefined' ? _network.root : ''; + const path = c.path || netroot; const ver = c.resver == null ? '' : `?${c.resver}`; const use = document.createElementNS(svgns, 'use'); use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', `${path}fonts/${type}.svg${ver}#${id}`); diff --git a/package-lock.json b/package-lock.json index c123eeb..2b2f8b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "postcss-preset-env": "^8.2.0", "sass": "^1.60.0", "typedoc": "^0.24.8", + "typedoc-theme-hierarchy": "^4.1.2", "vite": "^4.0.4", "vite-plugin-externals": "^0.6.2" } @@ -1592,9 +1593,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, "funding": [ { @@ -1652,9 +1653,9 @@ } }, "node_modules/postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", "dev": true, "funding": [ { @@ -1671,7 +1672,7 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -2397,6 +2398,32 @@ "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" } }, + "node_modules/typedoc-theme-hierarchy": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/typedoc-theme-hierarchy/-/typedoc-theme-hierarchy-4.1.2.tgz", + "integrity": "sha512-X3H+zaDkg7wLNoaPJoqXs3rnMfZ9BZjmlXRwplWDciaPfn2hojHxJJ+yVKdqqmojgiHJgg7MYWGDVpOfNlOJ5A==", + "dev": true, + "dependencies": { + "fs-extra": "11.1.1" + }, + "peerDependencies": { + "typedoc": "^0.24.0 || ^0.25.0" + } + }, + "node_modules/typedoc-theme-hierarchy/node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/typescript": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", @@ -2457,9 +2484,9 @@ "dev": true }, "node_modules/vite": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", - "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", + "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", "dev": true, "dependencies": { "esbuild": "^0.18.10", diff --git a/package.json b/package.json index 4ebd258..b2c8780 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "postcss-preset-env": "^8.2.0", "sass": "^1.60.0", "typedoc": "^0.24.8", + "typedoc-theme-hierarchy": "^4.1.2", "vite": "^4.0.4", "vite-plugin-externals": "^0.6.2" } diff --git a/readme/Grid 控件接口文档.docx b/readme/Grid 控件接口文档.docx index 9e3ad67..a546425 100644 Binary files a/readme/Grid 控件接口文档.docx and b/readme/Grid 控件接口文档.docx differ diff --git a/typedoc.json b/typedoc.json index cd926e9..094b740 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,17 +1,12 @@ { "entryPoints": [ - "lib/functions.d.ts", - "lib/ui/checkbox.d.ts", "lib/ui/dropdown.d.ts", - "lib/ui/icon.d.ts", - "lib/ui/popup.d.ts", - "lib/ui/tooltip.d.ts", "lib/ui/grid/column.d.ts", - "lib/ui/grid/grid.d.ts", - "lib/ui/media.d.ts" + "lib/ui/grid/grid.d.ts" ], "out": "./docs", "readme": "README.md", "name": "UI Library", + "disableSources": true, "cleanOutputDir": true } \ No newline at end of file diff --git a/vite.build.js b/vite.build.js index 3181c8c..906f2c1 100644 --- a/vite.build.js +++ b/vite.build.js @@ -23,70 +23,9 @@ const libraries = [ } }, sourcemap: true, - outDir: '../../../../IronIntel/Contractor2.0/Contractor/Site/js/lib', emptyOutDir: false } }, - { - clearScreen: false, - css: { - postcss: { - plugins: [postcssPresetEnv()] - } - }, - build: { - lib: { - entry: './lib/app.js', - name: 'lib-app', - formats: ['umd'], - fileName: (_format, name) => `${name}.min.js` - }, - rollupOptions: { - output: { - assetFileNames: info => info.name === 'style.css' ? 'app.min.css' : info.name - } - }, - sourcemap: true, - outDir: '../../../../IronIntel/Contractor2.0/Contractor/Site/js/lib', - emptyOutDir: false - }, - plugins: [ - viteExternalsPlugin({ - '../../ui': 'lib-ui', - '../../utility': 'lib-utility' - }) - ] - }, - { - clearScreen: false, - css: { - postcss: { - plugins: [postcssPresetEnv()] - } - }, - build: { - lib: { - entry: './lib/element.js', - name: 'lib-element', - formats: ['umd'], - fileName: (_format, name) => `${name}.min.js` - }, - rollupOptions: { - output: { - assetFileNames: info => info.name === 'style.css' ? 'element.min.css' : info.name - } - }, - sourcemap: true, - outDir: '../../../../IronIntel/Contractor2.0/Contractor/Site/js/lib', - emptyOutDir: false - }, - plugins: [ - viteExternalsPlugin({ - '../ui': 'lib-ui', - '../utility': 'lib-utility' - }) - ] - }, { clearScreen: false, build: {