diff --git a/README.md b/README.md index b37b1c8a..dab2ca25 100755 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ To see more in [devui.design](https://devui.design/home). ## Angular Support -Now supports Angular `^15.0.0` +Now supports Angular `^16.0.0` ## Getting Started diff --git a/README_zh_CN.md b/README_zh_CN.md index d5fc9fa3..d4366435 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -23,7 +23,7 @@ DevUI Design设计系统包含了DevUI规则、设计语言和最佳实践的资 ## Angular版本 -当前支持的angular版本`^15.0.0` +当前支持的angular版本`^16.0.0` ## 快速开始 diff --git a/angular.json b/angular.json index 257c0bd9..80bfbfc0 100755 --- a/angular.json +++ b/angular.json @@ -207,6 +207,5 @@ } } } - }, - "defaultProject": "devui" + } } diff --git a/devui-commons/src/devui-commons.scss b/devui-commons/src/devui-commons.scss index 451c3d6e..2083345b 100644 --- a/devui-commons/src/devui-commons.scss +++ b/devui-commons/src/devui-commons.scss @@ -234,6 +234,12 @@ } } +@media (max-width: 767px) { + .devui-content-layout { + margin-top: 56px; + } +} + body[ui-lang='en-us'] { @media (max-width: 1480px) { d-common-header .devui-search { @@ -242,6 +248,12 @@ body[ui-lang='en-us'] { } } +body[ui-theme='galaxy-theme'] { + .header-container { + background-image: linear-gradient(179deg, #141721 0%, #1A1E29 100%); + } +} + // 设置引导页MarkDown文档标题 .readme { h1 { diff --git a/devui-commons/src/header/header.component.html b/devui-commons/src/header/header.component.html index 94b0752e..fa91a9f9 100644 --- a/devui-commons/src/header/header.component.html +++ b/devui-commons/src/header/header.component.html @@ -1,8 +1,45 @@ -
+
- - + +
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+ +
+
+
+ + + + + + + + + + + + hamburger + + + + + + +
-
- - 收起 - - - - -
-
- - 展开 - - - - -
+
+
- -
+ +
+ + + + + +
+
+ + + + + +
+
\ No newline at end of file diff --git a/devui-commons/src/header/header.component.scss b/devui-commons/src/header/header.component.scss index f759e5f4..c79f3935 100644 --- a/devui-commons/src/header/header.component.scss +++ b/devui-commons/src/header/header.component.scss @@ -79,4 +79,52 @@ stroke: none; } } -} \ No newline at end of file +} + +.mobile-header-container { + display: flex; + align-items: center; + position: fixed; + z-index: 1000; + width: 100%; + background-image: linear-gradient(179deg, #E9FBFF 0%, #F3F5FF 100%); + .mobile-header-left { + display: flex; + align-items: center; + } + .mobile-hamburger { + margin-left: 12px; + } + + .expand-sidebar { + margin-top: 0 !important; + margin-left: 12px; + } +} + +.mobile-sider { + display: flex; + flex-direction: column; + justify-content: space-between; + position: fixed; + top: 56px; + left: 0; + + height: calc(100vh - 56px); + width: 0; + background-color: $devui-base-bg; + transition: all 0.3s; +} + +.mobile-sider-show { + width: 242px !important; + padding: 12px; +} + +.mobile-bottom-menu-hidden { + display: none; +} + +.mobile-bottom-menu-show { + display: block !important; +} diff --git a/devui-commons/src/header/header.component.ts b/devui-commons/src/header/header.component.ts index 4d15a09a..6f4f4a6b 100644 --- a/devui-commons/src/header/header.component.ts +++ b/devui-commons/src/header/header.component.ts @@ -1,20 +1,10 @@ import { DOCUMENT } from '@angular/common'; -import { Component, ContentChildren, HostListener, Inject, Input, isDevMode, OnInit, QueryList } from '@angular/core'; +import { Component, ContentChildren, HostListener, Inject, Input, isDevMode, OnInit, QueryList, ChangeDetectorRef } from '@angular/core'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import { DevuiCommonsService } from '../devui-commons.service'; import { I18nUtil } from '../i18n/i18n.util'; import { LogoComponent } from './logo/logo.component'; -export interface UserInfo { - displayNameCn: string; - uid?:string; - employeeNumber: string; - iconUrl:string; - docCount?:number; - questionCount?:number; - draftCount?:number; -}; - @Component({ selector: 'd-common-header', templateUrl: './header.component.html', @@ -30,6 +20,7 @@ export class HeaderComponent implements OnInit { @Input() repoName = 'ng-devui'; repoLink!: SafeResourceUrl; + githubLink; @ContentChildren(LogoComponent) subLogo: QueryList = new QueryList(); @@ -42,6 +33,10 @@ export class HeaderComponent implements OnInit { MIN_WIDTH = 1279; showExpandButton = false; showCollapseButton = false; + showHamburger = false; + showMobileHeader = false; + isHomePage = false; + mediaQuery; @HostListener('window:resize') @@ -55,6 +50,7 @@ export class HeaderComponent implements OnInit { constructor( private commonsService: DevuiCommonsService, private sanitizer: DomSanitizer, + private cdr: ChangeDetectorRef, @Inject(DOCUMENT) private doc: any, ) { this.document = this.doc; @@ -68,6 +64,20 @@ export class HeaderComponent implements OnInit { this.showExpandButton = this.document.body.clientWidth < this.MIN_WIDTH ? true : false; this.setSlideBarStyle(); this.repoLink = this.sanitizer.bypassSecurityTrustResourceUrl(`https://ghbtns.com/github-btn.html?user=DevCloudFE&repo=${this.repoName}&type=star&count=true`); + this.githubLink = `https://github.com/DevCloudFE/${this.repoName}`; + this.isHomePage = window.location.pathname.split('/')[1] === 'home' ? true : false; + this.mediaQuery = window.matchMedia('(max-width: 767px)'); + this.mediaQuery.addListener(this.handleScreenChange); + this.handleScreenChange(this.mediaQuery); + } + + handleScreenChange = (e) => { + if (e.matches) { + this.showMobileHeader = true; + } else { + this.showMobileHeader = false; + } + this.cdr.detectChanges(); } onSearch(term): void { @@ -90,7 +100,8 @@ export class HeaderComponent implements OnInit { this.showCollapseButton = false; this.showExpandButton = true; } - + this.cdr.detectChanges(); + this.showSlideMenu = typeof showMenu === 'boolean' ? showMenu : !this.showSlideMenu; this.setSlideBarStyle(true); } @@ -114,4 +125,9 @@ export class HeaderComponent implements OnInit { this.curLanguage = lang; this.searchPlaceholder = this.curLanguage === 'zh-cn' ? '请输入你想查找的组件' : 'Enter the component'; } -} + + toggleHamburger(event) { + this.showHamburger = !this.showHamburger; + this.cdr.detectChanges(); + } +} \ No newline at end of file diff --git a/devui-commons/src/header/language-switch/language-switch.component.html b/devui-commons/src/header/language-switch/language-switch.component.html index 5ccd2bed..42feca07 100644 --- a/devui-commons/src/header/language-switch/language-switch.component.html +++ b/devui-commons/src/header/language-switch/language-switch.component.html @@ -1,32 +1,5 @@
-
- - - 语言 - - - - - - - - - - - - - - - 中文 - - - - - - - - -
+ {{currentLang === 'zh-cn' ? languageArr[1] : languageArr[0]}}
diff --git a/devui-commons/src/header/language-switch/language-switch.component.ts b/devui-commons/src/header/language-switch/language-switch.component.ts index 74c7fa0b..654e2006 100644 --- a/devui-commons/src/header/language-switch/language-switch.component.ts +++ b/devui-commons/src/header/language-switch/language-switch.component.ts @@ -1,4 +1,3 @@ -import { I18nService } from 'ng-devui/i18n'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { DevuiCommonsService } from '../../devui-commons.service'; import { I18nUtil } from '../../i18n/i18n.util'; @@ -11,12 +10,9 @@ import { I18nUtil } from '../../i18n/i18n.util'; export class LanguageSwitchComponent implements OnInit { @Input() languageArr: string[] = ['CN', 'EN']; @Output() languageEvent = new EventEmitter(); - currentLang!: string; + currentLang: string; - constructor( - private commonsService: DevuiCommonsService, - private i18n: I18nService - ) { } + constructor(private commonsService: DevuiCommonsService) { } ngOnInit(): void { this.currentLang = I18nUtil.getCurrentLanguage(); @@ -26,7 +22,6 @@ export class LanguageSwitchComponent implements OnInit { this.currentLang = this.currentLang === 'zh-cn' ? 'en-us' : 'zh-cn'; localStorage.setItem('lang', this.currentLang); this.languageEvent.emit(this.currentLang); - this.i18n.toggleLang(this.currentLang); this.commonsService.broadcast('languageEvent', this.currentLang); } } \ No newline at end of file diff --git a/devui-commons/src/header/logo/logo.component.html b/devui-commons/src/header/logo/logo.component.html index 49da0e14..c2650952 100644 --- a/devui-commons/src/header/logo/logo.component.html +++ b/devui-commons/src/header/logo/logo.component.html @@ -1,33 +1,59 @@ -
+
-
+
diff --git a/devui/tabs/tabs.component.scss b/devui/tabs/tabs.component.scss index 97295b93..ac51173b 100755 --- a/devui/tabs/tabs.component.scss +++ b/devui/tabs/tabs.component.scss @@ -227,7 +227,7 @@ background: transparent; & > li { - margin-left: 32px; + margin-left: 24px; &:first-child { margin-left: 0; @@ -253,7 +253,7 @@ } &.devui-tab-add-icon { - margin-right: 32px; + margin-right: 24px; } } diff --git a/devui/tabs/tabs.component.ts b/devui/tabs/tabs.component.ts index 1278ceba..7d66cc6a 100755 --- a/devui/tabs/tabs.component.ts +++ b/devui/tabs/tabs.component.ts @@ -1,5 +1,6 @@ import { AfterViewInit, + ChangeDetectorRef, Component, ContentChildren, ElementRef, @@ -70,7 +71,7 @@ export class TabsComponent implements OnChanges, AfterViewInit { return this.scrollModeToggle && ['tabs', 'pills', 'wrapped'].includes(this.type); } - constructor(private el: ElementRef) { + constructor(private el: ElementRef, private cdr: ChangeDetectorRef) { this.id = `devuiTabs${TabsComponent.ID_SEED++}`; } @@ -118,6 +119,7 @@ export class TabsComponent implements OnChanges, AfterViewInit { const leftFix = this.scrollModeToggle ? this.tabsEle.nativeElement.scrollLeft : 0; this.offsetLeft = tabEle.getBoundingClientRect().left + leftFix - this.tabsEle.nativeElement.getBoundingClientRect().left; this.offsetWidth = tabEle.getBoundingClientRect().width; + this.cdr.detectChanges(); } }); } diff --git a/devui/tags/demo/basic/basic.component.html b/devui/tags/demo/basic/basic.component.html index b163348e..58582c5b 100755 --- a/devui/tags/demo/basic/basic.component.html +++ b/devui/tags/demo/basic/basic.component.html @@ -11,7 +11,12 @@

Deletable

{{ tagList2 | json }}

Custom

- + {{ tag.name }} diff --git a/devui/text-input/demo/basic/basic.component.html b/devui/text-input/demo/basic/basic.component.html index af33f714..80bf0986 100644 --- a/devui/text-input/demo/basic/basic.component.html +++ b/devui/text-input/demo/basic/basic.component.html @@ -4,3 +4,5 @@

Disabled

Error

+

Gray Mode

+ diff --git a/devui/text-input/doc/api-cn.md b/devui/text-input/doc/api-cn.md index a93081be..cbbc180c 100644 --- a/devui/text-input/doc/api-cn.md +++ b/devui/text-input/doc/api-cn.md @@ -19,3 +19,5 @@ import { TextInputModule } from 'ng-devui/text-input'; | :----------------: | :---------: | :-------: | :---: | :--------------------------: | ----------------------------------------------- | | error | `boolean` | false | 可选,文本框是否出现输入错误 | [基本用法](demo#basic-usage) | | size | `'sm'\|''\|'lg'` | '' | 可选,文本框尺寸,有三种选择`'lg'`,`''`,`'sm'` | [尺寸](demo#size) | +| showGlowStyle | `boolean` | true | 可选,是否显示悬浮发光效果 | +| styleType | `'default' \| 'gray'` | 'default' | 可选,default为默认有线框白底风格,gray为无线框灰底风格 | [基本用法](demo#basic-usage) | ✔ | diff --git a/devui/text-input/doc/api-en.md b/devui/text-input/doc/api-en.md index 02ccfac9..cdc6f982 100644 --- a/devui/text-input/doc/api-en.md +++ b/devui/text-input/doc/api-en.md @@ -19,3 +19,5 @@ In the page: | :----------------: | :---------: | :-------: | :---: | :--------------------------: | ----------------------------------------------- | | error | `boolean` | false | Optional. Indicating whether an input error occurs in the Text-input. | [Basic Usage](demo#basic-usage) | | size | `'sm'\|''\|'lg'` | '' | Optional. Size of the text box. The value can be `'lg'`,`''`,`'sm'` | [Size](demo#size) | +| showGlowStyle | `boolean` | true | (Optional) Indicates whether to display the floating glow effect.| +| styleType | `'default' \| 'gray'` | 'default' | Optional. Default indicates the white background style of the wire frame, and gray indicates the gray background style of the wireless frame. | [Basic Usage](demo#basic-usage) | ✔ | diff --git a/devui/text-input/text-input.directive.ts b/devui/text-input/text-input.directive.ts index 5271399f..062e928e 100644 --- a/devui/text-input/text-input.directive.ts +++ b/devui/text-input/text-input.directive.ts @@ -9,6 +9,10 @@ export class TextDirective { @Input() @HostBinding('class.error') error: boolean; @Input() size = ''; @Input() @WithConfig() styleType = 'default'; + @Input() @WithConfig() showGlowStyle = true; + @HostBinding('class.devui-glow-style') get hasGlowStyle () { + return this.showGlowStyle; + }; @HostBinding('class.devui-textinput-lg') get large() { diff --git a/devui/textarea/doc/api-cn.md b/devui/textarea/doc/api-cn.md index 26eff7af..890bf154 100644 --- a/devui/textarea/doc/api-cn.md +++ b/devui/textarea/doc/api-cn.md @@ -21,3 +21,4 @@ import { TextareaModule } from 'ng-devui/textarea'; | error | `boolean` | false | 可选,文本框是否出现输入错误 | [基本用法](demo#basic-usage) | | maxLengthBlocker | `boolean` | false | 可选,文本框输入中文符号超出 maxLength 限制时可开启 | [监听输入及最大字数](demo#count) | | resize | `none \| vertical \| horizontal \| both \| inherit` | none | 可选,文本框是否可调整大小,可选项:不可调整,水平调整,垂直调整,自由调整,默认继承 | [调整大小](demo#resize) | +| showGlowStyle | `boolean` | true | 可选,是否显示悬浮发光效果 | diff --git a/devui/textarea/doc/api-en.md b/devui/textarea/doc/api-en.md index 9c3dfd79..f7a1f143 100644 --- a/devui/textarea/doc/api-en.md +++ b/devui/textarea/doc/api-en.md @@ -21,3 +21,4 @@ In the page: | error | `boolean` | false | Optional. Indicating whether an input error occurs in the textarea | [Basic Usage](demo#basic-usage) | | maxLengthBlocker | `boolean` | false | Optional. This function can be enabled when the number of IME Composition symbols entered in the text box exceeds the limit specified by maxLength. | [Listening input and maximum number of words](demo#count) | | resize | `none \| vertical \| horizontal \| both \| inherit` | none | Optional. Indicates whether the textarea can be resized. The options are as follows: Unadjustable, Horizontal, Vertical, and Free. The default value is inherited | [Resizable](demo#resize) | +| showGlowStyle | `boolean` | true | (Optional) Indicates whether to display the floating glow effect.| diff --git a/devui/textarea/textarea.directive.ts b/devui/textarea/textarea.directive.ts index c57302ee..1e03d97f 100644 --- a/devui/textarea/textarea.directive.ts +++ b/devui/textarea/textarea.directive.ts @@ -10,6 +10,10 @@ export class TextareaDirective implements AfterViewInit, OnDestroy { @Input() @HostBinding('style.resize') resize: 'none' | 'vertical' | 'horizontal' | 'both' | 'inherit' = 'none'; @Input() @HostBinding('class.error') error: boolean; @Input() @WithConfig() styleType = 'default'; + @Input() @WithConfig() showGlowStyle = true; + @HostBinding('class.devui-glow-style') get hasGlowStyle () { + return this.showGlowStyle; + }; @HostBinding('class.devui-gray-style') get gray() { return this.styleType === 'gray'; diff --git a/devui/theme-collection/extend-theme.ts b/devui/theme-collection/extend-theme.ts index 74a97d72..0565073d 100644 --- a/devui/theme-collection/extend-theme.ts +++ b/devui/theme-collection/extend-theme.ts @@ -29,7 +29,7 @@ export const provenceTheme: Theme = new Theme({ 'devui-text': '#070036', 'devui-aide-text': '#717087', 'devui-placeholder': '#babbc0', - 'devui-disabled-text': '#cfd0d3', + 'devui-disabled-text': '#babbc0', 'devui-disabled-bg': '#f5f5f6', 'devui-line': '#E2E2E5', 'devui-dividing-line': '#F2F2F3', @@ -78,7 +78,7 @@ export const sweetTheme: Theme = new Theme({ 'devui-text': '#2f272f', 'devui-aide-text': '#827d82', 'devui-placeholder': '#bdb8bd', - 'devui-disabled-text': '#cbcacb', + 'devui-disabled-text': '#bdb8bd', 'devui-disabled-bg': '#f6f6f6', 'devui-line': '#aea6ad', 'devui-dividing-line': '#eae7e9', @@ -228,7 +228,9 @@ export const galaxyTheme: Theme = new Theme({ 'devui-shadow-length-fullscreen-overlay': '0 0 6px 0', 'devui-gray-form-control-bg': '#323338', 'devui-gray-form-control-hover-bg': '#393A3E', - 'devui-nav-expand-bg': '#1d1d21' + 'devui-nav-expand-bg': '#1d1d21', + 'devui-link': '#7693F5', + 'devui-link-active': '#465EB8' }, extends: 'devui-dark-theme', isDark: true, diff --git a/devui/theme-collection/overwrite-style.scss b/devui/theme-collection/overwrite-style.scss index 339a52b1..6d371087 100644 --- a/devui/theme-collection/overwrite-style.scss +++ b/devui/theme-collection/overwrite-style.scss @@ -236,6 +236,7 @@ d-select .devui-form-group { } .devui-select-list-wrapper.devui-form-control { + .devui-select-selected-item-with-template, .devui-select-placeholder { height: 28px; line-height: 28px; @@ -286,6 +287,16 @@ input { padding: 12px 12px 0 12px; } + &.devui-auto-complete-list { + & > .devui-no-data-tip { + padding: 12px; + } + + & > li { + padding: 8px 12px 4px 12px; + } + } + ul, .cdk-virtual-scroll-content-wrapper { padding: 12px; @@ -377,7 +388,7 @@ d-multiple-upload { } } -div.popper-container div.popper-container-scrollable div.devui-tree-select.devui-options-container { +div.popper-container div.popper-container-scrollable { padding: 12px; } @@ -408,7 +419,6 @@ d-pagination { span:hover, a:focus, span:focus { - background-color: transparent !important; color: $devui-text !important; border: 1px solid $devui-dividing-line !important; box-shadow: 0 1px 3px 0 $devui-light-shadow !important; diff --git a/devui/toggle-menu/toggle-menu-container.component.html b/devui/toggle-menu/toggle-menu-container.component.html index 282e54f9..f651dadc 100644 --- a/devui/toggle-menu/toggle-menu-container.component.html +++ b/devui/toggle-menu/toggle-menu-container.component.html @@ -34,8 +34,7 @@ 'devui-custom-right': listInstance?.customViewDirection === 'right', 'devui-custom-left': listInstance?.customViewDirection === 'left', 'devui-custom-top': listInstance?.customViewDirection === 'top', - 'devui-dropdown-menu-multiple': listInstance?.multiple, - 'devui-reset-min-width': width + 'devui-dropdown-menu-multiple': listInstance?.multiple }" [@fadeInOut]="isOpen && startAnimation ? (appendToBody ? menuPosition : popDirection) : 'void'" (@fadeInOut.done)="animationEnd()" diff --git a/devui/toggle-menu/toggle-menu-container.component.scss b/devui/toggle-menu/toggle-menu-container.component.scss index 51f9a540..4e7cf0b6 100644 --- a/devui/toggle-menu/toggle-menu-container.component.scss +++ b/devui/toggle-menu/toggle-menu-container.component.scss @@ -111,8 +111,6 @@ &.devui-custom-right, &.devui-custom-left, &.devui-custom-top { - min-width: 400px; - d-toggle-menu-list { width: 100%; display: flex !important; @@ -156,9 +154,5 @@ &.devui-custom-top > .devui-select-custom-area { border-bottom: 1px solid $devui-dividing-line; } - - &.devui-reset-min-width { - min-width: auto; - } } } diff --git a/devui/toggle/demo/basic/basic.component.css b/devui/toggle/demo/basic/basic.component.css index c7afddee..c2291124 100755 --- a/devui/toggle/demo/basic/basic.component.css +++ b/devui/toggle/demo/basic/basic.component.css @@ -1,3 +1,3 @@ d-toggle { - margin-right: 5px; + margin-right: 8px; } diff --git a/devui/toggle/demo/basic/basic.component.html b/devui/toggle/demo/basic/basic.component.html index 70e8482b..8100f9c7 100755 --- a/devui/toggle/demo/basic/basic.component.html +++ b/devui/toggle/demo/basic/basic.component.html @@ -1,19 +1,16 @@ -

Small

+

Large

- +
-

Middle

- -

Large

+

Small

- +
-

disabled

diff --git a/devui/toggle/demo/basic/basic.component.ts b/devui/toggle/demo/basic/basic.component.ts index 8c0f9368..a7fb2e42 100755 --- a/devui/toggle/demo/basic/basic.component.ts +++ b/devui/toggle/demo/basic/basic.component.ts @@ -1,16 +1,8 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; @Component({ selector: 'd-basic', templateUrl: './basic.component.html', - styleUrls: ['./basic.component.css'] + styleUrls: ['./basic.component.css'], }) -export class BasicComponent implements OnInit { - - constructor() { - } - - ngOnInit() { - - } -} +export class BasicComponent {} diff --git a/devui/toggle/demo/callback/callback.component.ts b/devui/toggle/demo/callback/callback.component.ts index 750bb8c2..1de93a7c 100644 --- a/devui/toggle/demo/callback/callback.component.ts +++ b/devui/toggle/demo/callback/callback.component.ts @@ -1,20 +1,21 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; @Component({ selector: 'd-callback', - templateUrl: './callback.component.html' + templateUrl: './callback.component.html', + styles: [ + ` + d-toggle { + margin-bottom: 8px; + } + `, + ], }) -export class CallbackComponent implements OnInit { +export class CallbackComponent { count = 0; - constructor() { } - - ngOnInit() { - } - onChange(state) { console.log(state); this.count++; } - } diff --git a/devui/toggle/demo/custom/custom.component.html b/devui/toggle/demo/custom/custom.component.html index 0f0fc75b..5c04513b 100644 --- a/devui/toggle/demo/custom/custom.component.html +++ b/devui/toggle/demo/custom/custom.component.html @@ -1,7 +1,7 @@ -
+
-
+
diff --git a/devui/toggle/demo/custom/custom.component.scss b/devui/toggle/demo/custom/custom.component.scss index 798c6d58..28cebb6f 100644 --- a/devui/toggle/demo/custom/custom.component.scss +++ b/devui/toggle/demo/custom/custom.component.scss @@ -1,3 +1,8 @@ +d-toggle { + display: block; + margin-bottom: 16px; +} + .toggle-inner { color: white; font-weight: bold; diff --git a/devui/toggle/demo/custom/custom.component.ts b/devui/toggle/demo/custom/custom.component.ts index ce7d58e1..d9b54d8d 100644 --- a/devui/toggle/demo/custom/custom.component.ts +++ b/devui/toggle/demo/custom/custom.component.ts @@ -3,8 +3,6 @@ import { Component } from '@angular/core'; @Component({ selector: 'd-custom', templateUrl: './custom.component.html', - styleUrls: ['./custom.component.scss'] + styleUrls: ['./custom.component.scss'], }) -export class CustomComponent { - constructor() { } -} +export class CustomComponent {} diff --git a/devui/toggle/demo/toggle-design.component.ts b/devui/toggle/demo/toggle-design.component.ts index a12fa0ac..1511fa8c 100644 --- a/devui/toggle/demo/toggle-design.component.ts +++ b/devui/toggle/demo/toggle-design.component.ts @@ -1,14 +1,12 @@ -import { Component, OnInit } from "@angular/core"; +import { Component, OnInit } from '@angular/core'; import { environment } from 'src/environments/environment'; @Component({ selector: 'd-toggle-design', templateUrl: './toggle-design.component.html', }) - export class ToggleDesignComponent implements OnInit { - imgSrc; - constructor() { } + imgSrc: string; ngOnInit(): void { this.imgSrc = environment.deployPrefix + 'assets/no-data.png'; diff --git a/devui/toggle/demo/two-binding/two-binding.component.html b/devui/toggle/demo/two-binding/two-binding.component.html index f818488e..67add1aa 100644 --- a/devui/toggle/demo/two-binding/two-binding.component.html +++ b/devui/toggle/demo/two-binding/two-binding.component.html @@ -1 +1,4 @@ - {{ enable }} +
+ + state: {{ enable }} +
diff --git a/devui/toggle/demo/two-binding/two-binding.component.ts b/devui/toggle/demo/two-binding/two-binding.component.ts index 1c695b24..9df00a05 100644 --- a/devui/toggle/demo/two-binding/two-binding.component.ts +++ b/devui/toggle/demo/two-binding/two-binding.component.ts @@ -1,19 +1,24 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { DialogService } from 'ng-devui/modal'; @Component({ selector: 'd-two-binding', - templateUrl: './two-binding.component.html' + templateUrl: './two-binding.component.html', + styles: [ + ` + d-toggle { + display: block; + margin-bottom: 8px; + } + `, + ], }) -export class TwoBindingComponent implements OnInit { +export class TwoBindingComponent { enable = true; - constructor(private dialogService: DialogService) { } + constructor(private dialogService: DialogService) {} - ngOnInit() { - } - - beforeChange: (value: any) => Promise = (currentValue) => { + beforeChange: (value: any) => Promise = (currentValue) => { console.log('currentValue: ' + currentValue); return new Promise((resolve) => { const results = this.dialogService.open({ @@ -31,7 +36,7 @@ export class TwoBindingComponent implements OnInit { handler: ($event: Event) => { results.modalInstance.hide(); resolve(true); - } + }, }, { id: 'btn-cancel', @@ -40,15 +45,14 @@ export class TwoBindingComponent implements OnInit { handler: ($event: Event) => { results.modalInstance.hide(); resolve(false); - } + }, }, - ] + ], }); }); }; - onChange2(state) { + onChange(state) { console.log(state); } - } diff --git a/devui/toggle/doc/api-cn.md b/devui/toggle/doc/api-cn.md index 78e458d7..0fff0649 100644 --- a/devui/toggle/doc/api-cn.md +++ b/devui/toggle/doc/api-cn.md @@ -1,30 +1,34 @@ # 如何使用 -在module中引入: + +在 module 中引入: + ```ts import { ToggleModule } from 'ng-devui/toggle'; ``` 在页面中使用: + ```html ``` -# Toggle -## d-toggle 参数 +## Toggle + +### d-toggle 参数 -| 参数 | 类型 | 默认 | 说明 | 跳转 Demo |全局配置项| -| :----------------: | :----------: | :-----------------------------: | :---: | :-------------------------------------------------------------------------- | ------------------------------------------- | -| size | `'sm'\|''\|'lg'` | '' | 可选,开关尺寸大小 | [基本用法](demo#basic-usage) | -| color | `string` | -- | 可选,开关打开时的自定义颜色 | [自定义样式](demo#custom) | -| checked | `boolean` | false | 可选,开关是否打开,默认关闭 | [基本用法](demo#basic-usage) | -| ngModel | `boolean` | false | 可选,指定当前是否打开,可双向绑定 | [双向绑定](demo#two-binding) | -| disabled | `boolean` | false | 可选,是否禁用开关 | [基本用法](demo#basic-usage) | -| checkedContent | `string\|TemplateRef` | -- | 可选,开关打开时内部模板 | [自定义样式](demo#custom) | -| uncheckedContent | `string\|TemplateRef` | -- | 可选,开关关闭时内部模板 | [自定义样式](demo#custom) | -| beforeChange | `Function\|Promise\|()=> Observable` | -- | 可选,开关变化前的回调函数,返回 boolean 类型,返回 false 可以阻止开关的变化 | [双向绑定](demo#two-binding) | +| 参数 | 类型 | 默认 | 说明 | 跳转 Demo | 全局配置项 | +| ---------------- | --------------------------------------------- | ----- | --------------------------------------------------------------------------- | ---------------------------- | ---------- | +| size | `'sm'\|''\|'lg'` | '' | 可选,开关尺寸大小 | [基本用法](demo#basic-usage) | +| color | `string` | -- | 可选,开关打开时的自定义颜色 | [自定义样式](demo#custom) | +| checked | `boolean` | false | 可选,开关是否打开,默认关闭 | [基本用法](demo#basic-usage) | +| ngModel | `boolean` | false | 可选,指定当前是否打开,可双向绑定 | [双向绑定](demo#two-binding) | +| disabled | `boolean` | false | 可选,是否禁用开关 | [基本用法](demo#basic-usage) | +| checkedContent | `string\|TemplateRef` | -- | 可选,开关打开时内部模板 | [自定义样式](demo#custom) | +| uncheckedContent | `string\|TemplateRef` | -- | 可选,开关关闭时内部模板 | [自定义样式](demo#custom) | +| beforeChange | `Function\|Promise\|()=> Observable` | -- | 可选,开关变化前的回调函数,返回 boolean 类型,返回 false 可以阻止开关的变化 | [双向绑定](demo#two-binding) | -## d-toggle 事件 +### d-toggle 事件 -| 事件 | 类型 | 说明 | 跳转 Demo | -| :----: | :---------------------: | :------------------------------------ | ------------------------------------------- | +| 事件 | 类型 | 说明 | 跳转 Demo | +| ------ | ----------------------- | ------------------------------------- | ------------------------- | | change | `EventEmitter` | 可选,开关打开返回 true,关闭返回 false | [回调事件](demo#callback) | diff --git a/devui/toggle/doc/api-en.md b/devui/toggle/doc/api-en.md index 598ad46b..1c177378 100644 --- a/devui/toggle/doc/api-en.md +++ b/devui/toggle/doc/api-en.md @@ -1,30 +1,34 @@ # How to use + Import into module: + ```ts import { ToggleModule } from 'ng-devui/toggle'; ``` In the page: + ```html ``` -# Toggle + +## Toggle ### d-toggle parameter -| Parameter | Type | Default | Description | Jump to Demo |Global Config| -| :----------------: | :----------: | :-----------------------------: | :---: | :-------------------------------------------------------------------------- | ------------------------------------------- | -| size | `'sm'\|''\|'lg'` |'' | Optional. Switch size. | [Basic Usage](demo#basic-usage) | -| color | `string` | -- | Optional. Customized color when the switch is enabled. | [Custom Style](demo#custom) | -| checked | `boolean` | false | Optional. Specifies whether to enable the function. The function is disabled by default. | [Basic Usage](demo#basic-usage) | -| ngModel | `boolean` | false | Optional. Specifies whether to enable the function. Bidirectional binding is supported. | [NgModel](demo#two-binding) | -| disabled | `boolean` | false | Optional. Indicating whether to disable the function. | [Basic Usage](demo#basic-usage) | -| checkedContent | `string\|TemplateRef` | -- | Optional. Customized inner template when the switch is enabled. | [Custom Style](demo#custom) | -| uncheckedContent | `string\|TemplateRef` | -- | Optional. Customized inner template when the switch is unenabled. | [Custom Style](demo#custom) | -| beforeChange | `Function\|Promise\|()=> Observable` | -- |Optional. Callback function before a switch is changed. The return value is of the boolean type. If false is returned, the switch is not changed. | [NgModel](demo#two-binding) | +| Parameter | Type | Default | Description | Jump to Demo | Global Config | +| ---------------- | --------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ------------- | +| size | `'sm'\|''\|'lg'` | '' | Optional. Switch size. | [Basic Usage](demo#basic-usage) | +| color | `string` | -- | Optional. Customized color when the switch is enabled. | [Custom Style](demo#custom) | +| checked | `boolean` | false | Optional. Specifies whether to enable the function. The function is disabled by default. | [Basic Usage](demo#basic-usage) | +| ngModel | `boolean` | false | Optional. Specifies whether to enable the function. Bidirectional binding is supported. | [NgModel](demo#two-binding) | +| disabled | `boolean` | false | Optional. Indicating whether to disable the function. | [Basic Usage](demo#basic-usage) | +| checkedContent | `string\|TemplateRef` | -- | Optional. Customized inner template when the switch is enabled. | [Custom Style](demo#custom) | +| uncheckedContent | `string\|TemplateRef` | -- | Optional. Customized inner template when the switch is unenabled. | [Custom Style](demo#custom) | +| beforeChange | `Function\|Promise\|()=> Observable` | -- | Optional. Callback function before a switch is changed. The return value is of the boolean type. If false is returned, the switch is not changed. | [NgModel](demo#two-binding) | ### d-toggle event -| Event | Type | Description | Jump to Demo | -| :----: | :---------------------: | :------------------------------------ | ------------------------------------------- | +| Event | Type | Description | Jump to Demo | +| ------ | ----------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------- | | change | `EventEmitter` | Optional. If the function is enabled, true is returned. If the function is disabled, false is returned. | [Callback Event](demo#callback) | diff --git a/devui/toggle/toggle.component.scss b/devui/toggle/toggle.component.scss index e3bad4b2..85264dc8 100755 --- a/devui/toggle/toggle.component.scss +++ b/devui/toggle/toggle.component.scss @@ -5,6 +5,7 @@ :host { display: inline-block; + width: fit-content; font-size: 0; line-height: 1; vertical-align: middle; @@ -26,23 +27,17 @@ transition: $devui-animation-duration-slow $devui-animation-ease-in-out-smooth all; &:not(.devui-checked):hover { - border-color: $devui-line; - } - - &:active { - border-color: $devui-brand-active-focus; - } - - &.devui-checked:hover { - border-color: $devui-brand-active; + background-color: $devui-gray-40; + border-color: $devui-gray-40; } .devui-toggle-inner-wrapper { display: inline-block; width: 100%; height: 100%; - padding-left: 16px; - font-size: $devui-font-size; + font-size: $devui-font-size-sm; + line-height: 18px; + padding-left: 14px; .devui-toggle-inner { width: 100%; @@ -54,29 +49,29 @@ &.devui-checked .devui-toggle-inner-wrapper { padding-left: unset; - padding-right: 16px; + padding-right: 14px; } small { - width: 16px; - height: 16px; + width: 12px; + height: 12px; background: $devui-light-text; border-radius: $devui-border-radius-full; position: absolute; - top: 1px; - left: 1px; + top: 3px; + left: 3px; transition: $devui-animation-duration-base $devui-animation-ease-in-out-smooth all; &.mouseDown { - width: 20px; + width: 15px; } } &.devui-checked small { - left: 19px; + left: 21px; &.mouseDown { - left: 15px; + left: 18px; } } @@ -85,32 +80,33 @@ height: 30px; .devui-toggle-inner-wrapper { - padding-left: 28px; font-size: $devui-font-size-modal-title; + line-height: 30px; + padding-left: 24px; } &.devui-checked .devui-toggle-inner-wrapper { padding-left: unset; - padding-right: 28px; + padding-right: 24px; } & small { - width: 28px; - height: 28px; - top: 1px; - left: 1px; + width: 22px; + height: 22px; + top: 4px; + left: 4px; &.mouseDown { - width: 32px; + width: 26px; } } &.devui-checked small { background: $devui-light-text; - left: 29px; + left: 32px; &.mouseDown { - left: 25px; + left: 28px; } } } @@ -120,8 +116,9 @@ height: 14px; .devui-toggle-inner-wrapper { + font-size: $devui-font-size-sm; + line-height: 14px; padding-left: 12px; - font-size: $devui-font-size; } &.devui-checked .devui-toggle-inner-wrapper { @@ -130,12 +127,14 @@ } & small { - width: 12px; - height: 12px; + width: 10px; + height: 10px; position: absolute; + top: 2px; + left: 3px; &.mouseDown { - width: 14px; + width: 12px; } } @@ -153,13 +152,13 @@ border-color: $devui-brand; &:hover { - background: $devui-brand-active; - border-color: $devui-brand-active; + background: $devui-primary-hover; + border-color: $devui-primary-hover; } &:active { - background: $devui-brand-active-focus; - border-color: $devui-brand-active-focus; + background: $devui-primary-hover; + border-color: $devui-primary-hover; } } diff --git a/devui/tooltip/tooltip.directive.ts b/devui/tooltip/tooltip.directive.ts index 29038a23..ea1d5f7a 100755 --- a/devui/tooltip/tooltip.directive.ts +++ b/devui/tooltip/tooltip.directive.ts @@ -148,6 +148,10 @@ export class TooltipDirective implements OnChanges, AfterViewInit, OnDestroy { ngOnChanges(changes: SimpleChanges): void { if (this.tooltipComponentRef) { const { content, position, showAnimation } = changes; + if (!content.currentValue) { + this.hide(); + } + if (content) { this.instanceAssignValue('content'); } diff --git a/devui/transfer/demo/custom/transfer-demo-custom.component.scss b/devui/transfer/demo/custom/transfer-demo-custom.component.scss index 43018c71..a357e7e3 100644 --- a/devui/transfer/demo/custom/transfer-demo-custom.component.scss +++ b/devui/transfer/demo/custom/transfer-demo-custom.component.scss @@ -9,11 +9,7 @@ -moz-align-items: center; -ms-align-items: center; -o-align-items: center; - margin-left: 20px; -} - -.line { - height: 1px; - background: $devui-dividing-line; - margin-bottom: 8px; + padding-left: 20px; + background: $devui-area; + font-weight: 600; } diff --git a/devui/transfer/transfer.component.html b/devui/transfer/transfer.component.html index 87fb0ded..c603ab39 100644 --- a/devui/transfer/transfer.component.html +++ b/devui/transfer/transfer.component.html @@ -4,7 +4,7 @@
+ + {{ titles.source }} + {{ sourceCheckedLen }}/{{ sourceDisplayOptionLen }}
-
(); @Output() nodeDblClicked = new EventEmitter(); @Output() nodeRightClicked = new EventEmitter<{ node: TreeNode; event: MouseEvent }>(); @@ -293,8 +294,8 @@ export class OperableTreeComponent implements OnInit, OnDestroy, AfterViewInit { const dragNodeIds: string[] = []; // 使用对象存储选择状态,因为节点id为数字,避免产生长索引数组影响性能 const dragNodesCheckStatus = {}; - if (this.canActivateMultipleNode) { - const multipleData = transferData['multipleData']; + const multipleData = transferData['multipleData']; + if (this.canActivateMultipleNode && multipleData?.length > 1) { // 多选时遍历所有被激活的节点确认是否为目标节点或其父节点并储存可用节点的选择状态 // 反序遍历用于先处理子节点以更新父子同时拖拽的状态 multipleData.reverse().forEach((node) => this.reverseSelection(node, dropNodeId, dragNodeIds, dragNodesCheckStatus)); @@ -335,15 +336,15 @@ export class OperableTreeComponent implements OnInit, OnDestroy, AfterViewInit { } setSelection(dropNode, dragNodeId, dragNodeIds, dragNodesCheckStatus) { - if (this.canActivateMultipleNode) { + let finalDragNodeIds = [dragNodeId]; + if (this.canActivateMultipleNode && dragNodeIds?.length > 1) { dragNodeIds.reverse().forEach((id) => this.handleDropNode(id, dropNode)); + finalDragNodeIds = dragNodeIds; } else { this.handleDropNode(dragNodeId, dropNode); } this.treeFactory.renderFlattenTree(); - // 移动结束后向上整理选择状态,恢复半选状态 - const finalDragNodeIds = this.canActivateMultipleNode ? dragNodeIds : [dragNodeId]; finalDragNodeIds.forEach((id) => { const node = this.treeFactory.getCompleteNodeById(id); node.data.isChecked = dragNodesCheckStatus[id].checked; diff --git a/devui/tree/tree-factory.class.ts b/devui/tree/tree-factory.class.ts index b635f85e..6c1262e7 100755 --- a/devui/tree/tree-factory.class.ts +++ b/devui/tree/tree-factory.class.ts @@ -643,6 +643,15 @@ export class TreeFactory { } } + public toggleAllNodes(toggle = true) { + for (const id of Object.keys(this.nodes)) { + this.nodes[id].data.isOpen = toggle; + } + if (this.isVirtualScroll) { + this.renderFlattenTree(); + } + } + public transferToTreeNode( originNode, parentId?, diff --git a/devui/upload/doc/api-cn.md b/devui/upload/doc/api-cn.md index 59038bbf..2e4cd070 100644 --- a/devui/upload/doc/api-cn.md +++ b/devui/upload/doc/api-cn.md @@ -38,6 +38,7 @@ import { UploadModule } from 'ng-devui/upload'; | dynamicUploadOptionsFn | [`IUploadOptions`](#iuploadoptions) | -- | 为文件动态设置自定义的上传参数, 参数为当前选中文件及`uploadOptions`的值 | [基本用法](demo#basic-usage) | | disabled | `boolean` | false | 可选,是否禁用上传组件 | [基本用法](demo#basic-usage) | | showTip | `boolean` | false | 可选,是否显示上传提示信息 | [自动上传](demo#auto-upload) | +| showGlowStyle | `boolean` | true | 可选,是否显示悬浮发光效果 | ## d-single-upload 事件 diff --git a/devui/upload/doc/api-en.md b/devui/upload/doc/api-en.md index 01e144f4..865e6a1f 100644 --- a/devui/upload/doc/api-en.md +++ b/devui/upload/doc/api-en.md @@ -36,6 +36,7 @@ In the page: | dynamicUploadOptionsFn | [`IUploadOptions`](#iuploadoptions) | -- | Set upload parameters dynamically for file. The parameters are the currently selected file and the value of `uploadOptions` | [Basic Usage](demo#basic-usage) | | disabled | `boolean` | false | Optional. Specifies whether to disable the upload component. | [Basic Usage](demo#basic-usage) | | showTip | `boolean` | false | Optional. Indicating whether to display the upload message. | [Automatic Upload](demo#auto-upload) | +| showGlowStyle | `boolean` | true | (Optional) Indicates whether to display the floating glow effect.| ## d-single-upload event diff --git a/devui/upload/multiple-upload.component.ts b/devui/upload/multiple-upload.component.ts index 2d7cbc90..0e35027f 100755 --- a/devui/upload/multiple-upload.component.ts +++ b/devui/upload/multiple-upload.component.ts @@ -2,7 +2,7 @@ import { DOCUMENT } from '@angular/common'; import { Component, EventEmitter, - forwardRef, Inject, Input, OnDestroy, + forwardRef, HostBinding, Inject, Input, OnDestroy, OnInit, Output, TemplateRef, ViewChild @@ -10,6 +10,7 @@ import { import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { I18nInterface, I18nService } from 'ng-devui/i18n'; import { ToastService } from 'ng-devui/toast'; +import { DevConfigService, WithConfig } from 'ng-devui/utils'; import { from, Observable, Subscription } from 'rxjs'; import { debounceTime, last, map, mergeMap } from 'rxjs/operators'; import { @@ -57,6 +58,10 @@ export class MultipleUploadComponent implements OnDestroy, OnInit { @Input() beforeUpload: (files) => boolean | Promise | Observable; @Input() setCustomUploadOptions: (files, uploadOptions) => IUploadOptions; @Input() enableDrop = false; + @Input() @WithConfig() showGlowStyle = true; + @HostBinding('class.devui-glow-style') get hasGlowStyle () { + return this.showGlowStyle; + }; @Output() successEvent: EventEmitter> = new EventEmitter>(); @Output() errorEvent: EventEmitter> = new EventEmitter>(); @Output() deleteUploadedFileEvent: EventEmitter = new EventEmitter(); @@ -79,6 +84,7 @@ export class MultipleUploadComponent implements OnDestroy, OnInit { private selectFiles: SelectFiles, private i18n: I18nService, @Inject(DOCUMENT) private doc: any, + private devConfigService: DevConfigService, private toastService: ToastService) { this.document = this.doc; } diff --git a/devui/upload/multiple-upload.spec.ts b/devui/upload/multiple-upload.spec.ts new file mode 100644 index 00000000..4814a40a --- /dev/null +++ b/devui/upload/multiple-upload.spec.ts @@ -0,0 +1,206 @@ +import { Component, DebugElement, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed, fakeAsync, flush, tick } from '@angular/core/testing'; +import { FormsModule } from '@angular/forms'; +import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { DomHelper } from '../utils/testing/dom-helper'; +import { IFileOptions, IUploadOptions, UploadStatus } from './file-uploader.types'; +import { MultipleUploadComponent } from './multiple-upload.component'; +import { UploadModule } from './upload.module'; +@Component({ + template: ` + + + ` +}) +class TestUploadComponent { + @ViewChild('multipleUpload') multipleUpload: MultipleUploadComponent; + fileOptions: IFileOptions = { + multiple: true, + accept: '.xls,.xlsx,.pages,.mp3,.png', + webkitdirectory: true + }; + additionalParameter = { + name: 'tom', + age: 11 + }; + uploadedFiles: Array = []; + uploadOptions: IUploadOptions = { + uri: '/upload', + headers: {}, + additionalParameter: this.additionalParameter, + maximumSize: 0.5, + method: 'POST', + fileFieldName: 'dFile', + withCredentials: true, + responseType: 'json' + }; + files = []; + autoUpload = false; + oneTimeUpload = false; + dynamicUploadOptionsFn(file, options) { + let uploadOptions = options; + if (file.type === 'application/pdf') { + uploadOptions = { + uri: '/upload', + headers: {}, + additionalParameter: this.additionalParameter, + maximumSize: 50, + method: 'POST', + fileFieldName: 'dFile', + withCredentials: true, + responseType: 'json' + }; + } + return uploadOptions; + } + + beforeUpload(file) { + return true; + } + + onSuccess = jasmine.createSpy('upload successfully'); + deleteUploadedFile = jasmine.createSpy('deleUploadedFile'); + onError = jasmine.createSpy('upload failed'); +} + +describe('upload', () => { + let fixture: ComponentFixture; + let debugEl: DebugElement; + let component: TestUploadComponent; + let domHelper: DomHelper; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [UploadModule, FormsModule, NoopAnimationsModule], + declarations: [TestUploadComponent] + }); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TestUploadComponent); + domHelper = new DomHelper(fixture); + debugEl = fixture.debugElement; + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + describe('basic', () => { + it('should render upload component correctly', () => { + expect(component).toBeTruthy(); + }); + + it('should upload file successfully', fakeAsync(() => { + const file1 = new File(['upload'], "upload1.png"); + const file2 = new File(['upload'], "upload2.png"); + component.multipleUpload.multipleUploadViewComponent.addFile(file1); + component.multipleUpload.multipleUploadViewComponent.addFile(file2); + component.multipleUpload.multipleUploadViewComponent.fileUploaders.forEach(FileUploader => { + spyOn(FileUploader, 'sendMultiple').and.callFake(() => { + return Promise.resolve({ file: FileUploader.file, response: 'successful', status: UploadStatus.uploaded }); + }); + }); + const el: HTMLElement = debugEl.query(By.css('.devui-btn-default.devui-btn-common')).nativeElement; + el.dispatchEvent(new Event('click')); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + expect(component.onSuccess).toHaveBeenCalled(); + })); + it('should upload file filed', fakeAsync(() => { + const file1 = new File(['upload'], "upload1.png"); + const file2 = new File(['upload'], "upload2.png"); + component.multipleUpload.multipleUploadViewComponent.addFile(file1); + component.multipleUpload.multipleUploadViewComponent.addFile(file2); + component.multipleUpload.multipleUploadViewComponent.fileUploaders.forEach(FileUploader => { + spyOn(FileUploader, 'sendMultiple').and.callFake(() => { + return Promise.reject({ file: FileUploader.file, response: 'error', status: UploadStatus.failed }); + }); + }); + const el: HTMLElement = debugEl.query(By.css('.devui-btn-default.devui-btn-common')).nativeElement; + el.dispatchEvent(new Event('click')); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + expect(component.onError).toHaveBeenCalled(); + })); + + it('should delete file successfully', fakeAsync(() => { + const file1 = new File(['upload'], "upload1.png"); + const file2 = new File(['upload'], "upload2.png"); + component.multipleUpload.multipleUploadViewComponent.addFile(file1); + component.multipleUpload.multipleUploadViewComponent.addFile(file2); + component.multipleUpload.multipleUploadViewComponent.fileUploaders.forEach(FileUploader => { + spyOn(FileUploader, 'sendMultiple').and.callFake(() => { + return Promise.resolve({ file: FileUploader.file, response: 'successful', status: UploadStatus.uploaded }); + }); + }); + const el: HTMLElement = debugEl.query(By.css('.devui-btn-default.devui-btn-common')).nativeElement; + el.dispatchEvent(new Event('click')); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + expect(component.onSuccess).toHaveBeenCalled(); + const deleteIcon: HTMLElement = debugEl.query(By.css('.devui-file-tag .icon.icon-close')).nativeElement; + deleteIcon.dispatchEvent(new Event('click')); + expect(component.deleteUploadedFile).toHaveBeenCalled(); + expect(component.multipleUpload.multipleUploadViewComponent.fileUploaders.length).toEqual(1); + })); + + it('should write value correctly', fakeAsync(() => { + expect(component).toBeTruthy(); + component.files = [new File(['upload'], "upload1.png"), new File(['upload'], "upload2.png")]; + tick(); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + // 避免第一次进入writeValue时值为空的错误判断 + if (component.files) { + expect(component.multipleUpload.multipleUploadViewComponent.fileUploaders[0].file.name).toEqual(component.files[0].name); + expect(component.multipleUpload.multipleUploadViewComponent.fileUploaders[1].file.name).toEqual(component.files[1].name); + } + })); + + it('should one time upload file successfully', fakeAsync(() => { + component.oneTimeUpload = true; + const file1 = new File(['upload'], "upload1.png"); + const file2 = new File(['upload'], "upload2.png"); + component.multipleUpload.multipleUploadViewComponent.addFile(file1); + component.multipleUpload.multipleUploadViewComponent.addFile(file2); + component.multipleUpload.multipleUploadViewComponent.fileUploaders.forEach(FileUploader => { + spyOn(FileUploader, 'send').and.callFake(() => { + return Promise.resolve({ file: FileUploader.file, response: 'successful', status: UploadStatus.uploaded }); + }); + }); + fixture.detectChanges(); + const el: HTMLElement = debugEl.query(By.css('.devui-btn-default.devui-btn-common')).nativeElement; + el.dispatchEvent(new Event('click')); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + expect(component.onSuccess).toHaveBeenCalled(); + })); + }); +}); diff --git a/devui/upload/single-upload.component.ts b/devui/upload/single-upload.component.ts index 21bbe7ad..edefa358 100755 --- a/devui/upload/single-upload.component.ts +++ b/devui/upload/single-upload.component.ts @@ -1,8 +1,11 @@ import { DOCUMENT } from '@angular/common'; -import { Component, EventEmitter, forwardRef, Inject, Input, OnDestroy, OnInit, Output, TemplateRef, ViewChild } from '@angular/core'; +import { + Component, EventEmitter, forwardRef, HostBinding, Inject, Input, OnDestroy, OnInit, Output, TemplateRef, ViewChild +} from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { I18nInterface, I18nService } from 'ng-devui/i18n'; import { ToastService } from 'ng-devui/toast'; +import { DevConfigService, WithConfig } from 'ng-devui/utils'; import { from, merge, Observable, Subscription } from 'rxjs'; import { last, map, mergeMap, toArray } from 'rxjs/operators'; import { FileUploader } from './file-uploader.class'; @@ -51,6 +54,10 @@ export class SingleUploadComponent implements OnDestroy, OnInit, ControlValueAcc @Output() fileDrop: EventEmitter = new EventEmitter(); @Output() fileOver: EventEmitter = new EventEmitter(); @Output() fileSelect: EventEmitter = new EventEmitter(); + @Input() @WithConfig() showGlowStyle = true; + @HostBinding('class.devui-glow-style') get hasGlowStyle () { + return this.showGlowStyle; + }; @ViewChild('dSingleUploadView', { static: true }) singleUploadViewComponent: SingleUploadViewComponent; UploadStatus = UploadStatus; isDropOVer = false; @@ -66,7 +73,8 @@ export class SingleUploadComponent implements OnDestroy, OnInit, ControlValueAcc private i18n: I18nService, private selectFiles: SelectFiles, @Inject(DOCUMENT) private doc: any, - private toastService: ToastService + private toastService: ToastService, + private devConfigService: DevConfigService ) { this.document = this.doc; } diff --git a/devui/upload/single-upload.spec.ts b/devui/upload/single-upload.spec.ts new file mode 100644 index 00000000..e0109936 --- /dev/null +++ b/devui/upload/single-upload.spec.ts @@ -0,0 +1,196 @@ +import { Component, DebugElement, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed, fakeAsync, flush, tick } from '@angular/core/testing'; +import { FormsModule } from '@angular/forms'; +import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { DomHelper } from '../utils/testing/dom-helper'; +import { IFileOptions, IUploadOptions } from './file-uploader.types'; +import { SingleUploadComponent } from './single-upload.component'; +import { UploadModule } from './upload.module'; +@Component({ + template: ` + + + ` +}) +class TestUploadComponent { + @ViewChild('singleupload') singleupload: SingleUploadComponent; + fileOptions: IFileOptions = { + multiple: false, + }; + additionalParameter = { + name: 'tom', + age: 11 + }; + uploadedFiles: Array = []; + uploadOptions: IUploadOptions = { + uri: '/upload', + headers: {}, + additionalParameter: this.additionalParameter, + maximumSize: 0.5, + method: 'POST', + fileFieldName: 'dFile', + withCredentials: true, + responseType: 'json' + }; + files = []; + dynamicUploadOptionsFn(file, options) { + let uploadOptions = options; + if (file.type === 'application/pdf') { + uploadOptions = { + uri: '/upload', + headers: {}, + additionalParameter: this.additionalParameter, + maximumSize: 50, + method: 'POST', + fileFieldName: 'dFile', + withCredentials: true, + responseType: 'json' + }; + } + return uploadOptions; + } + + beforeUpload(file) { + return true; + } + + onSuccess = jasmine.createSpy('upload successfully'); + deleteUploadedFile = jasmine.createSpy('deleUploadedFile'); + onError = jasmine.createSpy('upload failed'); +} + +describe('upload', () => { + let fixture: ComponentFixture; + let debugEl: DebugElement; + let component: TestUploadComponent; + let domHelper: DomHelper; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [UploadModule, NoopAnimationsModule, FormsModule], + declarations: [TestUploadComponent] + }); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TestUploadComponent); + domHelper = new DomHelper(fixture); + debugEl = fixture.debugElement; + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + describe('basic', () => { + it('should render upload component correctly', () => { + expect(component).toBeTruthy(); + }); + + it('should upload file successfully', fakeAsync(() => { + const file = new File(['upload'], "upload.txt"); + component.singleupload.singleUploadViewComponent.addFile(file); + + component.singleupload.singleUploadViewComponent.fileUploaders.forEach(FileUploader => { + spyOn(FileUploader, 'send').and.callFake(() => { + return Promise.resolve({ file: file, response: 'successful' }); + }); + }); + const el: HTMLElement = debugEl.query(By.css('.devui-btn-default.devui-btn-common')).nativeElement; + el.dispatchEvent(new Event('click')); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + expect(component.onSuccess).toHaveBeenCalled(); + })); + it('should upload file filed', fakeAsync(() => { + const file = new File(['upload'], "upload.txt"); + component.singleupload.singleUploadViewComponent.addFile(file); + component.singleupload.singleUploadViewComponent.fileUploaders.forEach(FileUploader => { + spyOn(FileUploader, 'send').and.callFake(() => { + return Promise.reject({ file: file, response: 'error' }); + }); + }); + const el: HTMLElement = debugEl.query(By.css('.devui-btn-default.devui-btn-common')).nativeElement; + el.dispatchEvent(new Event('click')); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + expect(component.onError).toHaveBeenCalled(); + })); + + it('should delete file successfully', fakeAsync(() => { + const file = new File(['upload'], "upload.txt"); + component.singleupload.singleUploadViewComponent.addFile(file); + component.singleupload.singleUploadViewComponent.fileUploaders.forEach(FileUploader => { + spyOn(FileUploader, 'send').and.callFake(() => { + return Promise.resolve({ file: file, response: 'error' }); + }); + }); + const el: HTMLElement = debugEl.query(By.css('.devui-btn-default.devui-btn-common')).nativeElement; + el.dispatchEvent(new Event('click')); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + expect(component.onSuccess).toHaveBeenCalled(); + const deleteIcon: HTMLElement = debugEl.query(By.css('.devui-file-tag .icon.icon-close')).nativeElement; + deleteIcon.dispatchEvent(new Event('click')); + expect(component.deleteUploadedFile).toHaveBeenCalled(); + })); + + it('should write value correctly', fakeAsync(() => { + expect(component).toBeTruthy(); + component.files = [new File(['upload'], "upload.png")]; + tick(); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + // 避免第一次进入writeValue时值为空的错误判断 + if (component.files) { + expect(component.singleupload.singleUploadViewComponent.fileUploaders[0].file.name).toEqual(component.files[0].name); + } + })); + + it('should chunk upload correctly', fakeAsync(() => { + expect(component).toBeTruthy(); + component.singleupload.defaultChunkSize = 1; + component.uploadOptions.isChunked = true; + const file = new File(['upload'], "upload.txt"); + component.singleupload.singleUploadViewComponent.addFile(file); + const chunkFiles = component.singleupload.createFileChunk(file); + chunkFiles.forEach(FileUploader => { + spyOn(FileUploader, 'send').and.callFake(() => { + return Promise.resolve({ file: FileUploader.file, response: 'successful' }); + }); + }); + spyOn(component.singleupload, 'createFileChunk').and.callFake(() => { + return chunkFiles; + }); + const el: HTMLElement = debugEl.query(By.css('.devui-btn-default.devui-btn-common')).nativeElement; + el.dispatchEvent(new Event('click')); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + expect(component.onSuccess).toHaveBeenCalled(); + })); + }); +}); diff --git a/devui/utils/cdk-overlay-config.type.ts b/devui/utils/cdk-overlay-config.type.ts index fcf5f0bc..d82c1d86 100755 --- a/devui/utils/cdk-overlay-config.type.ts +++ b/devui/utils/cdk-overlay-config.type.ts @@ -1,6 +1,6 @@ import { ConnectedPosition } from '@angular/cdk/overlay'; -// block Ӧ��������ȷ�����ݲ��� api �п��� +// Block application conditions are to be determined and are not open in APIs. export type AppendToBodyScrollStrategyType= 'block' | 'close' | 'noop' | 'reposition'; export type AppendToBodyDirection= 'rightDown'| 'rightUp' | 'leftUp'| 'leftDown' | 'centerDown' | 'centerUp'; export const AppendToBodyDirectionsConfig: { diff --git a/devui/utils/globalConfig/config.ts b/devui/utils/globalConfig/config.ts index 22cfb575..73873f21 100644 --- a/devui/utils/globalConfig/config.ts +++ b/devui/utils/globalConfig/config.ts @@ -1,6 +1,7 @@ import { InjectionToken } from '@angular/core'; export interface IGlobalConfig { showAnimation?: boolean; + showGlowStyle?: boolean; styleType?: 'default' | 'gray'; size?: 'xs' | 'sm' | 'md' | 'lg'; } diff --git a/devui/version.ts b/devui/version.ts index cdd8ac02..b739be72 100755 --- a/devui/version.ts +++ b/devui/version.ts @@ -1,3 +1,3 @@ import { Version } from '@angular/core'; -export const VERSION = new Version('15.1.0'); +export const VERSION = new Version('16.0.0'); diff --git a/package.json b/package.json index dd7e1306..5eeb5872 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "test:lib": "ng test devui-lib --no-watch --no-progress --browsers=ChromeHeadlessCI", "lint": "ng lint", "e2e": "ng e2e", - "postinstall": "ngcc --tsconfig tsconfig.json", "lint:devui": "eslint --ext .js,.ts \"{devui,src}/**/*.{ts,js}\"", "lint:devui:fix": "eslint --ext .js,.ts --fix \"{devui,src}/**/*.{ts,js}\"", "prettier": "prettier --config ./.prettierrc --write \"{devui,src}/**/*.html\"", @@ -26,24 +25,24 @@ "css-compile": "node scripts/sass-handler.js ./devui/style/devui.scss ./publish/devui.css", "css-postcompile": "node scripts/css-postcompile", "mobile-css-compile": "node scripts/sass-handler.js ./devui/style/devui-for-mobile.scss ./publish/devui-for-mobile.css", - "theme-css-compile": "node scripts/sass-handler.js ./devui/theme-collection/extend-theme.scss ./publish/theme-collection/extend-theme.css", + "theme-css-compile": "node scripts/sass-handler.js ./devui/theme-collection/extend-theme.scss ./publish/theme-collection/extend-theme.css && node scripts/sass-handler.js ./devui/theme-collection/overwrite-style.scss ./publish/theme-collection/overwrite-style.css", "css-minify": "cleancss --format breakWith=lf --output ./publish/devui.min.css ./publish/devui.css", "grid-css-compile": "node scripts/sass-handler.js ./devui/style/layout/devui-layout.scss ./publish/devui-layout.css && node scripts/css-handle/handle-prefix.js && cleancss --format breakWith=lf --output ./publish/devui-layout.min.css ./publish/devui-layout.css", "build:devui:ivy": "node --max_old_space_size=8192 ./node_modules/@angular/cli/bin/ng build devui-lib" }, "dependencies": { - "@angular/animations": "^15.1.0", - "@angular/cdk": "^15.1.0", - "@angular/common": "^15.1.0", - "@angular/compiler": "^15.1.0", - "@angular/core": "^15.1.0", - "@angular/forms": "^15.1.0", - "@angular/platform-browser": "^15.1.0", - "@angular/platform-browser-dynamic": "^15.1.0", - "@angular/router": "^15.1.0", + "@angular/animations": "^16.0.0", + "@angular/cdk": "^16.0.0", + "@angular/common": "^16.0.0", + "@angular/compiler": "^16.0.0", + "@angular/core": "^16.0.0", + "@angular/forms": "^16.0.0", + "@angular/platform-browser": "^16.0.0", + "@angular/platform-browser-dynamic": "^16.0.0", + "@angular/router": "^16.0.0", "@devui-design/icons": "^1.2.0", - "@ngx-translate/core": "^14.0.0", - "@ngx-translate/http-loader": "^7.0.0", + "@ngx-translate/core": "^15.0.0", + "@ngx-translate/http-loader": "^8.0.0", "@popperjs/core": "^2.10.2", "bootstrap": "^4.6.0", "color": "^3.1.3", @@ -52,16 +51,16 @@ "gridstack": "^6.0.0", "marked": "^4.0.10", "tslib": "^2.0.0", - "zone.js": "~0.11.4" + "zone.js": "~0.13.1" }, "devDependencies": { - "@angular-builders/custom-webpack": "^15.0.0", - "@angular-devkit/build-angular": "^15.1.1", - "@angular-eslint/builder": "^15.0.0", - "@angular-eslint/eslint-plugin": "^15.0.0", - "@angular/cli": "^15.1.1", - "@angular/compiler-cli": "^15.1.0", - "@angular/language-service": "^15.1.0", + "@angular-builders/custom-webpack": "^16.0.0", + "@angular-devkit/build-angular": "^16.0.0", + "@angular-eslint/builder": "^16.0.0", + "@angular-eslint/eslint-plugin": "^16.0.0", + "@angular/cli": "^16.0.0", + "@angular/compiler-cli": "^16.0.0", + "@angular/language-service": "^16.0.0", "@commitlint/cli": "^11.0.0", "@commitlint/config-conventional": "^11.0.0", "@stackblitz/sdk": "^1.5.2", @@ -91,7 +90,7 @@ "lint-staged": "^9.2.3", "lodash-es": "^4.17.15", "markdown-loader": "^5.1.0", - "ng-packagr": "^15.1.1", + "ng-packagr": "^16.0.0", "patch-package": "^6.2.2", "postcss": "^8.2.4", "prettier": "^2.1.2", diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 4990d5d6..a72b12cf 100755 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -4,6 +4,18 @@ body { background: $devui-base-bg; } +body[ui-theme='galaxy-theme'] { + .devui-component-icon { + background-color: rgba(26, 26, 28, 0.6); + } +} + +body[ui-theme='galaxy-theme'] { + .devui-nav-slider { + background-color: rgba(26, 26, 28, 0.6) !important; + } +} + @mixin font-content() { font-size: $devui-font-size; font-weight: $devui-font-content-weight; diff --git a/src/app/component/component.route.ts b/src/app/component/component.route.ts index a4bef79f..266de5c6 100755 --- a/src/app/component/component.route.ts +++ b/src/app/component/component.route.ts @@ -1,9 +1,9 @@ +import { Routes } from "@angular/router"; import { ExamplePanelComponent } from './example-panel.component'; import { GetStartedComponent } from './get-started.component'; import { GlobalConfigComponent } from './global-config.component'; import { ComponentsOverviewComponent } from './overview.component'; import { ThemeGuideComponent } from './theme-guide.component'; -import { Routes } from "@angular/router"; export const routesConfig: Routes = [ { @@ -142,6 +142,18 @@ export const routesConfig: Routes = [ bannerName: "17", }, }, + { + path: 'input-group', + component: ExamplePanelComponent, + loadChildren: () => import('../../../devui/input-group/demo/input-group-demo.module').then((m) => m.InputGroupDemoModule), + data: { + type: '数据录入', + enType: 'Data Entry', + name: 'InputGroup', + cnName: '输入框组合', + bannerName: "3", + }, + }, { path: 'alert', component: ExamplePanelComponent, diff --git a/src/app/component/example-panel.component.ts b/src/app/component/example-panel.component.ts index c2bbd6e4..39c50cf1 100755 --- a/src/app/component/example-panel.component.ts +++ b/src/app/component/example-panel.component.ts @@ -29,7 +29,7 @@ import { ComponentDataService } from './component.data.service'; selector: 'd-demo-cell', styleUrls: ['./example-panel.component.scss'], templateUrl: './example-panel.component.html', - }) +}) export class ExamplePanelComponent implements OnInit, AfterViewInit, OnDestroy { @Input() data: IExampleData; @ViewChildren('html') html: QueryList; @@ -69,6 +69,10 @@ export class ExamplePanelComponent implements OnInit, AfterViewInit, OnDestroy { this.comDataService.getComData().subscribe((value) => { this.componentsData = value; }); + this.setI18n(); + this.translate.onLangChange.subscribe((event: TranslationChangeEvent) => { + this.setI18n(); + }); } ngOnDestroy(): void { if (this.subs) { @@ -104,6 +108,10 @@ export class ExamplePanelComponent implements OnInit, AfterViewInit, OnDestroy { this.imgPrefix = './' + this.srcPrefix + '/overview/'; } + setI18n() { + this.footer = this.translate.instant('footer') || {}; + } + addScrollEvent() { this.subs.add( fromEvent(window, 'scroll').subscribe((value) => { diff --git a/src/app/component/get-started.component.ts b/src/app/component/get-started.component.ts index 7af0bce2..7277a98c 100755 --- a/src/app/component/get-started.component.ts +++ b/src/app/component/get-started.component.ts @@ -20,6 +20,7 @@ import * as hljs from 'highlight.js/lib/core'; ` .readme { box-sizing: border-box; + margin-bottom: 40px; } `, ], diff --git a/src/app/component/getStarted-cn.md b/src/app/component/getStarted-cn.md index 639c8d7a..e6183cec 100644 --- a/src/app/component/getStarted-cn.md +++ b/src/app/component/getStarted-cn.md @@ -10,7 +10,7 @@ ### Angular版本 -当前支持的Angular版本 `^15.0.0` +当前支持的Angular版本 `^16.0.0` ### 1. 创建一个项目 diff --git a/src/app/component/getStarted-en.md b/src/app/component/getStarted-en.md index 3cc7a5eb..7e9d863e 100644 --- a/src/app/component/getStarted-en.md +++ b/src/app/component/getStarted-en.md @@ -10,7 +10,7 @@ For details about how to use this function, see the following StackBlitz demonst ### Angular version -The supported Angular version is `^15.0.0`. +The supported Angular version is `^16.0.0`. ### 1. Create a project diff --git a/src/app/component/global-config.component.ts b/src/app/component/global-config.component.ts index 253464bb..d6bba95b 100644 --- a/src/app/component/global-config.component.ts +++ b/src/app/component/global-config.component.ts @@ -21,6 +21,7 @@ import * as hljs from 'highlight.js/lib/core'; ` .readme { box-sizing: border-box; + margin-bottom: 40px; } `, ], diff --git a/src/app/component/overview.component.ts b/src/app/component/overview.component.ts index f5b5bb81..c05a66cc 100644 --- a/src/app/component/overview.component.ts +++ b/src/app/component/overview.component.ts @@ -35,17 +35,22 @@ export class ComponentsOverviewComponent implements OnInit, OnDestroy { nowFilter: string; tagList: any = [ { title: '', name: 'newChangeCmps', checked: false }, - { title: '', name: 'recentlySuggestCmps', checked: false } + { title: '', name: 'recentlySuggestCmps', checked: false }, ]; currentLang: string; body: any; scrollSubscription: Subscription; constructor( - private translate: TranslateService, private router: Router, private comDataService: ComponentDataService, - private i18n: I18nService, @Inject(DOCUMENT) public doc: any + private translate: TranslateService, + private router: Router, + private comDataService: ComponentDataService, + private i18n: I18nService, + @Inject(DOCUMENT) public doc: any ) { - this.comDataService.getComData().subscribe(value => { this.componentsData = value; }); + this.comDataService.getComData().subscribe((value) => { + this.componentsData = value; + }); this.componentsDataDisplay = cloneDeep(this.componentsData); this.setI18n(); @@ -70,13 +75,15 @@ export class ComponentsOverviewComponent implements OnInit, OnDestroy { setI18n() { this.overviewText = this.translate.instant('public').overview || {}; - this.tagList.forEach(tag => { tag.title = this.overviewText ? this.overviewText[tag.name] : ''; }); + this.tagList.forEach((tag) => { + tag.title = this.overviewText ? this.overviewText[tag.name] : ''; + }); this.currentLang = this.i18n.getI18nText().locale; } calNumberOfComponents() { this.totalNumComponents = 0; - this.componentsData.forEach(components => { + this.componentsData.forEach((components) => { if (!components.nodisplay) { this.totalNumComponents = this.totalNumComponents + components.children.length; } @@ -84,7 +91,7 @@ export class ComponentsOverviewComponent implements OnInit, OnDestroy { } setPrefix() { - this.imgPrefix = './' + this.srcPrefix + '/overview/'; + this.imgPrefix = this.srcPrefix + '/overview/'; } setTheme() { @@ -101,9 +108,9 @@ export class ComponentsOverviewComponent implements OnInit, OnDestroy { setComponentsSuggest(type) { this.componentsSuggest = []; - this.componentsData.forEach(cmpList => { - cmpList.children.forEach(cmp => { - if (Array.isArray(type) && type.find(scope => scope.toLocaleLowerCase() === cmp.lowerName)) { + this.componentsData.forEach((cmpList) => { + cmpList.children.forEach((cmp) => { + if (Array.isArray(type) && type.find((scope) => scope.toLocaleLowerCase() === cmp.lowerName)) { this.componentsSuggest.push(cloneDeep(cmp)); } else if (type === 'newChange' && cmp.newChange) { this.componentsSuggest.push(cloneDeep(cmp)); @@ -122,19 +129,21 @@ export class ComponentsOverviewComponent implements OnInit, OnDestroy { searchComponent(event) { this.nowFilter = undefined; - this.tagList.forEach(tag => { tag.checked = false; }); + this.tagList.forEach((tag) => { + tag.checked = false; + }); this.setComponentsSuggest(suggestScopeList); this.componentsDataDisplay = filterData(event, this.componentsData); this.componentsLooking = []; if (!this.componentsDataDisplay || !this.componentsDataDisplay.length) { - const res = componentMap.find(cmp => { - return cmp.matches.find(child => { + const res = componentMap.find((cmp) => { + return cmp.matches.find((child) => { return child.includes(event); }); }); if (res) { - this.componentsData.forEach(cmpList => { - cmpList.children.forEach(cmp => { + this.componentsData.forEach((cmpList) => { + cmpList.children.forEach((cmp) => { if (res.name.includes(cmp.lowerName)) { this.componentsLooking.unshift(cloneDeep(cmp)); } @@ -145,8 +154,10 @@ export class ComponentsOverviewComponent implements OnInit, OnDestroy { } filter(type) { - const tagIndex = this.tagList.findIndex(tag => tag.name === type); - this.tagList.forEach(tag => { tag.checked = false; }); + const tagIndex = this.tagList.findIndex((tag) => tag.name === type); + this.tagList.forEach((tag) => { + tag.checked = false; + }); if (this.nowFilter !== type) { this.nowFilter = type; this.tagList[tagIndex].checked = true; diff --git a/src/app/component/scope-list.ts b/src/app/component/scope-list.ts index 0e527f59..48a33acf 100644 --- a/src/app/component/scope-list.ts +++ b/src/app/component/scope-list.ts @@ -8,18 +8,19 @@ export const suggestScopeList: Array = [ ]; export const newScopeList: Array | string = [ - 'tags', - 'userguide', - 'button', - 'category-search', 'data-table', 'datepicker-pro', - 'editorx', - 'upload', + 'transfer', + 'cascader', + 'tree', + 'nav-sprite', + 'pagination', + 'select', + 'splitter', 'tabs', - 'tree-select', - 'editable-select', - 'typography', + 'progress', + 'category-search', + 'input-group' ]; export const sunsetScopeList: Array | string = [ diff --git a/src/assets/design/button/danger.png b/src/assets/design/button/danger.png index 2f79bf73..1c7973e7 100644 Binary files a/src/assets/design/button/danger.png and b/src/assets/design/button/danger.png differ diff --git a/src/assets/i18n/en-us.json b/src/assets/i18n/en-us.json index f329beb8..6160dc2d 100644 --- a/src/assets/i18n/en-us.json +++ b/src/assets/i18n/en-us.json @@ -158,6 +158,36 @@ "change-values": "Change key value" } }, + "input-group": { + "name": "input-group", + "type": "Data entry", + "path": "input-group", + "description": "Adds a prefix or suffix to the content of one or more class text boxes and provides the adaptive stitching effect.", + "tmw": "Scenario where the content of multiple text boxes needs to be combined.", + "basicDemo": { + "title": "Basic Usage", + "description": "It can contain one or more text box components. Line breaks are not allowed." + }, + "responsiveDemo": { + "title": "Responsive usage", + "description": "It can be spliced based on the container width.", + "alarmRule": "Alarm Rule", + "defaultRule": "Default Rule", + "indicatorName": "Indicator Name", + "indicatorNamePlaceholder": "Enter an indicator name.", + "valueType": "Average", + "valueTypes": "Average, Maximum, Minimum" + }, + "embedDemo": { + "title": "Embed Usage", + "description": "Works with the text-input component and does not support adaptive stitching." + }, + "anchorLinkValues": { + "basic-usage": "Basic Usage", + "responsive-usage": "Responsive usage", + "embed-usage": "Embed Usage" + } + }, "alert": { "name": "Alert", "type": "Feedback", @@ -982,7 +1012,7 @@ }, "colorSource": { "devui-global-bg": "Global background with background color", - "devui-glass-morphism-bg":"Glass morphism background color", + "devui-glass-morphism-bg": "Glass morphism background color", "devui-global-bg-normal": "Global white background", "devui-base-bg": "Base block background white", "devui-base-bg-dark": "Base Block Background Dark (Fixed)", @@ -1017,18 +1047,18 @@ "devui-unavailable": "Unavailable or Disabled", "devui-shadow": "shadow color", "devui-light-shadow": "Weak shadow color", - "devui-icon-fill-weak":"Weakened icon fill color", - "devui-shape-icon-fill":"Shape type svg icon fill color", - "devui-shape-icon-fill-hover":"Shape type svg icon hover color", - "devui-shape-icon-fill-active":"Shape type svg icon active color", - "devui-shape-icon-fill-disabled":"Shape type svg icon disabled color", + "devui-icon-fill-weak": "Weakened icon fill color", + "devui-shape-icon-fill": "Shape type svg icon fill color", + "devui-shape-icon-fill-hover": "Shape type svg icon hover color", + "devui-shape-icon-fill-active": "Shape type svg icon active color", + "devui-shape-icon-fill-disabled": "Shape type svg icon disabled color", "devui-icon-text": "Text icon color, which is the same as the text color.", "devui-icon-bg": "svg Icon Background Color", "devui-icon-fill": "Grey fill color of the svg icon", "devui-icon-fill-hover": "svg Icon Gray Fill Color Hover Feedback Color", "devui-icon-fill-active": "svg Icon Highlight Fill Color (Active)", "devui-icon-fill-active-hover": "svg Icon Highlight Fill Color Hover Feedback Color", - "devui-form-control-bg":"Form control background color", + "devui-form-control-bg": "Form control background color", "devui-form-control-line": "Form control border color, which is the same as the border divider.", "devui-form-control-line-hover": "Form Control Border Hover Feedback Color", "devui-form-control-line-active": "Form Control Border Activation Color for Focus", @@ -1073,8 +1103,8 @@ "devui-primary-bg": "Primary background color", "devui-default-line": "Default border", "devui-default-bg": "Default background color", - "devui-connected-overlay-shadow":"Drop-down-menu shadow color", - "devui-feedback-overlay-shadow":"Information feedback block shadow color" + "devui-connected-overlay-shadow": "Drop-down-menu shadow color", + "devui-feedback-overlay-shadow": "Information feedback block shadow color" }, "catalogs": { "basic": "Basic variable", @@ -1230,7 +1260,9 @@ "devui-shadow": "shadow color", "devui-light-shadow": "Weak shadow color", "devui-connected-overlay-shadow": "Pop-up (overlay) layer with connection points shadow color", - "devui-feedback-overlay-shadow": "Information Prompt Feedback Class shadow color" + "devui-feedback-overlay-shadow": "Information Prompt Feedback Class shadow color", + "devui-hover-shadow": "Dom hover shadow", + "devui-float-block-shadow": "Special floating layer background color" } } }, @@ -1509,6 +1541,10 @@ "title": "Tree table with a large amount of data Interaction", "description": "Advanced operations on tree tables with a large amount of data, including dragging, selecting, deleting, and inheriting most functions of the original table." }, + "virtualScrollTreeTableMultiDragDemo": { + "title": "Draging Big Data Tree Tables in Batches", + "description": "Hold down Ctrl to select and drag rows in batches." + }, "mutilStylesDemo": { "title": "Table Style" }, @@ -1549,7 +1585,8 @@ "muti-drag-row": "Batch Row dragging", "virtual-scroll-tree-table-basic": "Big Data Tree Table Basic", "virtual-scroll-tree-table-operation": "Big Data Tree Table Operation", - "virtual-scroll-tree-table-interaction": "Big Data Tree Table Interaction" + "virtual-scroll-tree-table-interaction": "Big Data Tree Table Interaction", + "virtual-scroll-tree-table-multi-drag": "Big Data Tree Table multiDrag" } }, "datepickerPro": { @@ -2384,7 +2421,11 @@ "tmw": "You can use it easily when you want to search existed data.", "basicDemo": { "title": "Basic usage", - "description": "Set source to a data source." + "description": "Set source to a data source, and use enableSelectedValueList to control whether to display all options when a value is selected." + }, + "ObjectDemo": { + "title": "Set an array of objects as the source", + "description": "Reads the label attribute by default when source is the object array. Use valueParser and formatter methods to customize the display value and format." }, "disableDataDemo": { "title": "Set disable options", @@ -2408,6 +2449,7 @@ }, "anchorLinkValues": { "basic-usage": "Basic usage", + "object-source": "Set an array of objects as the source", "disable-data-with-source": "Set disable options", "with-search-function": "Customized data matching method", "async-data-with-function": "Asynchronously obtaining the data source and setting the matching method", @@ -2715,7 +2757,7 @@ "tmw": "
1. When the operation takes a long time.
2. When an operation takes a long time to interrupt the current interface or background operation.
3. To display the percentage of completed operations or the number of completed steps/total steps.
", "basicDemo": { "title": "Linear Progress Bar", - "description": "You can configure to display text or custom templates on the right or as you progress bar." + "description": "You can configure text and custom templates to be displayed in the middle, right, or as you progress." }, "circleDemo": { "title": "Circular progress bar", @@ -3631,7 +3673,7 @@ "multiRowNotWebkitTitle": "Multiline Text by calculation", "multiRowExpandTitle": "Multiline Text Using collapse" }, - "adaptiveInputDemo":{ + "adaptiveInputDemo": { "title": "Adaptive Input", "description": "During editing, expands the input to textarea, then restored. This feature enables users to easily input and read a large amount of text when using input." }, diff --git a/src/assets/i18n/zh-cn.json b/src/assets/i18n/zh-cn.json index a63af0b7..c94f0427 100644 --- a/src/assets/i18n/zh-cn.json +++ b/src/assets/i18n/zh-cn.json @@ -49,6 +49,8 @@ "showCode": "展开代码", "hideCode": "收起代码", "copyCode": "复制代码", + "codesandbox": "在 CodeSandbox 上打开", + "stackblitz": "在 StackBlitz 上打开", "success": "复制成功" }, "components": { @@ -156,6 +158,36 @@ "change-values": "改变键值" } }, + "input-group": { + "name": "输入框组合", + "type": "数据录入", + "path": "input-group", + "description": "为一个或多个类输入框内容添加前后缀,并提供自适应拼接效果。", + "tmw": "多个类输入框内容需要拼接组合的场景。", + "basicDemo": { + "title": "基本用法", + "description": "可包含单个或多个类输入框组件拼接,不可换行。" + }, + "responsiveDemo": { + "title": "自适应拼接用法", + "description": "多个该组件可根据容器宽度自适应拼接。", + "alarmRule": "告警规则", + "defaultRule": "默认规则", + "indicatorName": "指标名称", + "indicatorNamePlaceholder": "请输入指标名称", + "valueType": "平均值", + "valueTypes": "平均值,最大值,最小值" + }, + "embedDemo": { + "title": "嵌入用法", + "description": "与 text-input 组件配合使用,不支持自适应拼接。" + }, + "anchorLinkValues": { + "basic-usage": "基本用法", + "responsive-usage": "自适应拼接用法", + "embed-usage": "嵌入用法" + } + }, "alert": { "name": "Alert 警告", "type": "反馈", @@ -1217,7 +1249,7 @@ }, "colorSource": { "devui-global-bg": "全局带底色背景", - "devui-glass-morphism-bg":"毛玻璃样式背景色", + "devui-glass-morphism-bg": "毛玻璃样式背景色", "devui-global-bg-normal": "全局白色背景", "devui-base-bg": "基础区块背景白色", "devui-base-bg-dark": "基础区块背景深色(固定)", @@ -1253,17 +1285,17 @@ "devui-shadow": "阴影色", "devui-light-shadow": "弱化阴影色", "devui-icon-text": "文字图标颜色,同 正文颜色", - "devui-icon-fill-weak":"弱化型图标填充色", - "devui-shape-icon-fill":"面型svg图标填充色", - "devui-shape-icon-fill-hover":"面型svg图标悬停反馈色", - "devui-shape-icon-fill-active":"面型svg图标高亮填充色(激活状态)", - "devui-shape-icon-fill-disabled":"面型svg图标禁用色", + "devui-icon-fill-weak": "弱化型图标填充色", + "devui-shape-icon-fill": "面型svg图标填充色", + "devui-shape-icon-fill-hover": "面型svg图标悬停反馈色", + "devui-shape-icon-fill-active": "面型svg图标高亮填充色(激活状态)", + "devui-shape-icon-fill-disabled": "面型svg图标禁用色", "devui-icon-bg": " svg图标 背景色", "devui-icon-fill": " svg图标 灰色填充色", "devui-icon-fill-hover": "svg图标 灰色填充色悬停反馈色", "devui-icon-fill-active": "svg图标 高亮填充色(激活状态)", "devui-icon-fill-active-hover": "svg图标 高亮填充色悬停反馈色", - "devui-form-control-bg":"表单控件背景色", + "devui-form-control-bg": "表单控件背景色", "devui-form-control-line": "表单控件边框色,同 边框分割线", "devui-form-control-line-hover": "表单控件边框悬停反馈色", "devui-form-control-line-active": "表单控件边框激活色,用于获得焦点", @@ -1308,8 +1340,8 @@ "devui-primary-bg": "主要底色", "devui-default-line": "默认边框", "devui-default-bg": "默认底色", - "devui-connected-overlay-shadow":"下拉菜单类阴影", - "devui-feedback-overlay-shadow":"信息提示类阴影" + "devui-connected-overlay-shadow": "下拉菜单类阴影", + "devui-feedback-overlay-shadow": "信息提示类阴影" }, "catalogs": { "basic": "基本变量", @@ -1464,7 +1496,9 @@ "devui-shadow": "阴影色", "devui-light-shadow": "弱化阴影色", "devui-connected-overlay-shadow": "有连接点的弹出(覆盖)层阴影色", - "devui-feedback-overlay-shadow": "信息提示反馈类阴影色" + "devui-feedback-overlay-shadow": "信息提示反馈类阴影色", + "devui-hover-shadow": "悬浮阴影色", + "devui-float-block-shadow": "特殊浮层背景色" } } }, @@ -1743,6 +1777,10 @@ "title": "大数据量树形表格交互", "description": "大数据量树形表格的高级操作,拖拽、复选、批量删除、继承原本数据表格的大部分功能。" }, + "virtualScrollTreeTableMultiDragDemo": { + "title": "大数据树形表格批量拖拽", + "description": "按住ctrl批量选择行并进行批量拖拽" + }, "mutilStylesDemo": { "title": "表格样式" }, @@ -1783,7 +1821,8 @@ "muti-drag-row": "批量行拖拽", "virtual-scroll-tree-table-basic": "大数据量树形表格基本用法", "virtual-scroll-tree-table-operation": "大数据量树形表格操作功能", - "virtual-scroll-tree-table-interaction": "大数据量树形表格交互" + "virtual-scroll-tree-table-interaction": "大数据量树形表格交互", + "virtual-scroll-tree-table-multi-drag": "大数据量树形表格批量拖拽" } }, "datepickerPro": { @@ -2618,7 +2657,11 @@ "tmw": "当需要同时支持用户输入数据和选择已有数据的时候使用,加入输入联想功能,方便用户搜索已有数据。", "basicDemo": { "title": "基本用法", - "description": "通过 source 设置数据源。" + "description": "通过 source 设置数据源,使用 enableSelectedValueList 控制在已选值时是否显示全部选项。" + }, + "objectDemo": { + "title": "使用对象数组", + "description": "对象数组默认读取 label 属性,可使用 value Parser 和 formatter 方法自定义显示值和格式。" }, "disableDataDemo": { "title": "设置禁用选项", @@ -2642,6 +2685,7 @@ }, "anchorLinkValues": { "basic-usage": "基本用法", + "object-source": "使用对象数组", "disable-data-with-source": "设置禁用选项", "with-search-function": "自定义匹配方法", "async-data-with-function": "异步获取数据源并设置匹配方法", @@ -2949,7 +2993,7 @@ "tmw": "
1. 当操作需要较长的时间时,向用户展示操作进度。
2. 当操作需要打断现有界面或后台运行,需要较长时间时。
3. 当需要显示一个操作完成的百分比或已完成的步骤/总步骤时。
", "basicDemo": { "title": "线形进度条", - "description": "可配置在右端或随进度显示文字或自定义模板。" + "description": "可配置在中间、右端或随进度显示的文字和自定义模板。" }, "circleDemo": { "title": "环形进度条", @@ -3972,7 +4016,7 @@ "multiRowNotWebkitTitle": "使用计算方式处理多行文本", "multiRowExpandTitle": "设置展开收起显示多行文本" }, - "adaptiveInputDemo":{ + "adaptiveInputDemo": { "title": "自适应内容输入框", "description": "编辑时将单行input扩展为textarea,结束后恢复。使用户在使用单行input时更方便的输入和阅读大量文本内容。" }, diff --git a/src/index.html b/src/index.html index e1c10c80..98122483 100755 --- a/src/index.html +++ b/src/index.html @@ -7,7 +7,7 @@ diff --git a/src/styles.scss b/src/styles.scss index c5d1ebc4..23be4d93 100755 --- a/src/styles.scss +++ b/src/styles.scss @@ -44,8 +44,10 @@ section { .devui-api-table-wrapper { overflow-x: auto; + table { overflow-wrap: anywhere; + tr th:first-child { min-width: 136px; } diff --git a/tsconfig.json b/tsconfig.json index 1ff20d8f..0d6db736 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,7 @@ "outDir": "./dist/out-tsc", "sourceMap": true, "declaration": false, - "module": "es2020", + "module": "ES2022", "moduleResolution": "node", "allowSyntheticDefaultImports": true, "esModuleInterop": true, @@ -23,12 +23,12 @@ "noImplicitReturns": false, "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true, - "target": "es2020", + "target": "ES2022", "typeRoots": [ "node_modules/@types" ], "lib": [ - "es2020", + "ES2022", "dom" ], "paths": {