diff --git a/packages/theme/layout-blank/style/index.less b/packages/theme/layout-blank/style/index.less index 04b2e2408..81b7e08bb 100644 --- a/packages/theme/layout-blank/style/index.less +++ b/packages/theme/layout-blank/style/index.less @@ -1,3 +1,2 @@ -@import './theme-default.less'; @import './_layout.less'; @import './fix/index.less'; diff --git a/packages/theme/layout-blank/style/theme-default.less b/packages/theme/layout-blank/style/theme-default.less index 42b5179df..840a19bf5 100644 --- a/packages/theme/layout-blank/style/theme-default.less +++ b/packages/theme/layout-blank/style/theme-default.less @@ -1,6 +1,9 @@ -@yunzai-blank-prefix: ~'.yunzai-blank'; -@yunzai-blank-zindex: @zindex-base; +@yunzai-blank-name: 'yunzai-blank'; +@yunzai-blank-prefix: ~'.@{yunzai-blank-name}'; +@yunzai-blank-zindex: @zindex-base; @yunzai-blank-bg: #f5f7fa; +// @yunzai-blank-bg: ~'var(--@{yunzai-blank-name}-bg, #f5f7fa)'; + @yunzai-blank-content-padding-vertical: 0; @yunzai-blank-content-padding-horizontal: @layout-gutter * 2; diff --git a/packages/theme/layout-blank/style/theme-variable.less b/packages/theme/layout-blank/style/theme-variable.less new file mode 100644 index 000000000..f8a7cd7a3 --- /dev/null +++ b/packages/theme/layout-blank/style/theme-variable.less @@ -0,0 +1 @@ +@import './theme-default.less'; diff --git a/packages/theme/layout-default/layout-nav.component.spec.ts b/packages/theme/layout-default/layout-nav.component.spec.ts new file mode 100644 index 000000000..a2a3d03b8 --- /dev/null +++ b/packages/theme/layout-default/layout-nav.component.spec.ts @@ -0,0 +1,681 @@ +import { DOCUMENT } from '@angular/common'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { Component, DebugElement, ViewChild } from '@angular/core'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { Router, RouterModule, provideRouter } from '@angular/router'; + +import { ACLService } from '@yelon/acl'; +import { YunzaiThemeModule, MenuIcon, MenuService, SettingsService } from '@yelon/theme'; +import { deepCopy } from '@yelon/util/other'; +import { WINDOW } from '@yelon/util/token'; +import { NzSafeAny } from 'ng-zorro-antd/core/types'; + +import { LayoutDefaultNavComponent, Nav } from './layout-nav.component'; +import { LayoutDefaultModule } from './layout.module'; + +const floatingShowCls = '.sidebar-nav__floating-show'; +const MOCKMENUS = [ + { + text: '主导航', + group: true, + children: [ + { + text: '仪表盘', + children: [ + { text: 'v1', link: '/v1' }, + { text: 'v2', link: '#/v2', i18n: 'v2-i18n' }, + { text: 'v3' }, + { + text: 'externalLink-blank', + externalLink: '//ng.yunzainfo/blank', + target: '_blank' + }, + { + text: 'externalLink-top', + externalLink: '//ng.yunzainfo/top', + target: '_top' + } + ] + }, + { + text: 'widgets', + disabled: true + } + ] + } +] as Nav[]; + +const MOCKOPENSTRICTLY = [ + { + text: '', + group: true, + children: [ + { + text: '', + link: '/v1', + open: true, + children: [{ text: '' }] + }, + { + text: '', + link: '/v1', + open: true, + children: [{ text: '' }] + } + ] + } +] as Nav[]; + +class MockACLService { + can(val: string): boolean { + return val === 'admin'; + } +} + +class MockWindow { + location = new MockLocation(); + open(): void {} +} +class MockLocation { + private url!: string; + get href(): string { + return this.url; + } + set href(url: string) { + this.url = url; + } +} + +describe('theme: layout-default-nav', () => { + let fixture: ComponentFixture; + let dl: DebugElement; + let context: TestComponent; + let router: Router; + let setSrv: SettingsService; + let menuSrv: MenuService; + let page: PageObject; + let doc: Document; + + function createModule(): void { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule, RouterModule.forRoot([]), YunzaiThemeModule, LayoutDefaultModule], + declarations: [TestComponent], + providers: [ + provideHttpClient(), + provideHttpClientTesting(), + { provide: ACLService, useClass: MockACLService }, + { provide: WINDOW, useFactory: () => new MockWindow() } + ] + }); + } + + function createComp(needMockNavigateByUrl: boolean = true, callback?: () => void): void { + fixture = TestBed.createComponent(TestComponent); + dl = fixture.debugElement; + context = fixture.componentInstance; + fixture.detectChanges(); + router = TestBed.inject(Router); + setSrv = TestBed.inject(SettingsService); + menuSrv = TestBed.inject(MenuService); + doc = TestBed.inject(DOCUMENT); + menuSrv.add(deepCopy(MOCKMENUS)); + page = new PageObject(); + if (needMockNavigateByUrl) spyOn(router, 'navigateByUrl'); + if (callback) callback(); + } + + describe('', () => { + beforeEach(() => createModule()); + + describe('[default]', () => { + it('should be navigate url', () => { + createComp(); + spyOn(context, 'select'); + const data = deepCopy(MOCKMENUS); + menuSrv.add(data); + expect(context.select).not.toHaveBeenCalled(); + expect(router.navigateByUrl).not.toHaveBeenCalled(); + const itemEl = page.getEl('.sidebar-nav__depth1 a'); + itemEl!.click(); + fixture.detectChanges(); + expect(context.select).toHaveBeenCalled(); + expect(router.navigateByUrl).toHaveBeenCalled(); + }); + + describe('should be navigate external link', () => { + it('with target is _blank', () => { + createComp(); + const win = TestBed.inject(WINDOW); + spyOn(win, 'open'); + const itemEl = page.getEl('.sidebar-nav__item [data-id="6"]'); + itemEl!.click(); + expect(win.open).toHaveBeenCalled(); + }); + it('with target is _top', () => { + createComp(); + const win = TestBed.inject(WINDOW); + const itemEl = page.getEl('.sidebar-nav__item [data-id="7"]'); + itemEl!.click(); + expect(win.location.href).toBe(`//ng.yunzainfo/top`); + }); + }); + + it('should be hide group name', () => { + createComp(); + page.checkCount('.sidebar-nav__group-title'); + const data = deepCopy(MOCKMENUS) as Nav[]; + data[0].group = false; + menuSrv.add(data); + fixture.detectChanges(); + page.checkCount('.sidebar-nav__group-title', 0); + }); + + it('should be toggle open', () => { + createComp(); + const data = deepCopy(MOCKMENUS); + menuSrv.add(data); + expect(data[0].children![0].open).toBe(false); + const subTitleEl = page.getEl('.sidebar-nav__item-link'); + subTitleEl!.click(); + fixture.detectChanges(); + expect(data[0].children![0].open).toBe(true); + }); + + it('should be reset menu when service is changed', () => { + createComp(); + page.checkText('.sidebar-nav__group-title', MOCKMENUS[0].text); + const newMenu = deepCopy(MOCKMENUS); + newMenu[0].text = 'new主导航'; + menuSrv.add(newMenu); + fixture.detectChanges(); + page.checkText('.sidebar-nav__group-title', newMenu[0].text); + }); + + it('should be block click menu when is disabled', () => { + createComp(); + spyOn(context, 'select'); + const newMenus = [ + { + text: '', + children: [{ text: 'new menu', disabled: true }] + } + ]; + menuSrv.add(newMenus); + expect(context.select).not.toHaveBeenCalled(); + const itemEl = page.getEl('.sidebar-nav__item-disabled'); + itemEl!.click(); + fixture.detectChanges(); + expect(context.select).toHaveBeenCalled(); + }); + + it('should be support html in text or i18n', () => { + createComp(); + menuSrv.add([{ text: 'text 1' }]); + page.checkText('.sidebar-nav__item', `text 1`); + menuSrv.add([{ i18n: 'i18n 1' }]); + page.checkText('.sidebar-nav__item', `i18n 1`); + }); + + describe('#hideEmptyChildren', () => { + it('with true', () => { + createComp(); + menuSrv.add([ + { + text: 'parent', + children: [ + { text: 'l1', hide: true }, + { text: 'l2', hide: false } + ] + } + ]); + page.checkCount('.sidebar-nav__group-title', 1); + menuSrv.add([ + { + text: 'parent', + children: [ + { text: 'l1', hide: true }, + { text: 'l2', hide: true } + ] + } + ]); + page.checkCount('.sidebar-nav__group-title', 0); + }); + it('with false', () => { + createComp(); + context.hideEmptyChildren = false; + fixture.detectChanges(); + menuSrv.add([ + { + text: 'parent', + children: [ + { text: 'l1', hide: true }, + { text: 'l2', hide: false } + ] + } + ]); + page.checkCount('.sidebar-nav__group-title', 1); + menuSrv.add([ + { + text: 'parent', + children: [ + { text: 'l1', hide: true }, + { text: 'l2', hide: true } + ] + } + ]); + page.checkCount('.sidebar-nav__group-title', 1); + }); + }); + }); + + describe('#icon', () => { + function updateIcon(icon: string | MenuIcon): void { + createComp(); + + menuSrv.add([ + { + text: '', + group: true, + children: [ + { + text: '', + icon + } + ] + } + ] as Nav[]); + + fixture.detectChanges(); + } + describe('with icon', () => { + it('when is string and includes [anticon-]', () => { + updateIcon('anticon-edit'); + const el = page.getEl('.sidebar-nav__item-icon') as HTMLElement; + expect(el.classList).toContain('anticon-edit'); + }); + it('when is string and http prefix', () => { + updateIcon('http://ng.yunzainfo/1.jpg'); + page.checkCount('.sidebar-nav__item-img', 1); + }); + it('when is class string', () => { + updateIcon('demo-class'); + page.checkCount('.demo-class', 1); + }); + }); + it('with className', () => { + updateIcon({ type: 'class', value: 'demo-class' }); + page.checkCount('.demo-class', 1); + }); + it('with img', () => { + updateIcon({ type: 'img', value: '1.jpg' }); + page.checkCount('.sidebar-nav__item-img', 1); + }); + it('with svg', () => { + updateIcon({ type: 'svg', value: '' }); + page.checkCount('.sidebar-nav__item-svg', 1); + }); + }); + + describe('[collapsed]', () => { + describe('#default', () => { + beforeEach(() => { + createComp(); + setSrv.layout.collapsed = true; + fixture.detectChanges(); + }); + it(`should be won't show sub-menu when not collapse`, () => { + setSrv.layout.collapsed = false; + fixture.detectChanges(); + page.showSubMenu(false); + }); + it('should be show sub-menu', () => { + page.showSubMenu(); + }); + it('should be displayed full submenu', () => { + const clientHeight = spyOnProperty(doc.documentElement, 'clientHeight').and.returnValue(0); + spyOnProperty(doc.querySelector('body')!, 'clientHeight').and.returnValue(0); + expect(clientHeight).not.toHaveBeenCalled(); + page.showSubMenu(); + expect(clientHeight).toHaveBeenCalled(); + }); + it('should be working when include badge', () => { + const mockMenu = deepCopy(MOCKMENUS) as Nav[]; + mockMenu[0].children![0].badge = 1; + menuSrv.add(mockMenu); + fixture.detectChanges(); + expect(page.getEl('.ant-badge') != null).toBe(true); + page.showSubMenu(); + expect(page.getEl('.sidebar-nav__floating-container .sidebar-nav__item', true) != null).toBe(true); + }); + it('should be ingore children title trigger event', () => { + spyOn(context, 'select'); + expect(context.select).not.toHaveBeenCalled(); + const mockMenu = deepCopy(MOCKMENUS) as Nav[]; + mockMenu[0].children![0].children = [{ text: 'a', children: [{ text: 'b' }] }]; + menuSrv.add(mockMenu); + fixture.detectChanges(); + page.showSubMenu(); + const containerEl = page.getEl(floatingShowCls, true)!; + (containerEl.querySelector('.sidebar-nav__item-link') as HTMLElement).click(); + expect(context.select).not.toHaveBeenCalled(); + }); + }); + describe('should be hide sub-menu in floating container', () => { + it('muse be hide via click menu link', () => { + createComp(); + setSrv.layout.collapsed = true; + fixture.detectChanges(); + page.showSubMenu(); + page.hideSubMenu(); + expect(router.navigateByUrl).toHaveBeenCalled(); + }); + it('muse be hide via mouse leave area', () => { + createComp(); + setSrv.layout.collapsed = true; + fixture.detectChanges(); + page.showSubMenu(); + page.getEl(floatingShowCls, true)!.dispatchEvent(new Event('mouseleave')); + fixture.detectChanges(); + expect(page.getEl(floatingShowCls, true)).toBeNull(); + }); + it('muse be not hide via click except menu link area', () => { + createComp(); + setSrv.layout.collapsed = true; + fixture.detectChanges(); + page.showSubMenu(); + const containerEl = page.getEl(floatingShowCls, true); + containerEl!.querySelectorAll('li')[1].click(); + fixture.detectChanges(); + expect(router.navigateByUrl).not.toHaveBeenCalled(); + }); + it('muse be hide via click span of menu item', () => { + createComp(); + setSrv.layout.collapsed = true; + fixture.detectChanges(); + page.showSubMenu(); + const containerEl = page.getEl(floatingShowCls, true); + containerEl!.querySelectorAll('span')[1].click(); + fixture.detectChanges(); + expect(router.navigateByUrl).toHaveBeenCalled(); + }); + it('muse be hide via document click', () => { + createComp(); + setSrv.layout.collapsed = true; + fixture.detectChanges(); + page.showSubMenu(); + document.dispatchEvent(new MouseEvent('click')); + fixture.detectChanges(); + expect(router.navigateByUrl).not.toHaveBeenCalled(); + }); + it('muse be hide when move to other item', () => { + createComp(); + setSrv.layout.collapsed = true; + fixture.detectChanges(); + page.showSubMenu(); + expect(page.isShowSubMenu()).toBe(true); + const widgetEl = page.getEl('.sidebar-nav__item-disabled', true); + widgetEl!.dispatchEvent(new Event('mouseenter')); + fixture.detectChanges(); + expect(page.isShowSubMenu()).toBe(false); + }); + }); + it('#52', () => { + createComp(); + setSrv.layout.collapsed = true; + fixture.detectChanges(); + page.showSubMenu(); + spyOn(context.comp['floatingEl'], 'remove'); + page.hideSubMenu(); + expect(page.getEl(floatingShowCls, true)).toBeNull(); + }); + }); + + describe('#disabledAcl', () => { + const newMenus = [ + { + text: '', + children: [ + { text: 'new menu', acl: 'admin' }, + { text: 'new menu', acl: 'user' } + ] + } + ]; + beforeEach(() => createComp()); + it('should be disabled item when with true', () => { + context.disabledAcl = true; + fixture.detectChanges(); + menuSrv.add(newMenus); + const itemEl = page.getEl('.sidebar-nav__item [data-id="3"]'); + expect(itemEl!.classList).toContain('sidebar-nav__item-disabled'); + }); + it('should be hidden item when with false', () => { + context.disabledAcl = false; + fixture.detectChanges(); + menuSrv.add(newMenus); + const itemEl = page.getEl('.sidebar-nav__item [data-id="3"]'); + expect(itemEl == null).toBe(true); + }); + }); + + describe('#openStrictly', () => { + beforeEach(() => { + createComp(); + context.openStrictly = true; + fixture.detectChanges(); + menuSrv.add(deepCopy(MOCKOPENSTRICTLY)); + fixture.detectChanges(); + }); + it('should working', () => { + page.checkCount('.sidebar-nav__open', 2); + }); + it(`should be won't close other item`, () => { + const list = dl.queryAll(By.css('.sidebar-nav__item-link')); + expect(list.length).toBe(4); + (list[0].nativeElement as HTMLElement).click(); + fixture.detectChanges(); + page.checkCount('.sidebar-nav__open', 1); + (list[2].nativeElement as HTMLElement).click(); + fixture.detectChanges(); + page.checkCount('.sidebar-nav__open', 0); + }); + }); + }); + + describe('[underPad]', () => { + beforeEach(createModule); + + it('should be auto collapsed when less than pad', fakeAsync(() => { + // create test component + TestBed.overrideTemplate( + TestComponent, + `` + ); + const defaultCollapsed = false; + createComp(false, () => { + spyOnProperty(window, 'innerWidth').and.returnValue(767); + setSrv.layout.collapsed = defaultCollapsed; + fixture.detectChanges(); + }); + router.navigateByUrl('/'); + fixture.detectChanges(); + tick(20); + expect(setSrv.layout.collapsed).toBe(!defaultCollapsed); + })); + it(`should be won't collapsed when more than pad`, fakeAsync(() => { + // create test component + TestBed.overrideTemplate( + TestComponent, + `` + ); + const defaultCollapsed = false; + createComp(false, () => { + spyOnProperty(window, 'innerWidth').and.returnValue(769); + setSrv.layout.collapsed = defaultCollapsed; + fixture.detectChanges(); + }); + router.navigateByUrl('/'); + fixture.detectChanges(); + tick(1000); + expect(setSrv.layout.collapsed).toBe(defaultCollapsed); + })); + it('should be auto expaned when less than pad trigger click', fakeAsync(() => { + // create test component + TestBed.overrideTemplate( + TestComponent, + `` + ); + createComp(); + setSrv.layout.collapsed = true; + fixture.detectChanges(); + spyOnProperty(window, 'innerWidth').and.returnValue(767); + expect(setSrv.layout.collapsed).toBe(true); + page.getEl('.sidebar-nav')!.click(); + fixture.detectChanges(); + tick(20); + expect(setSrv.layout.collapsed).toBe(false); + })); + }); + + describe('should be recursive path', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterModule.forRoot([]), YunzaiThemeModule, LayoutDefaultModule], + providers: [ + provideRouter([ + { path: 'user', component: TestRouteComponent }, + { path: 'user2', component: TestRouteComponent }, + { path: 'user/type', component: TestRouteComponent } + ]) + ], + declarations: [TestComponent, TestRouteComponent] + }); + }); + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(TestComponent); + dl = fixture.debugElement; + context = fixture.componentInstance; + menuSrv = TestBed.inject(MenuService); + fixture.detectChanges(); + createComp(false); + menuSrv.add([ + { + text: '主导航', + group: true, + children: [ + { text: 'user1', link: '/user' }, + { text: 'user2', link: '/user' } + ] + } + ]); + })); + it('with true', fakeAsync(() => { + context.recursivePath = true; + fixture.detectChanges(); + router.navigateByUrl('/user2'); + tick(); + fixture.detectChanges(); + page.checkCount('.sidebar-nav__selected', 0); + router.navigateByUrl('/user/type'); + tick(); + fixture.detectChanges(); + page.checkCount('.sidebar-nav__selected', 1); + })); + it('with false', fakeAsync(() => { + context.recursivePath = false; + fixture.detectChanges(); + router.navigateByUrl('/user2'); + tick(); + fixture.detectChanges(); + page.checkCount('.sidebar-nav__selected', 0); + router.navigateByUrl('/user/type'); + tick(); + fixture.detectChanges(); + page.checkCount('.sidebar-nav__selected', 0); + })); + it('should be ingore _open when enabled openStrictly', fakeAsync(() => { + context.openStrictly = true; + fixture.detectChanges(); + menuSrv.add(deepCopy(MOCKOPENSTRICTLY)); + page.checkCount('.sidebar-nav__open', 2); + router.navigateByUrl('/user2'); + fixture.detectChanges(); + page.checkCount('.sidebar-nav__open', 2); + })); + }); + + class PageObject { + getEl(cls: string, body: boolean = false): T | null { + const el = body + ? document.querySelector(cls) + : dl.query(By.css(cls)) + ? dl.query(By.css(cls)).nativeElement + : null; + return el ? (el as T) : null; + } + checkText(cls: string, value: NzSafeAny): void { + const el = this.getEl(cls); + expect(el ? el.innerText.trim() : '').toBe(value); + } + checkCount(cls: string, count: number = 1): this { + expect(dl.queryAll(By.css(cls)).length).toBe(count); + return this; + } + /** 期望显示子菜单,默认:`true` */ + showSubMenu(resultExpectShow: boolean = true): void { + let conEl = this.getEl(floatingShowCls, true); + expect(conEl).toBeNull(); + const subTitleEl = this.getEl('.sidebar-nav__item-link'); + subTitleEl!.dispatchEvent(new Event('mouseenter')); + fixture.detectChanges(); + conEl = this.getEl(floatingShowCls, true); + if (resultExpectShow) { + expect(conEl).not.toBeNull(); + } else { + expect(conEl).toBeNull(); + } + } + /** 期望隐藏子菜单,默认:`true` */ + hideSubMenu(resultExpectHide: boolean = true): void { + const containerEl = this.getEl(floatingShowCls, true); + expect(containerEl).not.toBeNull(); + containerEl!.querySelector(resultExpectHide ? 'a' : 'li')!.click(); + fixture.detectChanges(); + const conEl = this.getEl(floatingShowCls, true); + if (resultExpectHide) expect(conEl).toBeNull(); + else expect(conEl).not.toBeNull(); + } + isShowSubMenu(): boolean { + return page.getEl(floatingShowCls, true) != null; + } + } +}); + +@Component({ + template: ` + + ` +}) +class TestComponent { + @ViewChild('comp', { static: true }) + comp!: LayoutDefaultNavComponent; + disabledAcl = false; + autoCloseUnderPad = false; + recursivePath = false; + hideEmptyChildren = true; + openStrictly = false; + select(): void {} +} + +@Component({ template: `` }) +class TestRouteComponent {} diff --git a/packages/theme/layout-default/layout-nav.component.ts b/packages/theme/layout-default/layout-nav.component.ts index 9dea3f6ce..2ca9e386a 100644 --- a/packages/theme/layout-default/layout-nav.component.ts +++ b/packages/theme/layout-default/layout-nav.component.ts @@ -56,7 +56,7 @@ export class LayoutDefaultNavComponent implements OnInit, OnDestroy { private readonly cdr = inject(ChangeDetectorRef); private readonly ngZone = inject(NgZone); private readonly sanitizer = inject(DomSanitizer); - private readonly directionality = inject(Directionality, { optional: true }); + private readonly directionality = inject(Directionality); private bodyEl!: HTMLBodyElement; private destroy$ = inject(DestroyRef); @@ -91,7 +91,7 @@ export class LayoutDefaultNavComponent implements OnInit, OnDestroy { return false; } const id = +linkNode.dataset!.id!; - // Should be ignore children title trigger event + // Should be ingore children title trigger event if (isNaN(id)) { return false; } @@ -111,7 +111,6 @@ export class LayoutDefaultNavComponent implements OnInit, OnDestroy { private clearFloating(): void { if (!this.floatingEl) return; this.floatingEl.removeEventListener('click', this.floatingClickHandle.bind(this)); - // fix ie: https://github.com/hbyunzai/yelon/issues/52 if (this.floatingEl.hasOwnProperty('remove')) { this.floatingEl.remove(); } else if (this.floatingEl.parentNode) { @@ -192,12 +191,7 @@ export class LayoutDefaultNavComponent implements OnInit, OnDestroy { if (item.target === '_blank') { this.win.open(item.externalLink); } else { - // this.win.location.href = item.externalLink; - - // 浏览器缓存iframe路径 - localStorage.setItem('iframeSrc', item.externalLink); - this.menuSrv.setRouterLink(item.externalLink); - this.router.navigate(['iframePage']); + this.win.location.href = item.externalLink; } return; } @@ -264,8 +258,8 @@ export class LayoutDefaultNavComponent implements OnInit, OnDestroy { .subscribe(() => this.clearFloating()); this.underPad(); - this.dir = this.directionality?.value; - this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { + this.dir = this.directionality.value; + this.directionality.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; this.cdr.detectChanges(); }); diff --git a/packages/theme/layout-default/layout.component.spec.ts b/packages/theme/layout-default/layout.component.spec.ts index 70a3138ff..8dc03f0c6 100644 --- a/packages/theme/layout-default/layout.component.spec.ts +++ b/packages/theme/layout-default/layout.component.spec.ts @@ -1,19 +1,24 @@ import { Component, DebugElement, TemplateRef, ViewChild } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { NavigationCancel, NavigationError, RouteConfigLoadEnd, RouteConfigLoadStart } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; +import { + NavigationCancel, + NavigationError, + RouteConfigLoadEnd, + RouteConfigLoadStart, + RouterModule +} from '@angular/router'; import { createTestContext } from '@yelon/testing'; import { NzIconTestModule } from 'ng-zorro-antd/icon/testing'; import { NzMessageService } from 'ng-zorro-antd/message'; +import { SettingsService } from '../src/services/settings/settings.service'; +import { YunzaiThemeModule } from '../src/theme.module'; import { LayoutDefaultComponent } from './layout.component'; import { LayoutDefaultModule } from './layout.module'; import { LayoutDefaultService } from './layout.service'; import { LayoutDefaultOptions } from './types'; -import { SettingsService } from '../src/services/settings/settings.service'; -import { YunzaiThemeModule } from '../src/theme.module'; describe('theme: layout-default', () => { let fixture: ComponentFixture; @@ -23,7 +28,7 @@ describe('theme: layout-default', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [LayoutDefaultModule, RouterTestingModule, NzIconTestModule, YunzaiThemeModule], + imports: [LayoutDefaultModule, RouterModule.forRoot([]), NzIconTestModule, YunzaiThemeModule], declarations: [TestComponent] }); diff --git a/packages/theme/layout-default/style/_layout.less b/packages/theme/layout-default/style/_layout.less index 2893643ac..f85c918e7 100644 --- a/packages/theme/layout-default/style/_layout.less +++ b/packages/theme/layout-default/style/_layout.less @@ -35,7 +35,7 @@ body { } &__content { - margin: 0 @yunzai-default-content-padding @yunzai-default-content-padding @yunzai-default-content-padding; + margin: 0 @yunzai-default-content-padding @yunzai-default-content-padding; .router-ant(); &-title { @@ -52,18 +52,19 @@ body { background-color: @yunzai-default-content-heading-bg; border-bottom: 1px solid @yunzai-default-content-heading-border; - > h1 { + >h1 { margin-bottom: 0; font-size: 18px; font-weight: normal; - > small { + >small { display: block; font-size: 12px; color: @muted-color; } } } + // fix width nz-input-group { width: auto; @@ -89,6 +90,7 @@ body { @{yunzai-default-prefix}__content { margin-left: (@yunzai-default-aside-wd + @yunzai-default-content-padding); } + @{yunzai-default-prefix}__collapsed { @{yunzai-default-prefix} { &__sidebar { @@ -109,6 +111,7 @@ body { margin-right: (@yunzai-default-aside-wd + @yunzai-default-content-padding); margin-left: 0; } + @{yunzai-default-prefix}__collapsed { @{yunzai-default-prefix} { &__content { @@ -120,4 +123,5 @@ body { } } } + .layout-default-rtl-mixin(@rtl-enabled); diff --git a/packages/theme/layout-default/style/fix/_reuse-tab.less b/packages/theme/layout-default/style/fix/_reuse-tab.less index 26a733e38..21da03602 100644 --- a/packages/theme/layout-default/style/fix/_reuse-tab.less +++ b/packages/theme/layout-default/style/fix/_reuse-tab.less @@ -2,7 +2,7 @@ @yunzai-default-aside-collapsed-width: @yunzai-default-aside-collapsed-wd + @yunzai-default-content-padding; @{reuse-tab-prefix} { - margin: 0 -@yunzai-default-content-padding 0 -@yunzai-default-content-padding; + margin: 0 -@yunzai-default-content-padding; } @{yunzai-default-prefix}__fixed { @@ -36,7 +36,7 @@ @media (min-width: @mobile-min) { @{yunzai-default-prefix}__fixed { @{reuse-tab-prefix} { - + router-outlet { + +router-outlet { display: block; height: @reuse-tab-height; } @@ -71,4 +71,5 @@ } } } + .layout-default-reuse-tab-rtl-mixin(@rtl-enabled); diff --git a/packages/theme/layout-default/style/index.less b/packages/theme/layout-default/style/index.less index bb6d629a9..2146f0b7c 100644 --- a/packages/theme/layout-default/style/index.less +++ b/packages/theme/layout-default/style/index.less @@ -1,4 +1,3 @@ -@import './theme-default.less'; // Layout @import './_layout.less'; @import './_header.less'; diff --git a/packages/theme/layout-default/style/theme-dark.less b/packages/theme/layout-default/style/theme-dark.less index faed691c7..2b7c471e1 100644 --- a/packages/theme/layout-default/style/theme-dark.less +++ b/packages/theme/layout-default/style/theme-dark.less @@ -7,4 +7,4 @@ @yunzai-default-aside-nav-selected-bg: @component-background; @yunzai-default-content-heading-bg: @component-background; @yunzai-default-content-heading-border: @border-color-split; -@yunzai-default-content-bg: @body-background; +@yunzai-default-content-bg: ~'var(--@{yunzai-default-name}-content-bg, @{body-background})'; diff --git a/packages/theme/layout-default/style/theme-default.less b/packages/theme/layout-default/style/theme-default.less index aacbaf915..d7b64491a 100644 --- a/packages/theme/layout-default/style/theme-default.less +++ b/packages/theme/layout-default/style/theme-default.less @@ -1,7 +1,8 @@ -@yunzai-default-prefix: ~'.yunzai-default'; +@yunzai-default-name: 'yunzai-default'; +@yunzai-default-prefix: ~'.@{yunzai-default-name}'; @yunzai-default-zindex: @zindex-base; @yunzai-default-ease: cubic-bezier(0.25, 0, 0.15, 1); -@yunzai-default-header-hg: 64px; +@yunzai-default-header-hg: ~'var(--@{yunzai-default-name}-header-hg, 64px)'; @yunzai-default-header-bg: @primary-color; @yunzai-default-header-padding: @layout-gutter * 2; @yunzai-default-header-search-enabled: true; @@ -13,7 +14,7 @@ @yunzai-default-header-top-menu-item-padding: 0 16px; @yunzai-default-aside-wd: 200px; -@yunzai-default-aside-bg: #fff; +@yunzai-default-aside-bg: ~'var(--@{yunzai-default-name}-aside-bg, #fff)'; @yunzai-default-aside-scrollbar-width: 0; @yunzai-default-aside-scrollbar-height: 0; @yunzai-default-aside-scrollbar-track-color: transparent; @@ -28,7 +29,7 @@ @yunzai-default-aside-nav-text-hover-color: @primary-color; @yunzai-default-aside-nav-group-text-color: @text-color-secondary; @yunzai-default-aside-nav-selected-text-color: @primary-color; -@yunzai-default-aside-nav-selected-bg: #fcfcfc; +@yunzai-default-aside-nav-selected-bg: ~'var(--@{yunzai-default-name}-aside-nav-selected-bg, #fcfcfc)'; @yunzai-default-aside-nav-depth1-padding-left: @layout-gutter * 3; @yunzai-default-aside-nav-depth2-padding-left: @layout-gutter * 4; @yunzai-default-aside-nav-depth3-padding-left: @layout-gutter * 5; @@ -40,10 +41,10 @@ @yunzai-default-aside-collapsed-nav-img-wh: 24px; @yunzai-default-aside-collapsed-padding: (@layout-gutter * 2) 0; -@yunzai-default-content-heading-bg: #fafbfc; -@yunzai-default-content-heading-border: #efe3e5; +@yunzai-default-content-heading-bg: ~'var(--@{yunzai-default-name}-heading-bg, #fafbfc)'; +@yunzai-default-content-heading-border: ~'var(--@{yunzai-default-name}-heading-border, #efe3e5)'; @yunzai-default-content-padding: @layout-gutter * 3; -@yunzai-default-content-bg: #f5f7fa; +@yunzai-default-content-bg: ~'var(--@{yunzai-default-name}-content-bg, #f5f7fa)'; @yunzai-default-widget-app-icons-enabled: true; @yunzai-default-aside-user-enabled: true; diff --git a/packages/theme/layout-default/style/theme-variable.less b/packages/theme/layout-default/style/theme-variable.less new file mode 100644 index 000000000..f8a7cd7a3 --- /dev/null +++ b/packages/theme/layout-default/style/theme-variable.less @@ -0,0 +1 @@ +@import './theme-default.less'; diff --git a/packages/theme/setting-drawer/setting-drawer.component.ts b/packages/theme/setting-drawer/setting-drawer.component.ts index cf876356c..57fe0dcd4 100644 --- a/packages/theme/setting-drawer/setting-drawer.component.ts +++ b/packages/theme/setting-drawer/setting-drawer.component.ts @@ -39,7 +39,7 @@ export class SettingDrawerComponent implements OnInit { private readonly lazy = inject(LazyService); private readonly ngZone = inject(NgZone); private readonly doc = inject(DOCUMENT); - private readonly directionality = inject(Directionality, { optional: true }); + private readonly directionality = inject(Directionality); private readonly destroy$ = inject(DestroyRef); @Input({ transform: booleanAttribute }) autoApplyColor = true; @@ -72,8 +72,8 @@ export class SettingDrawerComponent implements OnInit { } ngOnInit(): void { - this.dir = this.directionality?.value; - this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { + this.dir = this.directionality.value; + this.directionality.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; this.cdr.detectChanges(); }); diff --git a/packages/theme/src/services/http/http.client.ts b/packages/theme/src/services/http/http.client.ts index 2a60d7d6d..386854727 100644 --- a/packages/theme/src/services/http/http.client.ts +++ b/packages/theme/src/services/http/http.client.ts @@ -297,7 +297,7 @@ export class _HttpClient { params?: any, options?: { headers?: _HttpHeaders; - observe: 'response'; + observe: 'body' | 'response'; reportProgress?: boolean; responseType?: 'json'; withCredentials?: boolean; @@ -427,7 +427,6 @@ export class _HttpClient { */ jsonp(url: string, params?: any, callbackParam: string = 'JSONP_CALLBACK'): Observable { return of(null).pipe( - // Make sure to always be asynchronous, see issues: https://github.com/hbyunzai/ng-yunzai/issues/1954 delay(0), tap(() => this.push()), switchMap(() => this.http.jsonp(this.appliedUrl(url, params), callbackParam)), @@ -1039,7 +1038,6 @@ export class _HttpClient { ): Observable { if (options.params) options.params = this.parseParams(options.params); return of(null).pipe( - // Make sure to always be asynchronous, see issues: https://github.com/hbyunzai/ng-yunzai/issues/1954 delay(0), tap(() => this.push()), switchMap(() => this.http.request(method, url, options)), diff --git a/packages/theme/src/services/http/http.spec.ts b/packages/theme/src/services/http/http.spec.ts index 4d735a9af..ac60095a3 100644 --- a/packages/theme/src/services/http/http.spec.ts +++ b/packages/theme/src/services/http/http.spec.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { HttpParams, HttpResponse } from '@angular/common/http'; -import { HttpClientTestingModule, HttpTestingController, TestRequest } from '@angular/common/http/testing'; +import { HttpParams, HttpResponse, provideHttpClient } from '@angular/common/http'; +import { HttpTestingController, TestRequest, provideHttpClientTesting } from '@angular/common/http/testing'; import { Type } from '@angular/core'; import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { of, catchError } from 'rxjs'; @@ -22,12 +22,11 @@ describe('theme: http.client', () => { const BODY = 'body data'; function createModule(config?: YunzaiThemeHttpClientConfig): void { - const providers: any[] = [_HttpClient]; + const providers: any[] = [provideHttpClient(), provideHttpClientTesting(), _HttpClient]; if (config) { providers.push(provideYunzaiConfig({ themeHttp: config })); } TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], providers }); @@ -101,7 +100,7 @@ describe('theme: http.client', () => { ret.flush(OK); expect(res).toBe(OK); })); - it('should be ignore process when params is HttpParams', fakeAsync(() => { + it('should be ingore process when params is HttpParams', fakeAsync(() => { http.get(URL, new HttpParams({ fromObject: { a: 'aa' } })).subscribe(_ => (res = _)); tick(); const ret = backend.expectOne(() => true) as TestRequest; @@ -696,7 +695,7 @@ describe('theme: http.client', () => { const ret = backend.expectOne(() => true) as TestRequest; expect(ret.request.urlWithParams).toContain(`${Math.trunc(+now / 1000)}`); })); - it('should be ignore null values', fakeAsync(() => { + it('should be ingore null values', fakeAsync(() => { createModule({ nullValueHandling: 'ignore' }); http.get(URL, { a: 1, b: null, c: undefined }).subscribe(); tick(); diff --git a/packages/theme/src/services/i18n/i18n.spec.ts b/packages/theme/src/services/i18n/i18n.spec.ts index 3742acdaa..0da405313 100644 --- a/packages/theme/src/services/i18n/i18n.spec.ts +++ b/packages/theme/src/services/i18n/i18n.spec.ts @@ -1,14 +1,13 @@ import { Component } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { Router } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; +import { Router, RouterModule } from '@angular/router'; import { provideYunzaiConfig } from '@yelon/util/config'; +import { YunzaiThemeModule } from '../../theme.module'; import { YunzaiI18NService, YUNZAI_I18N_TOKEN } from './i18n'; import { yunzaiI18nCanActivate, yunzaiI18nCanActivateChild } from './i18n-url.guard'; -import { YunzaiThemeModule } from '../../theme.module'; describe('theme: i18n', () => { let fixture: ComponentFixture; @@ -110,7 +109,7 @@ describe('theme: i18n', () => { TestBed.configureTestingModule({ imports: [ YunzaiThemeModule, - RouterTestingModule.withRoutes([ + RouterModule.forRoot([ { path: ':i18n', component: TestComponent, @@ -134,9 +133,7 @@ describe('theme: i18n', () => { TestBed.configureTestingModule({ imports: [ YunzaiThemeModule, - RouterTestingModule.withRoutes([ - { path: ':invalid', component: TestComponent, canActivate: [yunzaiI18nCanActivate] } - ]) + RouterModule.forRoot([{ path: ':invalid', component: TestComponent, canActivate: [yunzaiI18nCanActivate] }]) ], declarations: [TestComponent] }); @@ -153,9 +150,7 @@ describe('theme: i18n', () => { TestBed.configureTestingModule({ imports: [ YunzaiThemeModule, - RouterTestingModule.withRoutes([ - { path: ':lang', component: TestComponent, canActivate: [yunzaiI18nCanActivate] } - ]) + RouterModule.forRoot([{ path: ':lang', component: TestComponent, canActivate: [yunzaiI18nCanActivate] }]) ], declarations: [TestComponent], providers: [provideYunzaiConfig({ themeI18n: { paramNameOfUrlGuard: 'lang' } })] diff --git a/packages/theme/src/services/menu/menu.service.spec.ts b/packages/theme/src/services/menu/menu.service.spec.ts index a2ec17eeb..a8327ba21 100644 --- a/packages/theme/src/services/menu/menu.service.spec.ts +++ b/packages/theme/src/services/menu/menu.service.spec.ts @@ -5,9 +5,9 @@ import { ACLService } from '@yelon/acl'; import { deepCopy } from '@yelon/util/other'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { YunzaiI18NServiceFake, YUNZAI_I18N_TOKEN } from '../i18n/i18n'; import { Menu, MenuInner } from './interface'; import { MenuService } from './menu.service'; -import { YunzaiI18NServiceFake, YUNZAI_I18N_TOKEN } from '../i18n/i18n'; class MockACLService { can(val: string): boolean { @@ -222,7 +222,7 @@ describe('Service: Menu', () => { srv.setItem(newMenus[0], { text: 'obj' }); expect(srv.getItem('a')!.text).toBe('obj'); }); - it('should be ignore update when not found key', () => { + it('should be ingore update when not found key', () => { const newMenus = [{ text: 'a', key: 'a' }]; srv.add(newMenus); srv.setItem('invalid-key', { text: 'b' }); diff --git a/packages/theme/src/services/menu/menu.service.ts b/packages/theme/src/services/menu/menu.service.ts index ae9637898..cc4e1e132 100644 --- a/packages/theme/src/services/menu/menu.service.ts +++ b/packages/theme/src/services/menu/menu.service.ts @@ -4,16 +4,16 @@ import { BehaviorSubject, Observable, Subscription, share } from 'rxjs'; import { ACLService } from '@yelon/acl'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; -import { Menu, MenuIcon, MenuInner } from './interface'; import { YUNZAI_I18N_TOKEN } from '../i18n/i18n'; +import { Menu, MenuIcon, MenuInner } from './interface'; /** - * 菜单服务,[在线文档](https://ng.yunzainfo.com/theme/menu) + * 菜单服务 */ @Injectable({ providedIn: 'root' }) export class MenuService implements OnDestroy { - private readonly i18nSrv = inject(YUNZAI_I18N_TOKEN, { optional: true }); - private readonly aclService = inject(ACLService, { optional: true }); + private readonly i18nSrv = inject(YUNZAI_I18N_TOKEN); + private readonly aclService = inject(ACLService); private _change$: BehaviorSubject = new BehaviorSubject([]); private i18n$?: Subscription; private data: Menu[] = []; @@ -22,10 +22,8 @@ export class MenuService implements OnDestroy { */ openStrictly = false; - private $routerLink: BehaviorSubject = new BehaviorSubject(''); - constructor() { - this.i18n$ = this.i18nSrv?.change.subscribe(() => this.resume()); + this.i18n$ = this.i18nSrv.change.subscribe(() => this.resume()); } get change(): Observable { @@ -95,7 +93,7 @@ export class MenuService implements OnDestroy { item.icon = { theme: 'outline', spin: false, ...(item.icon as MenuIcon) }; } - item.text = item.i18n && this.i18nSrv ? this.i18nSrv.fanyi(item.i18n) : item.text; + item.text = item.i18n ? this.i18nSrv.fanyi(item.i18n) : item.text; // group item.group = item.group !== false; @@ -107,7 +105,7 @@ export class MenuService implements OnDestroy { item.disabled = typeof item.disabled === 'undefined' ? false : item.disabled; // acl - item._aclResult = item.acl && this.aclService ? this.aclService.can(item.acl) : true; + item._aclResult = item.acl ? this.aclService.can(item.acl) : true; item.open = item.open != null ? item.open : false; } @@ -164,7 +162,7 @@ export class MenuService implements OnDestroy { this.data[0].children!.splice(pos, 0, shortcutMenu); } let _data = this.data[0].children![pos]; - if (_data.i18n && this.i18nSrv) _data.text = this.i18nSrv.fanyi(_data.i18n); + if (_data.i18n) _data.text = this.i18nSrv.fanyi(_data.i18n); _data = Object.assign(_data, { shortcutRoot: true, _id: -1, @@ -349,12 +347,4 @@ export class MenuService implements OnDestroy { this._change$.unsubscribe(); this.i18n$?.unsubscribe(); } - - setRouterLink(url: string): void { - this.$routerLink.next(url); - } - - getRouterLink(): Observable { - return this.$routerLink.asObservable(); - } } diff --git a/packages/theme/src/services/title/title.service.spec.ts b/packages/theme/src/services/title/title.service.spec.ts index 5feba0f19..66724b3cb 100644 --- a/packages/theme/src/services/title/title.service.spec.ts +++ b/packages/theme/src/services/title/title.service.spec.ts @@ -1,17 +1,16 @@ import { DOCUMENT } from '@angular/common'; import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { Title } from '@angular/platform-browser'; -import { ActivatedRoute } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; +import { ActivatedRoute, RouterModule } from '@angular/router'; import { of } from 'rxjs'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; -import { RouteTitle, TitleService } from './title.service'; import { YunzaiThemeModule } from '../../theme.module'; import { YunzaiI18NService, YunzaiI18NServiceFake, YUNZAI_I18N_TOKEN } from '../i18n/i18n'; import { Menu } from '../menu/interface'; import { MenuService } from '../menu/menu.service'; +import { RouteTitle, TitleService } from './title.service'; describe('Service: Title', () => { let getPathByUrlData: NzSafeAny; @@ -34,7 +33,7 @@ describe('Service: Title', () => { function genModule(providers: NzSafeAny[] = [], loadI18n: boolean = true): void { const i18nProvider: NzSafeAny[] = loadI18n ? [{ provide: YUNZAI_I18N_TOKEN, useClass: YunzaiI18NServiceFake }] : []; TestBed.configureTestingModule({ - imports: [YunzaiThemeModule, RouterTestingModule], + imports: [YunzaiThemeModule, RouterModule.forRoot([])], providers: [TitleService, MenuService, { provide: Title, useClass: TestTitleService }, ...i18nProvider].concat( providers ) diff --git a/packages/theme/src/services/title/title.service.ts b/packages/theme/src/services/title/title.service.ts index 2d3b5c42e..4237f9ba1 100644 --- a/packages/theme/src/services/title/title.service.ts +++ b/packages/theme/src/services/title/title.service.ts @@ -5,17 +5,6 @@ import { Title } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable, of, map, delay, isObservable, switchMap, Subscription } from 'rxjs'; -import { - deepCopy, - useLocalStorageProjectInfo, - useLocalStorageUser, - YUNZAI_CONFIG, - YunzaiBusinessConfig, - YunzaiConfig, - YunzaiMenu, - YunzaiProjectInfo -} from '@yelon/util'; - import { YUNZAI_I18N_TOKEN } from '../i18n/i18n'; import { MenuService } from '../menu/menu.service'; @@ -32,7 +21,6 @@ export class TitleService implements OnDestroy { private _separator: string = ' - '; private _reverse: boolean = false; private tit$?: Subscription; - private config!: YunzaiBusinessConfig; readonly DELAY_TIME = 25; @@ -40,13 +28,10 @@ export class TitleService implements OnDestroy { private readonly injector = inject(Injector); private readonly title = inject(Title); private readonly menuSrv = inject(MenuService); - private readonly i18nSrv = inject(YUNZAI_I18N_TOKEN, { optional: true }); - - private readonly conf: YunzaiConfig = inject(YUNZAI_CONFIG); + private readonly i18nSrv = inject(YUNZAI_I18N_TOKEN); constructor() { - this.i18nSrv?.change.pipe(takeUntilDestroyed()).subscribe(() => this.setTitle()); - this.config = this.conf.bis!; + this.i18nSrv.change.pipe(takeUntilDestroyed()).subscribe(() => this.setTitle()); } /** @@ -124,7 +109,7 @@ export class TitleService implements OnDestroy { let next = this.injector.get(ActivatedRoute); while (next.firstChild) next = next.firstChild; const data: RouteTitle = (next.snapshot && next.snapshot.data) || {}; - if (data.titleI18n && this.i18nSrv) data.title = this.i18nSrv.fanyi(data.titleI18n); + if (data.titleI18n) data.title = this.i18nSrv.fanyi(data.titleI18n); return isObservable(data.title) ? data.title : of(data.title!); } @@ -134,37 +119,10 @@ export class TitleService implements OnDestroy { const item = menus[menus.length - 1]; let title; - if (item.i18n && this.i18nSrv) title = this.i18nSrv.fanyi(item.i18n); + if (item.i18n) title = this.i18nSrv.fanyi(item.i18n); return of(title || item.text!); } - private getBySystemSet(): Observable { - if (!this.config || !this.config.systemCode) return of(''); - let title = ''; - const [, getUser] = useLocalStorageUser(); - const yunzaiUser = getUser()!; - const yunzaiMenus: YunzaiMenu[] = deepCopy(yunzaiUser.menu).filter( - m => m.systemCode && m.systemCode === this.config.systemCode - ); - if (!yunzaiMenus || yunzaiMenus.length === 0) return of(''); - let systemName = ''; - const currentMenu = yunzaiMenus.pop(); - if (currentMenu) { - systemName = currentMenu.text; - } - const [, getProjectInfo] = useLocalStorageProjectInfo(); - const projectInfo: YunzaiProjectInfo = getProjectInfo()!; - if (!projectInfo) return of(''); - const pageTitlePattern = projectInfo.pageTitlePattern; - if (!pageTitlePattern) return of(''); - if (pageTitlePattern) { - title = pageTitlePattern.replace(`$\{systemName}`, systemName); - } else { - title = systemName; - } - return of(title); - } - /** * Set the document title */ @@ -173,7 +131,6 @@ export class TitleService implements OnDestroy { this.tit$ = of(title) .pipe( switchMap(tit => (tit ? of(tit) : this.getByRoute())), - switchMap(tit => (tit ? of(tit) : this.getBySystemSet())), switchMap(tit => (tit ? of(tit) : this.getByMenu())), switchMap(tit => (tit ? of(tit) : this.getByElement())), map(tit => tit || this.default), @@ -200,7 +157,7 @@ export class TitleService implements OnDestroy { * Set i18n key of the document title */ setTitleByI18n(key: string, params?: unknown): void { - this.setTitle(this.i18nSrv?.fanyi(key, params)); + this.setTitle(this.i18nSrv.fanyi(key, params)); } ngOnDestroy(): void { diff --git a/packages/theme/system/entry.less b/packages/theme/system/entry.less index e33500d80..50a82b204 100644 --- a/packages/theme/system/entry.less +++ b/packages/theme/system/entry.less @@ -1,5 +1,3 @@ -@import './theme-default.less'; - // Angular @import './ng/index.less'; diff --git a/packages/theme/system/index.less b/packages/theme/system/index.less index 82e94d57b..239cfc156 100644 --- a/packages/theme/system/index.less +++ b/packages/theme/system/index.less @@ -1,6 +1,9 @@ -// antd: base +/** antd: base */ @import 'ng-zorro-antd/style/entry.less'; -// antd: all components + +/** antd: all components */ @import 'ng-zorro-antd/components.less'; -// system + +/** system */ +@import './mixins/index.less'; @import './entry.less'; diff --git a/packages/theme/system/mixins/_freak.js b/packages/theme/system/mixins/_freak.js new file mode 100644 index 000000000..4c811a93c --- /dev/null +++ b/packages/theme/system/mixins/_freak.js @@ -0,0 +1,49 @@ +module.exports = { + install: function (less, pluginManager, functions) { + functions.add('genComment', function (value, ...comments) { + return ( + '/* AUTOGENERATE: ' + + comments + .map(c => c.value) + .join('|SPLIT|') + .replace(/(\{0\})/g, '"' + value.value + '"') + + '*/' + ); + }); + functions.add('genCommentColor', function (color, ...comments) { + var imgColor = ' ![Color](https://dummyimage.com/20x20/' + color.value.substring(1) + '/fff.png&text=+)'; + return ( + '/* AUTOGENERATE: ' + + comments + .map(c => c.value + imgColor) + .join('|SPLIT|') + .replace(/(\{0\})/g, '"' + color.value + '"') + + '*/' + ); + }); + functions.add('genCommentTypeColor', function (type, color) { + var comments = []; + switch (type.value) { + case 'text': + comments = ['Set the text color to {0}', '设置文本颜色为 {0}']; + break; + case 'background': + comments = ['Set the background light color to {0}', '设置背景颜色为 {0}']; + break; + case 'hoverBackground': + comments = ['Set the hover background color to {0}', '设置悬停时背景颜色为 {0}']; + break; + } + if (comments.length === 0) return ''; + var imgColor = ' ![Color](https://dummyimage.com/20x20/' + color.value.substring(1) + '/fff.png&text=+)'; + return ( + '/* AUTOGENERATE: ' + + comments + .map(c => c + imgColor) + .join('|SPLIT|') + .replace(/(\{0\})/g, '"' + color.value + '"') + + '*/' + ); + }); + } +}; diff --git a/packages/theme/system/mixins/_freak.less b/packages/theme/system/mixins/_freak.less index afe89d03e..39cae0fcf 100644 --- a/packages/theme/system/mixins/_freak.less +++ b/packages/theme/system/mixins/_freak.less @@ -1,57 +1 @@ -.freakMixin() { - @functions: ~`(function() { - function toColorList(list) { - list = list.slice(1, list.length - 1).split(','); - var ret = []; - for (var i = 0, c = list.length; i < c; i++) { - ret.push(list[i].trim().split(' ')); - } - return ret; - } - - var catchColors; - function _initColor(list) { - if (!catchColors) catchColors = toColorList(list); - } - - this.getColor = function(list, name, position) { - _initColor(list); - var ret = ''; - for (var i = 0, c = catchColors.length; i < c; i++) { - if (catchColors[i][0] === name) { - ret = catchColors[i][position - 1]; - break; - } - } - return ret; - } - - this.genComment = function(value, ...comments) { - return '/* AUTOGENERATE: ' + comments.join('|SPLIT|').replace(/(\{0\})/g, '"' + value + '"') + '*/'; - } - - this.genCommentColor = function(color, ...comments) { - var imgColor = ' ![Color](https://dummyimage.com/20x20/' + color.substring(1) + '/fff.png&text=+)'; - return '/* AUTOGENERATE: ' + comments.map(c => c + imgColor).join('|SPLIT|').replace(/(\{0\})/g, '"' + color + '"') + '*/'; - } - - this.genCommentTypeColor = function(type, color) { - var comments = []; - switch (type) { - case 'text': - comments = ['Set the text color to {0}', '设置文本颜色为 {0}']; - break; - case 'background': - comments = ['Set the background light color to {0}', '设置背景颜色为 {0}']; - break; - case 'hoverBackground': - comments = ['Set the hover background color to {0}', '设置悬停时背景颜色为 {0}']; - break; - } - if (comments.length === 0) return ''; - var imgColor = ' ![Color](https://dummyimage.com/20x20/' + color.substring(1) + '/fff.png&text=+)'; - return '/* AUTOGENERATE: ' + comments.map(c => c + imgColor).join('|SPLIT|').replace(/(\{0\})/g, '"' + color + '"') + '*/'; - } - })()`; -} -.freakMixin(); +@plugin "./_freak"; diff --git a/packages/theme/system/theme-default.less b/packages/theme/system/theme-default.less index f25b00397..7d09a9e20 100644 --- a/packages/theme/system/theme-default.less +++ b/packages/theme/system/theme-default.less @@ -1,22 +1,19 @@ -@import 'ng-zorro-antd/src/style/mixins/index.less'; -@import './mixins/index.less'; - @root-entry-name: default; -@white: #fff; -@black: #000; - // grey @grey-1: #ffffff; @grey-2: #fafafa; @grey-3: #f5f5f5; -@grey-4: #e8e8e8; +@grey-4: #f0f0f0; @grey-5: #d9d9d9; @grey-6: #bfbfbf; @grey-7: #8c8c8c; @grey-8: #595959; -@grey-9: #262626; -@grey-10: #000000; +@grey-9: #434343; +@grey-10: #262626; +@grey-11: #1f1f1f; +@grey-12: #141414; +@grey-13: #000000; @color-light-index: 5; @color-basic-index: 6; @@ -137,23 +134,18 @@ @muted-color: @grey-7; -// Colors -@white: #fff; -@black: #000; - // scrollbar @scrollbar-enabled: true; @scrollbar-width: 6px; @scrollbar-height: 6px; -@scrollbar-track-color: rgba(0, 0, 0, 0.3); +@scrollbar-track-color: fade(@black, 30%); @scrollbar-thumb-color: transparent; @scrollbar-table-enabled: false; // type // ========== -// font-size -// https://ant.design/docs/spec/font-cn#字号 +/** font-size https://ant.design/docs/spec/font-cn#字号 */ @font-size-large: @font-size-base + 8; // 20px @font-size-small: @font-size-base; // 12px @@ -167,8 +159,8 @@ @enable-all-colors: false; // Code -@code-border-color: #eee; -@code-bg: #f7f7f7; +@code-border-color: @border-color-base; +@code-bg: @tag-default-bg; @drawer-xl: 1200px; @drawer-lg: 900px; @@ -208,22 +200,22 @@ @nz-table-rep-max-width: @mobile-max; @nz-table-rep-min-width: @nz-table-rep-max-width + 1; @nz-table-rep-header-background: @border-color-split; -@nz-table-rep-even-background: #f9f9f9; +@nz-table-rep-even-background: @item-hover-bg; @nz-table-rep-padding-vertical: (@table-padding-vertical / 2); @nz-table-rep-padding-horizontal: (@table-padding-horizontal / 2); @nz-table-rep-column-name-width: 100px; @nz-table-rep-column-name-text-align: right; @nz-table-rep-column-name-padding-right: 8px; -@nz-table-rep-column-name-color: rgba(0, 0, 0, 0.5); +@nz-table-rep-column-name-color: fade(@black, 50%); // Forced to turn off Modal animation @forced-turn-off-nz-modal-animation-enabled: false; @sf-title-text-align: left; -@sf-optional-color: rgba(0, 0, 0, 0.35); +@sf-optional-color: fade(@black, 35%); @sf-optional-margin: 2px; @sf-inline-ant-select-min-width: 100px; -@sf-widget-array-type-card-remove-bg: rgba(0, 0, 0, 0.26); +@sf-widget-array-type-card-remove-bg: fade(@black, 26%); @yn-yes-color: @blue-6; @yn-no-color: @grey-7; diff --git a/packages/theme/system/theme-variable.less b/packages/theme/system/theme-variable.less new file mode 100644 index 000000000..ed473a3d4 --- /dev/null +++ b/packages/theme/system/theme-variable.less @@ -0,0 +1,34 @@ +@import './theme-default.less'; + +@root-entry-name: variable; + +@{html-selector} { + --grey-1: #fff; + --grey-2: #fafafa; + --grey-3: #f5f5f5; + --grey-4: #f0f0f0; + --grey-5: #d9d9d9; + --grey-6: #bfbfbf; + --grey-7: #8c8c8c; + --grey-8: #595959; + --grey-9: #434343; + --grey-10: #262626; + --grey-11: #1f1f1f; + --grey-12: #141414; + --grey-13: #000; +} + +// grey +@grey-1: var(--grey-1); +@grey-2: var(--grey-2); +@grey-3: var(--grey-3); +@grey-4: var(--grey-4); +@grey-5: var(--grey-5); +@grey-6: var(--grey-6); +@grey-7: var(--grey-7); +@grey-8: var(--grey-8); +@grey-9: var(--grey-9); +@grey-10: var(--grey-10); +@grey-11: var(--grey-11); +@grey-12: var(--grey-12); +@grey-13: var(--grey-13); diff --git a/packages/theme/system/utils/_border.less b/packages/theme/system/utils/_border.less index 666056d39..7a6d87a3c 100644 --- a/packages/theme/system/utils/_border.less +++ b/packages/theme/system/utils/_border.less @@ -12,35 +12,42 @@ } @border-width-list: 0, 1; + .for(@border-width-list, { .border-css-mixin(@i) when(@i > 0) { @css-value-comment: ~'1px'; @css-value: ~'@{i}px solid @{border-color}'; } + .border-css-mixin(@i) when(default()) { @css-value-comment: ~'0px'; @css-value: 0; } + .border-css-mixin(@adIndex); .border-@{adIndex} { - e(~`genComment('@{css-value-comment}', 'Set the border size to {0}', '设置边框大小为 {0}')`); + genComment('@{css-value-comment}', 'Set the border size to {0}', '设置边框大小为 {0}'); border: @css-value !important; } + .border-top-@{adIndex} { - e(~`genComment('@{css-value-comment}', 'Set the top border size to {0}', '设置上边框大小为 {0}')`); + genComment('@{css-value-comment}', 'Set the top border size to {0}', '设置上边框大小为 {0}'); border-top: @css-value !important; } + .border-right-@{adIndex} { - e(~`genComment('@{css-value-comment}', 'Set the right border size to {0}', '设置右边框大小为 {0}')`); + genComment('@{css-value-comment}', 'Set the right border size to {0}', '设置右边框大小为 {0}'); border-right: @css-value !important; } + .border-bottom-@{adIndex} { - e(~`genComment('@{css-value-comment}', 'Set the bottom border size to {0}', '设置下边框大小为 {0}')`); + genComment('@{css-value-comment}', 'Set the bottom border size to {0}', '设置下边框大小为 {0}'); border-bottom: @css-value !important; } + .border-left-@{adIndex} { - e(~`genComment('@{css-value-comment}', 'Set the left border size to {0}', '设置左边框大小为 {0}')`); + genComment('@{css-value-comment}', 'Set the left border size to {0}', '设置左边框大小为 {0}'); border-left: @css-value !important; } }); @@ -49,14 +56,14 @@ .for-each(@colors, { .border-@{adKey} { @border-colors-color: extract(@adItem, @color-basic-position); - e(~`genCommentColor('@{border-colors-color}', 'Set the border color to {0}', '设置边框颜色为 {0}')`); + genCommentColor('@{border-colors-color}', 'Set the border color to {0}', '设置边框颜色为 {0}'); border-color: @border-colors-color !important; } }); .for-each(@aliasColors, { .border-@{adKey} { - e(~`genComment('@{adValue}', 'Set the border color to {0}', '设置边框颜色为 {0}')`); + genComment('@{adValue}', 'Set the border color to {0}', '设置边框颜色为 {0}'); border-color: @adValue !important; } }); @@ -64,23 +71,27 @@ // Border-radius .for-each(@border-grids, { .rounded-@{adKey} { - e(~`genComment('@{adValue}', 'Set the border radius to {0}', '设置边框圆角为 {0}')`); + genComment('@{adValue}', 'Set the border radius to {0}', '设置边框圆角为 {0}'); border-radius: @adValue; } + .rounded-top-left-@{adKey} { - e(~`genComment('@{adValue}', 'Set the border top-left corner to {0}', '设置左上角边框圆角为 {0}')`); + genComment('@{adValue}', 'Set the border top-left corner to {0}', '设置左上角边框圆角为 {0}'); border-top-left-radius: @adValue; } + .rounded-top-right-@{adKey} { - e(~`genComment('@{adValue}', 'Set the border top-right corner to {0}', '设置右上角边框圆角为 {0}')`); + genComment('@{adValue}', 'Set the border top-right corner to {0}', '设置右上角边框圆角为 {0}'); border-top-right-radius: @adValue; } + .rounded-bottom-left-@{adKey} { - e(~`genComment('@{adValue}', 'Set the border bottom-left corner to {0}', '设置左下角边框圆角为 {0}')`); + genComment('@{adValue}', 'Set the border bottom-left corner to {0}', '设置左下角边框圆角为 {0}'); border-bottom-left-radius: @adValue; } + .rounded-bottom-right-@{adKey} { - e(~`genComment('@{adValue}', 'Set the border bottom-right corner to {0}', '设置右下角边框圆角为 {0}')`); + genComment('@{adValue}', 'Set the border bottom-right corner to {0}', '设置右下角边框圆角为 {0}'); border-bottom-right-radius: @adValue; } }); @@ -117,10 +128,12 @@ */ .rotate-loop(@i) when (@i > 0) { @num: @i * 15; + .rotate-@{num} { - e(~`genComment('@{num}', 'Element transform to {0} degree', '元素旋转 {0} 度')`); + genComment('@{num}', 'Element transform to {0} degree', '元素旋转 {0} 度'); transform: rotate(~'@{num}deg'); } + .rotate-loop(@i - 1); } diff --git a/packages/theme/system/utils/_color.less b/packages/theme/system/utils/_color.less index 94698b299..8c7a8b131 100644 --- a/packages/theme/system/utils/_color.less +++ b/packages/theme/system/utils/_color.less @@ -6,6 +6,7 @@ .bg-white { background-color: #fff !important; } + /** * Set the background to transparent * @@ -14,6 +15,7 @@ .bg-transparent { background-color: transparent !important; } + /** * Set the text to white * @@ -22,6 +24,7 @@ .text-white { color: #fff !important; } + /** * Set the color as the primary color when hovering the text * @@ -29,6 +32,7 @@ */ .text-hover { cursor: pointer; + &:hover { color: @primary-color !important; } @@ -37,92 +41,112 @@ .for-each(@colors, { .bg-@{adKey}-light { @color-bg-light-color: extract(@adItem, @color-light-position); - e(~`genCommentTypeColor('background', '@{color-bg-light-color}')`); + genCommentTypeColor('background', '@{color-bg-light-color}'); background-color: @color-bg-light-color !important; } + .bg-@{adKey} { @color-bg-color: extract(@adItem, @color-basic-position); - e(~`genCommentTypeColor('background', '@{color-bg-color}')`); + genCommentTypeColor('background', '@{color-bg-color}'); background-color: @color-bg-color !important; } + .bg-@{adKey}-dark { @color-bg-dark-color: extract(@adItem, @color-dark-position); - e(~`genCommentTypeColor('background', '@{color-bg-dark-color}')`); + genCommentTypeColor('background', '@{color-bg-dark-color}'); background-color: @color-bg-dark-color !important; } .bg-@{adKey}-light-h { @color-bg-light-h-color: extract(@adItem, @color-light-position); - e(~`genCommentTypeColor('hoverBackground', '@{color-bg-light-h-color}')`); + genCommentTypeColor('hoverBackground', '@{color-bg-light-h-color}'); transition: background-color 300ms; - &:hover { background-color: @color-bg-light-h-color !important; } + + &:hover { + background-color: @color-bg-light-h-color !important; + } } + .bg-@{adKey}-h { @color-bg-basic-h-color: extract(@adItem, @color-basic-position); - e(~`genCommentTypeColor('hoverBackground', '@{color-bg-basic-h-color}')`); + genCommentTypeColor('hoverBackground', '@{color-bg-basic-h-color}'); transition: background-color 300ms; - &:hover { background-color: @color-bg-basic-h-color !important; } + + &:hover { + background-color: @color-bg-basic-h-color !important; + } } + .bg-@{adKey}-dark-h { @color-bg-dark-h-color: extract(@adItem, @color-dark-position); - e(~`genCommentTypeColor('hoverBackground', '@{color-bg-dark-h-color}')`); + genCommentTypeColor('hoverBackground', '@{color-bg-dark-h-color}'); transition: background-color 300ms; - &:hover { background-color: @color-bg-dark-h-color !important; } + + &:hover { + background-color: @color-bg-dark-h-color !important; + } } .text-@{adKey}-light { @color-text-light-color: extract(@adItem, @color-light-position); - e(~`genCommentTypeColor('text', '@{color-text-light-color}')`); + genCommentTypeColor('text', '@{color-text-light-color}'); color: @color-text-light-color !important; } + .text-@{adKey} { @color-text-basic-color: extract(@adItem, @color-basic-position); - e(~`genCommentTypeColor('text', '@{color-text-basic-color}')`); + genCommentTypeColor('text', '@{color-text-basic-color}'); color: @color-text-basic-color !important; } + .text-@{adKey}-dark { @color-text-dark-color: extract(@adItem, @color-dark-position); - e(~`genCommentTypeColor('text', '@{color-text-dark-color}')`); + genCommentTypeColor('text', '@{color-text-dark-color}'); color: @color-text-dark-color !important; } }); .for-each(@aliasColors, { - .bg-@{adKey}-light { - @alias-bg-light-color: color(~`colorPalette('@{adValue}', @{color-light-index})`); - e(~`genCommentTypeColor('background', '@{alias-bg-light-color}')`); + .bg-@{adKey}-light { + @alias-bg-light-color: color(colorPalette('@{adValue}', @color-light-index)); + genCommentTypeColor('background', '@{alias-bg-light-color}'); background-color: @alias-bg-light-color !important; } + .bg-@{adKey} { - e(~`genCommentTypeColor('background', '@{adValue}')`); + genCommentTypeColor('background', '@{adValue}'); background-color: @adValue !important; } - .bg-@{adKey}-dark { - @alias-bg-dark-color: color(~`colorPalette('@{adValue}', @{color-dark-index})`); - e(~`genCommentTypeColor('background', '@{alias-bg-dark-color}')`); + + .bg-@{adKey}-dark { + @alias-bg-dark-color: color(colorPalette('@{adValue}', @color-dark-index)); + genCommentTypeColor('background', '@{alias-bg-dark-color}'); background-color: @alias-bg-dark-color !important; - } + } .bg-@{adKey}-h { - e(~`genCommentTypeColor('hoverBackground', '@{adValue}')`); + genCommentTypeColor('hoverBackground', '@{adValue}'); transition: background-color 300ms; + &:hover { background-color: @adValue !important; } } - .text-@{adKey}-light { - @alias-text-light-color: color(~`colorPalette('@{adValue}', @{color-light-index})`); - e(~`genCommentTypeColor('text', '@{alias-text-light-color}')`); + .text-@{adKey}-light { + @alias-text-light-color: color(colorPalette('@{adValue}', @color-light-index)); + genCommentTypeColor('text', '@{alias-text-light-color}'); color: @alias-text-light-color !important; - } + } + .text-@{adKey} { - e(~`genCommentTypeColor('text', '@{adValue}')`); + genCommentTypeColor('text', '@{adValue}'); color: @adValue !important; } - .text-@{adKey}-dark { - @alias-text-dark-color: color(~`colorPalette('@{adValue}', @{color-dark-index})`); - e(~`genCommentTypeColor('text', '@{alias-text-dark-color}')`); + + .text-@{adKey}-dark { + @alias-text-dark-color: color(colorPalette('@{adValue}', @color-dark-index)); + genCommentTypeColor('text', '@{alias-text-dark-color}'); color: @alias-text-dark-color !important; } }); @@ -130,41 +154,49 @@ // grey .for-each(@greyColorer, { .bg-grey-@{adKey} { - e(~`genCommentTypeColor('background', '@{adValue}')`); + genCommentTypeColor('background', '@{adValue}'); background-color: @adValue !important; } + .bg-grey-@{adKey}-h { - e(~`genCommentTypeColor('hoverBackground', '@{adValue}')`); + genCommentTypeColor('hoverBackground', '@{adValue}'); transition: background-color 300ms; + &:hover { background-color: @adValue !important; } } + .text-grey-@{adKey} { - e(~`genCommentTypeColor('text', '@{adValue}')`); + genCommentTypeColor('text', '@{adValue}'); color: @adValue !important; } }); -.all-colors-minin(@enabled) when (@enabled = true) { +.all-colors-minin(@enabled) when (@enabled =true) { .loop-no(@adKey, @adValue, @i: 1) when (@i =< length(@color-no-list)) { - @name: '@{adKey}-@{i}'; // red-1, red-2, red-3 .... + @name: '@{adKey}-@{i}'; // red-1, red-2, red-3 .... @nameValue: e(@@name); + .bg-@{adKey}-@{i} { - e(~`genCommentTypeColor('background', '@{nameValue}')`); + genCommentTypeColor('background', '@{nameValue}'); background-color: @nameValue !important; } + .bg-@{adKey}-@{i}-h { - e(~`genCommentTypeColor('hoverBackground', '@{nameValue}')`); + genCommentTypeColor('hoverBackground', '@{nameValue}'); transition: background-color 300ms; + &:hover { background-color: @nameValue !important; } } + .text-@{adKey}-@{i} { - e(~`genCommentTypeColor('text', '@{nameValue}')`); + genCommentTypeColor('text', '@{nameValue}'); color: @nameValue !important; } + .loop-no(@adKey, @adValue, @i + 1); } @@ -172,4 +204,5 @@ .loop-no(@adKey, @adValue, 1); }); } + .all-colors-minin(@enable-all-colors); diff --git a/packages/theme/system/utils/_spacing.less b/packages/theme/system/utils/_spacing.less index 185039190..e3f229354 100644 --- a/packages/theme/system/utils/_spacing.less +++ b/packages/theme/system/utils/_spacing.less @@ -4,35 +4,42 @@ @item: extract(@mp-list, @i); @abbrev: extract(@item, 2); @prop: extract(@item, 1); + .@{abbrev}@{infix} { - e(~`genComment('@{adValue}', 'Set the @{prop} size to {0}', '设置 @{prop} 大小为 {0}')`); - @{prop}: @adValue !important; + genComment('@{adValue}', 'Set the @{prop} size to {0}', '设置 @{prop} 大小为 {0}'); + @{prop}: @adValue !important; } + .@{abbrev}t@{infix} { - e(~`genComment('@{adValue}', 'Set the @{prop}-top size to {0}', '设置 @{prop}-top 大小为 {0}')`); - @{prop}-top: @adValue !important; + genComment('@{adValue}', 'Set the @{prop}-top size to {0}', '设置 @{prop}-top 大小为 {0}'); + @{prop}-top: @adValue !important; } + .@{abbrev}r@{infix} { - e(~`genComment('@{adValue}', 'Set the @{prop}-right size to {0}', '设置 @{prop}-right 大小为 {0}')`); - @{prop}-right: @adValue !important; + genComment('@{adValue}', 'Set the @{prop}-right size to {0}', '设置 @{prop}-right 大小为 {0}'); + @{prop}-right: @adValue !important; } + .@{abbrev}b@{infix} { - e(~`genComment('@{adValue}', 'Set the @{prop}-bottom size to {0}', '设置 @{prop}-bottom 大小为 {0}')`); - @{prop}-bottom: @adValue !important; + genComment('@{adValue}', 'Set the @{prop}-bottom size to {0}', '设置 @{prop}-bottom 大小为 {0}'); + @{prop}-bottom: @adValue !important; } + .@{abbrev}l@{infix} { - e(~`genComment('@{adValue}', 'Set the @{prop}-left size to {0}', '设置 @{prop}-left 大小为 {0}')`); - @{prop}-left: @adValue !important; + genComment('@{adValue}', 'Set the @{prop}-left size to {0}', '设置 @{prop}-left 大小为 {0}'); + @{prop}-left: @adValue !important; } + .@{abbrev}x@{infix} { - e(~`genComment('@{adValue}', 'Set the @{prop} horizontal (left & right) size to {0}', '设置 @{prop} 水平(即左与右)大小为 {0}')`); - @{prop}-right: @adValue !important; - @{prop}-left: @adValue !important; + genComment('@{adValue}', 'Set the @{prop} horizontal (left & right) size to {0}', '设置 @{prop} 水平(即左与右)大小为 {0}'); + @{prop}-right: @adValue !important; + @{prop}-left: @adValue !important; } + .@{abbrev}y@{infix} { - e(~`genComment('@{adValue}', 'Set the @{prop} vertical (top & bottom) size to {0}', '设置 @{prop} 垂直(即上与下)大小为 {0}')`); - @{prop}-top: @adValue !important; - @{prop}-bottom: @adValue !important; + genComment('@{adValue}', 'Set the @{prop} vertical (top & bottom) size to {0}', '设置 @{prop} 垂直(即上与下)大小为 {0}'); + @{prop}-top: @adValue !important; + @{prop}-bottom: @adValue !important; } .loop-mp(@infix, @adKey, @adValue, @i + 1); @@ -42,15 +49,17 @@ @item: extract(@mp-list, @i); @abbrev: extract(@item, 2); @prop: extract(@item, 1); + // 无须设置注释 [dir="rtl"] { .@{abbrev}r@{infix} { - @{prop}-left: @adValue !important; - @{prop}-right: inherit !important; + @{prop}-left: @adValue !important; + @{prop}-right: inherit !important; } + .@{abbrev}l@{infix} { - @{prop}-right: @adValue !important; - @{prop}-left: inherit !important; + @{prop}-right: @adValue !important; + @{prop}-left: inherit !important; } } @@ -59,26 +68,32 @@ .spacings-loop(@type) { .for-each(@spacings, { - .infix-mixin(@adKey) when(@adKey = 0) { + .infix-mixin(@adKey) when(@adKey =0) { @infix: 0; } + .infix-mixin(@adKey) when(default()) { @infix: ~'-@{adKey}'; } + .infix-mixin(@adKey); - .loop-mp-fn-mixin(@type) when(@type = 'lrt') { + .loop-mp-fn-mixin(@type) when(@type ='lrt') { .loop-mp(@infix, @adKey, @adValue, 1); } + .loop-mp-fn-mixin(@type) when(default()) { .loop-mp-rtl(@infix, @adKey, @adValue, 1); } + .loop-mp-fn-mixin(@type); }); } .spacings-loop('lrt'); + .spacings-rtl-mixin(@enabled) when(@enabled=true) { .spacings-loop('rtl'); } + .spacings-rtl-mixin(@rtl-enabled); diff --git a/packages/theme/system/utils/_width.less b/packages/theme/system/utils/_width.less index ff4b690f0..cef6ab81f 100644 --- a/packages/theme/system/utils/_width.less +++ b/packages/theme/system/utils/_width.less @@ -1,20 +1,23 @@ .for-each(@widths, { - .width-@{adKey} { - e(~`genComment('@{adValue}', 'Specify the width as {0}', '指定宽度为 {0}')`); + .width-@{adKey} { + genComment('@{adValue}', 'Specify the width as {0}', '指定宽度为 {0}'); width: @adValue !important; } - .max-width-@{adKey} { - e(~`genComment('@{adValue}', 'Specify the maximum width as {0}', '指定最大宽度为 {0}')`); + + .max-width-@{adKey} { + genComment('@{adValue}', 'Specify the maximum width as {0}', '指定最大宽度为 {0}'); max-width: @adValue !important; } - .min-width-@{adKey} { - e(~`genComment('@{adValue}', 'Specify the minimum width as {0}', '指定最小宽度为 {0}')`); + .min-width-@{adKey} { + genComment('@{adValue}', 'Specify the minimum width as {0}', '指定最小宽度为 {0}'); min-width: @adValue !important; } }); @media (max-width: @mobile-max) { + + /* stylelint-disable-next-line rule-empty-line-before */ .width-sm, .max-width-sm, .min-width-sm { diff --git a/packages/theme/theme-btn/theme-btn.component.ts b/packages/theme/theme-btn/theme-btn.component.ts index 7021775b8..fd2f41a6c 100644 --- a/packages/theme/theme-btn/theme-btn.component.ts +++ b/packages/theme/theme-btn/theme-btn.component.ts @@ -18,17 +18,15 @@ import { } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { I18nPipe } from '@yelon/theme'; import { YunzaiConfigService } from '@yelon/util/config'; import { NzDropDownDirective, NzDropdownMenuComponent } from 'ng-zorro-antd/dropdown'; -import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzMenuDirective, NzMenuItemComponent } from 'ng-zorro-antd/menu'; import { NzTooltipDirective } from 'ng-zorro-antd/tooltip'; export interface ThemeBtnType { key: string; text: string; - color?: string; + color:string; } export const YUNZAI_THEME_BTN_KEYS = new InjectionToken('YUNZAI_THEME_BTN_KEYS'); @@ -42,22 +40,14 @@ export const YUNZAI_THEME_BTN_KEYS = new InjectionToken('YUNZAI_THEME_BT }, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [ - NzDropDownDirective, - NzDropdownMenuComponent, - NzMenuDirective, - NzMenuItemComponent, - NzTooltipDirective, - NzIconModule, - I18nPipe - ] + imports: [NzDropDownDirective, NzDropdownMenuComponent, NzMenuDirective, NzMenuItemComponent, NzTooltipDirective] }) export class ThemeBtnComponent implements OnInit, OnDestroy { private readonly doc = inject(DOCUMENT); private readonly platform = inject(Platform); private readonly renderer = inject(Renderer2); private readonly configSrv = inject(YunzaiConfigService); - private readonly directionality = inject(Directionality, { optional: true }); + private readonly directionality = inject(Directionality); private readonly cdr = inject(ChangeDetectorRef); private readonly destroy$ = inject(DestroyRef); @@ -81,12 +71,11 @@ export class ThemeBtnComponent implements OnInit, OnDestroy { @Input() deployUrl = ''; @Output() readonly themeChange = new EventEmitter(); dir?: Direction = 'ltr'; - private key = inject(YUNZAI_THEME_BTN_KEYS, { optional: true }) ?? 'site-theme'; ngOnInit(): void { - this.dir = this.directionality?.value; - this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe((direction: Direction) => { + this.dir = this.directionality.value; + this.directionality.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe((direction: Direction) => { this.dir = direction; this.cdr.detectChanges(); });