From 377fe120d74085bac86607674b244855cab0215b Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Wed, 27 Apr 2022 17:50:59 +0200 Subject: [PATCH] More stories (#873) * More stories. * Fix components. --- .../shared/forms/array-editor.component.ts | 2 +- .../forms/stock-photo-editor.component.html | 6 +- .../forms/stock-photo-editor.component.scss | 17 +- .../reference-dropdown.component.html | 2 +- .../reference-dropdown.component.ts | 7 + .../references/references-tags.component.html | 3 +- .../references/references-tags.component.ts | 7 + .../contributor-add-form.component.html | 2 +- .../workflows/workflow-step.component.html | 6 +- .../workflow-transition.component.html | 5 +- .../forms/editors/autocomplete.component.html | 8 +- .../forms/editors/autocomplete.component.ts | 2 +- .../forms/editors/autocomplete.stories.ts | 106 +++++++++++ .../forms/editors/date-time-editor.stories.ts | 22 ++- .../forms/editors/dropdown.component.html | 12 +- .../forms/editors/dropdown.component.scss | 8 + .../forms/editors/dropdown.component.ts | 6 + .../angular/forms/editors/dropdown.stories.ts | 175 ++++++++++++++++++ .../forms/editors/tag-editor.component.html | 52 +++--- .../forms/editors/tag-editor.component.scss | 6 + .../forms/editors/tag-editor.component.ts | 14 +- .../forms/editors/tag-editor.stories.ts | 170 +++++++++++++++++ .../app/framework/angular/layout.component.ts | 17 +- .../angular/list-view.component.html | 2 +- .../framework/angular/loader.component.html | 9 + .../framework/angular/loader.component.scss | 22 +++ .../app/framework/angular/loader.component.ts | 24 +++ .../app/framework/angular/loader.stories.ts | 82 ++++++++ .../modals/modal-placement.directive.ts | 11 +- frontend/src/app/framework/declarations.ts | 1 + frontend/src/app/framework/module.ts | 4 +- .../asset-folder-dropdown-item.component.html | 2 +- .../shell/pages/internal/logo.component.html | 5 +- .../shell/pages/internal/logo.component.scss | 5 +- frontend/src/app/theme/_common.scss | 53 ------ 35 files changed, 758 insertions(+), 117 deletions(-) create mode 100644 frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts create mode 100644 frontend/src/app/framework/angular/forms/editors/dropdown.stories.ts create mode 100644 frontend/src/app/framework/angular/forms/editors/tag-editor.stories.ts create mode 100644 frontend/src/app/framework/angular/loader.component.html create mode 100644 frontend/src/app/framework/angular/loader.component.scss create mode 100644 frontend/src/app/framework/angular/loader.component.ts create mode 100644 frontend/src/app/framework/angular/loader.stories.ts diff --git a/frontend/src/app/features/content/shared/forms/array-editor.component.ts b/frontend/src/app/features/content/shared/forms/array-editor.component.ts index eb23ad513..447ec9f14 100644 --- a/frontend/src/app/features/content/shared/forms/array-editor.component.ts +++ b/frontend/src/app/features/content/shared/forms/array-editor.component.ts @@ -71,7 +71,7 @@ export class ArrayEditorComponent implements OnChanges { const maxItems = this.formModel.field.properties['maxItems'] || Number.MAX_VALUE; if (Types.is(this.formModel.field.properties, ComponentsFieldPropertiesDto)) { - this.schemasList = this.formModel.field.properties.schemaIds?.map(x => this.formModel.globals.schemas[x]).defined() || []; + this.schemasList = this.formModel.field.properties.schemaIds?.map(x => this.formModel.globals.schemas[x]).defined().sortedByString(x => x.displayName) || []; } else { this.isArray = true; } diff --git a/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.html b/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.html index b341476a2..6427ba248 100644 --- a/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.html +++ b/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.html @@ -14,7 +14,7 @@
- +
@@ -22,7 +22,7 @@ - + @@ -44,7 +44,7 @@
diff --git a/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.scss b/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.scss index dca77b4c0..457f5aea6 100644 --- a/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.scss +++ b/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.scss @@ -9,12 +9,17 @@ $color-background: #000; background: $color-background; border: 0; border-radius: 0; - color: white; text-align: center; - i { - margin-top: 1rem; - margin-bottom: 1rem; + & { + position: relative; + max-height: none; + min-height: 2rem; + } + + sqx-loader { + @include absolute(50%, null, null, 50%); + margin-left: -8px; } img { @@ -22,10 +27,6 @@ $color-background: #000; } } -.spin2 { - font-size: 14px; -} - .search { display: inline-block; margin-left: 0; diff --git a/frontend/src/app/features/content/shared/references/reference-dropdown.component.html b/frontend/src/app/features/content/shared/references/reference-dropdown.component.html index acaa15e61..0c7eecd94 100644 --- a/frontend/src/app/features/content/shared/references/reference-dropdown.component.html +++ b/frontend/src/app/features/content/shared/references/reference-dropdown.component.html @@ -1,4 +1,4 @@ - + diff --git a/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts b/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts index 5be8f8870..c1fc1267f 100644 --- a/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts +++ b/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts @@ -21,6 +21,9 @@ interface State { // The name of the selected item. selectedItem?: ContentName; + + // True when loading. + isLoading?: boolean; } type ContentName = { name: string; id?: string }; @@ -143,6 +146,8 @@ export class ReferenceDropdownComponent extends StatefulControlComponent) { + this.next({ isLoading: true }); + observable .subscribe({ next: ({ items }) => { @@ -196,5 +201,7 @@ export class ReferenceDropdownComponent extends StatefulControlComponent + [suggestions]="snapshot.converter.suggestions" + [suggestionsLoading]="snapshot.isLoading"> \ No newline at end of file diff --git a/frontend/src/app/features/content/shared/references/references-tags.component.ts b/frontend/src/app/features/content/shared/references/references-tags.component.ts index 9adb51356..278adb89e 100644 --- a/frontend/src/app/features/content/shared/references/references-tags.component.ts +++ b/frontend/src/app/features/content/shared/references/references-tags.component.ts @@ -19,6 +19,9 @@ export const SQX_REFERENCES_TAGS_CONTROL_VALUE_ACCESSOR: any = { interface State { // The tags converter. converter: ReferencesTagsConverter; + + // True when loading. + isLoading?: boolean; } const NO_EMIT = { emitEvent: false }; @@ -126,6 +129,8 @@ export class ReferencesTagsComponent extends StatefulControlComponent) { + this.next({ isLoading: true }); + observable .subscribe({ next: ({ items }) => { @@ -165,5 +170,7 @@ export class ReferencesTagsComponent extends StatefulControlComponent
- + diff --git a/frontend/src/app/features/settings/pages/workflows/workflow-step.component.html b/frontend/src/app/features/settings/pages/workflows/workflow-step.component.html index 88c9d9656..17f5e66e6 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflow-step.component.html +++ b/frontend/src/app/features/settings/pages/workflows/workflow-step.component.html @@ -91,13 +91,15 @@ {{ 'workflows.syntax.for' | sqxTranslate }}
- + [suggestions]="roles" placeholder="{{ 'common.role' | sqxTranslate }}" + [styleDashed]="true">
diff --git a/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.html b/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.html index a9d036c98..c367c5730 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.html +++ b/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.html @@ -22,13 +22,14 @@ {{ 'workflows.syntax.for' | sqxTranslate }}
-
diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html index 94c51d516..7a1cce8cc 100644 --- a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html +++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html @@ -12,7 +12,13 @@ [formControl]="queryInput">
- + + + + + + +
diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts index ee351c73d..52b2f4ab0 100644 --- a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts @@ -58,7 +58,7 @@ export class AutocompleteComponent extends StatefulControlComponent new LocalizerService(TRANSLATIONS) }, + ], + }), + ], +} as Meta; + +const Template: Story = (args: AutocompleteComponent) => ({ + props: args, + template: ` + + + + + `, +}); + +class Source implements AutocompleteSource { + constructor( + private readonly values: string[], + private readonly delay = 0, + ) { + } + + public find(query: string): Observable { + return timer(this.delay).pipe(map(() => this.values.filter(x => x.indexOf(query) >= 0))); + } +} + +export const Default = Template.bind({}); + +Default.args = { + source: new Source(['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing']), +}; + +export const Disabled = Template.bind({}); + +Disabled.args = { + disabled: true, +}; + +export const Icon = Template.bind({}); + +Icon.args = { + icon: 'user', +}; + +export const IconLoading = Template.bind({}); + +IconLoading.args = { + source: new Source(['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing'], 4000), + icon: 'user', +}; + +export const StyleEmpty = Template.bind({}); + +StyleEmpty.args = { + inputStyle: 'empty', +}; + +export const StyleUnderlined = Template.bind({}); + +StyleUnderlined.args = { + inputStyle: 'underlined', +}; \ No newline at end of file diff --git a/frontend/src/app/framework/angular/forms/editors/date-time-editor.stories.ts b/frontend/src/app/framework/angular/forms/editors/date-time-editor.stories.ts index dbe540333..5e5fbdd54 100644 --- a/frontend/src/app/framework/angular/forms/editors/date-time-editor.stories.ts +++ b/frontend/src/app/framework/angular/forms/editors/date-time-editor.stories.ts @@ -5,10 +5,10 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { moduleMetadata } from '@storybook/angular'; import { Meta, Story } from '@storybook/angular/types-6-0'; -import { LocalizerService, SqxFrameworkModule, UIOptions } from '@app/framework'; -import { DateTimeEditorComponent } from './date-time-editor.component'; +import { DateTimeEditorComponent, LocalizerService, SqxFrameworkModule, UIOptions } from '@app/framework'; const translations = { 'common.date': 'Date', @@ -23,6 +23,9 @@ export default { title: 'Framework/DateTimeEditor', component: DateTimeEditorComponent, argTypes: { + disabled: { + control: 'boolean', + }, hideClear: { control: 'boolean', }, @@ -43,6 +46,7 @@ export default { decorators: [ moduleMetadata({ imports: [ + BrowserAnimationsModule, SqxFrameworkModule, SqxFrameworkModule.forRoot(), ], @@ -64,8 +68,22 @@ Date.args = { mode: 'Date', }; +export const DateDisabled = Template.bind({}); + +DateDisabled.args = { + mode: 'Date', + disabled: true, +}; + export const DateTime = Template.bind({}); DateTime.args = { mode: 'DateTime', +}; + +export const DateTimeDisabled = Template.bind({}); + +DateTimeDisabled.args = { + mode: 'DateTime', + disabled: true, }; \ No newline at end of file diff --git a/frontend/src/app/framework/angular/forms/editors/dropdown.component.html b/frontend/src/app/framework/angular/forms/editors/dropdown.component.html index 7c3cf913c..b2d2e0d92 100644 --- a/frontend/src/app/framework/angular/forms/editors/dropdown.component.html +++ b/frontend/src/app/framework/angular/forms/editors/dropdown.component.html @@ -24,7 +24,7 @@ [style]="dropdownStyles" [position]="dropdownPosition">
- +
@@ -38,6 +38,16 @@
+ +
+ + + + + + {{itemsEmptyText | sqxTranslate}} + +
diff --git a/frontend/src/app/framework/angular/forms/editors/dropdown.component.scss b/frontend/src/app/framework/angular/forms/editors/dropdown.component.scss index f54d194f3..a536de888 100644 --- a/frontend/src/app/framework/angular/forms/editors/dropdown.component.scss +++ b/frontend/src/app/framework/angular/forms/editors/dropdown.component.scss @@ -19,6 +19,10 @@ $color-input-disabled: #eef1f4; white-space: nowrap; } +.no-events { + pointer-events: none; +} + .search-form { padding: .5rem; } @@ -57,4 +61,8 @@ $color-input-disabled: #eef1f4; .form-control { cursor: default; } +} + +sqx-loader { + margin-top: .25rem; } \ No newline at end of file diff --git a/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts b/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts index caba20a1d..bbd6a774a 100644 --- a/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts @@ -46,6 +46,12 @@ export class DropdownComponent extends StatefulControlComponent | undefined | null = []; diff --git a/frontend/src/app/framework/angular/forms/editors/dropdown.stories.ts b/frontend/src/app/framework/angular/forms/editors/dropdown.stories.ts new file mode 100644 index 000000000..950db8576 --- /dev/null +++ b/frontend/src/app/framework/angular/forms/editors/dropdown.stories.ts @@ -0,0 +1,175 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Component } from '@angular/core'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { moduleMetadata } from '@storybook/angular'; +import { Meta, Story } from '@storybook/angular/types-6-0'; +import { DropdownComponent, LocalizerService, SqxFrameworkModule } from '@app/framework'; + +const TRANSLATIONS = { + 'common.search': 'Search', + 'common.empty': 'Nothing available.', +}; + +@Component({ + selector: 'sqx-dropdown-test', + template: ` + + + + + `, +}) +class TestComponent { + public items: string[] = []; + public itemsLoading = false; + + public load() { + this.items = []; + this.itemsLoading = true; + + setTimeout(() => { + this.items = ['A', 'B']; + this.itemsLoading = false; + }, 1000); + } +} + +export default { + title: 'Framework/Dropdown', + component: DropdownComponent, + argTypes: { + disabled: { + control: 'boolean', + }, + dropdownFullWidth: { + control: 'boolean', + }, + }, + decorators: [ + moduleMetadata({ + declarations: [ + TestComponent, + ], + imports: [ + BrowserAnimationsModule, + SqxFrameworkModule, + SqxFrameworkModule.forRoot(), + ], + providers: [ + { provide: LocalizerService, useFactory: () => new LocalizerService(TRANSLATIONS) }, + ], + }), + ], +} as Meta; + +const Template: Story = (args: DropdownComponent) => ({ + props: args, + template: ` + + + + + `, +}); + +const Template2: Story = (args: DropdownComponent) => ({ + props: args, + template: ` + + + + {{target.label}} + + + + `, +}); + +const Template3: Story = (args: DropdownComponent) => ({ + props: args, + template: ` + + `, +}); + +export const Default = Template.bind({}); + +Default.args = { + items: ['A', 'B', 'C'], + model: 'B', +}; + +export const Empty = Template.bind({}); + +Empty.args = { + items: [], + model: 'B', +}; + +export const EmptyLoading = Template.bind({}); + +EmptyLoading.args = { + items: [], + itemsLoading: true, +}; + +export const NoSearch = Template.bind({}); + +NoSearch.args = { + items: ['A', 'B', 'C'], + canSearch: false, +}; + +export const FullWidth = Template.bind({}); + +FullWidth.args = { + items: ['A', 'B', 'C'], + dropdownFullWidth: true, +}; + +export const ComplexValues = Template2.bind({}); + +ComplexValues.args = { + searchProperty: 'label', + items: [{ + id: 1, + label: 'Lorem', + }, { + id: 2, + label: 'ipsum', + }, { + id: 3, + label: 'dolor', + }, { + id: 4, + label: 'sit', + }], + model: 2, + valueProperty: 'id', +}; + +export const Lazy = Template3.bind({}); \ No newline at end of file diff --git a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html index 5f2be2c4e..b7a450d23 100644 --- a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html +++ b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html @@ -9,7 +9,7 @@ [class.multiline]="!singleLine" [class.focus]="snapshot.hasFocus" [class.disabled]="snapshot.isDisabled" - [class.dashed]="dashed && !(snapshot.items.length > 0)"> + [class.dashed]="styleDashed && !(snapshot.items.length > 0)"> {{item}} @@ -49,29 +49,37 @@ - - - -
-
-
- + + +
+
+
+ - -
+
-
-
+
+ +
+ + + + + + {{suggestionsEmptyText | sqxTranslate}} + +
+
\ No newline at end of file diff --git a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.scss b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.scss index fd812c330..d2f36d84f 100644 --- a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.scss +++ b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.scss @@ -185,5 +185,11 @@ div { .suggestions-dropdown { @include force-width(450px); + max-height: none; + min-height: 4rem; padding: 1rem; +} + +sqx-loader { + margin-top: .25rem; } \ No newline at end of file diff --git a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts index 195b413f2..da96c29cb 100644 --- a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts @@ -68,14 +68,11 @@ export class TagEditorComponent extends StatefulControlComponent + + + + `, +}) +class TestComponent { + public suggestions: string[] = []; + public suggestionsLoading = false; + + public load() { + this.suggestions = []; + this.suggestionsLoading = true; + + setTimeout(() => { + this.suggestions = ['A', 'B']; + this.suggestionsLoading = false; + }, 1000); + } +} + +export default { + title: 'Framework/TagEditor', + component: TagEditorComponent, + argTypes: { + dashed: { + control: 'boolean', + }, + disabled: { + control: 'boolean', + }, + }, + decorators: [ + moduleMetadata({ + declarations: [ + TestComponent, + ], + imports: [ + SqxFrameworkModule, + SqxFrameworkModule.forRoot(), + ], + providers: [ + { provide: LocalizerService, useFactory: () => new LocalizerService(TRANSLATIONS) }, + ], + }), + ], +} as Meta; + +const Template: Story = (args: TagEditorComponent) => ({ + props: args, + template: ` + + + + + `, +}); + +const Template2: Story = (args: TagEditorComponent) => ({ + props: args, + template: ` + + `, +}); + +export const Default = Template.bind({}); + +export const Suggestions = Template.bind({}); + +Suggestions.args = { + suggestions: ['A', 'B', 'C'], + allowOpen: true, +}; + +export const SuggestionsEmpty = Template.bind({}); + +SuggestionsEmpty.args = { + suggestions: [], + allowOpen: true, +}; + +export const SuggestionsLoading = Template.bind({}); + +SuggestionsLoading.args = { + suggestionsLoading: true, + allowOpen: true, +}; + +export const Values = Template.bind({}); + +Values.args = { + suggestions: [], + ngModel: ['A', 'A', 'B'], +}; + +export const StyleDashed = Template.bind({}); + +StyleDashed.args = { + styleDashed: true, + ngModel: [], +}; + +export const StyleDashedValues = Template.bind({}); + +StyleDashedValues.args = { + styleDashed: true, + ngModel: ['A', 'B', 'C'], +}; + +export const StyleBlank = Template.bind({}); + +StyleBlank.args = { + styleBlank: true, + ngModel: [], +}; + +export const StyleBlankValues = Template.bind({}); + +StyleBlankValues.args = { + styleBlank: true, + ngModel: ['A', 'B', 'C'], +}; + +export const Multiline = Template.bind({}); + +Multiline.args = { + singleLine: false, + ngModel: ['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit', 'sed', 'do', 'eiusmod', 'tempor', 'incididunt', 'ut', 'labore', 'et', 'dolore', 'magna', 'aliqua'], +}; + +export const SingleLine = Template.bind({}); + +SingleLine.args = { + singleLine: true, + ngModel: ['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit', 'sed', 'do', 'eiusmod', 'tempor', 'incididunt', 'ut', 'labore', 'et', 'dolore', 'magna', 'aliqua'], +}; + +export const Lazy = Template2.bind({}); \ No newline at end of file diff --git a/frontend/src/app/framework/angular/layout.component.ts b/frontend/src/app/framework/angular/layout.component.ts index 015894e79..dadbeb411 100644 --- a/frontend/src/app/framework/angular/layout.component.ts +++ b/frontend/src/app/framework/angular/layout.component.ts @@ -9,7 +9,7 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core'; import { ActivatedRoute, NavigationEnd, QueryParamsHandling, Router } from '@angular/router'; -import { filter, map, startWith } from 'rxjs'; +import { concat, defer, filter, map, of } from 'rxjs'; import { LayoutContainerDirective } from './layout-container.directive'; @Component({ @@ -87,13 +87,14 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit { } public firstChild = - this.router.events.pipe( - filter(event => event instanceof NavigationEnd), - map(() => { - return !!this.route.firstChild; - }), - startWith(!!this.route.firstChild), - ); + concat( + defer(() => of(!!this.route.firstChild)), + this.router.events.pipe( + filter(event => event instanceof NavigationEnd), + map(() => { + return !!this.route.firstChild; + }), + )); constructor( private readonly container: LayoutContainerDirective, diff --git a/frontend/src/app/framework/angular/list-view.component.html b/frontend/src/app/framework/angular/list-view.component.html index 36de1a668..2f29f4efa 100644 --- a/frontend/src/app/framework/angular/list-view.component.html +++ b/frontend/src/app/framework/angular/list-view.component.html @@ -37,7 +37,7 @@
- +
diff --git a/frontend/src/app/framework/angular/loader.component.html b/frontend/src/app/framework/angular/loader.component.html new file mode 100644 index 000000000..1e8899e43 --- /dev/null +++ b/frontend/src/app/framework/angular/loader.component.html @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/app/framework/angular/loader.component.scss b/frontend/src/app/framework/angular/loader.component.scss new file mode 100644 index 000000000..3251926f6 --- /dev/null +++ b/frontend/src/app/framework/angular/loader.component.scss @@ -0,0 +1,22 @@ +@import 'mixins'; +@import 'vars'; + +:host { + display: inline-block; +} + +.theme { + stroke: $color-theme-brand; +} + +.white { + stroke: $color-white; +} + +.input { + stroke: $color-input; +} + +.text { + stroke: $color-text; +} \ No newline at end of file diff --git a/frontend/src/app/framework/angular/loader.component.ts b/frontend/src/app/framework/angular/loader.component.ts new file mode 100644 index 000000000..d36e173a2 --- /dev/null +++ b/frontend/src/app/framework/angular/loader.component.ts @@ -0,0 +1,24 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +/* eslint-disable import/no-cycle */ + +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; + +@Component({ + selector: 'sqx-loader', + styleUrls: ['./loader.component.scss'], + templateUrl: './loader.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class LoaderComponent { + @Input() + public size = 18; + + @Input() + public color: 'input' | 'theme' | 'white' | 'text' = 'text'; +} \ No newline at end of file diff --git a/frontend/src/app/framework/angular/loader.stories.ts b/frontend/src/app/framework/angular/loader.stories.ts new file mode 100644 index 000000000..da1c0a8bd --- /dev/null +++ b/frontend/src/app/framework/angular/loader.stories.ts @@ -0,0 +1,82 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { moduleMetadata } from '@storybook/angular'; +import { Meta, Story } from '@storybook/angular/types-6-0'; +import { LoaderComponent, SqxFrameworkModule } from '@app/framework'; + +export default { + title: 'Framework/Loader', + component: LoaderComponent, + argTypes: { + size: { + control: 'number', + }, + color: { + control: 'enum', + options: [ + 'white', + 'theme', + 'text', + ], + }, + }, + decorators: [ + moduleMetadata({ + imports: [ + SqxFrameworkModule, + SqxFrameworkModule.forRoot(), + ], + }), + ], +} as Meta; + +const Template: Story = (args: LoaderComponent) => ({ + props: args, +}); + +export const ColorWhite = Template.bind({}); + +ColorWhite.args = { + color: 'white', +}; + +export const ColorTheme = Template.bind({}); + +ColorTheme.args = { + color: 'theme', +}; + +export const ColorText = Template.bind({}); + +ColorText.args = { + color: 'text', +}; + +export const ColorInput = Template.bind({}); + +ColorInput.args = { + color: 'input', +}; + +export const SizeSmall = Template.bind({}); + +SizeSmall.args = { + size: 16, +}; + +export const SizeMedium = Template.bind({}); + +SizeMedium.args = { + size: 32, +}; + +export const SizeLarge = Template.bind({}); + +SizeLarge.args = { + size: 64, +}; \ No newline at end of file diff --git a/frontend/src/app/framework/angular/modals/modal-placement.directive.ts b/frontend/src/app/framework/angular/modals/modal-placement.directive.ts index 990d414dd..21b203038 100644 --- a/frontend/src/app/framework/angular/modals/modal-placement.directive.ts +++ b/frontend/src/app/framework/angular/modals/modal-placement.directive.ts @@ -14,6 +14,7 @@ import { AnchorX, AnchorY, computeAnchors, positionModal, PositionRequest, Relat }) export class ModalPlacementDirective extends ResourceOwner implements AfterViewInit, OnDestroy { private targetElement?: Element; + private isViewInit = false; @Input('sqxAnchoredTo') public set target(element: Element) { @@ -26,7 +27,9 @@ export class ModalPlacementDirective extends ResourceOwner implements AfterViewI this.listenToElement(element); } - this.updatePosition(); + if (this.isViewInit) { + this.updatePosition(); + } } } @@ -79,6 +82,8 @@ export class ModalPlacementDirective extends ResourceOwner implements AfterViewI private readonly element: ElementRef, ) { super(); + + renderer.setStyle(element.nativeElement, 'visibility', 'hidden'); } private listenToElement(element: any) { @@ -110,6 +115,8 @@ export class ModalPlacementDirective extends ResourceOwner implements AfterViewI } this.updatePosition(); + + this.isViewInit = true; } private updatePosition() { @@ -186,5 +193,7 @@ export class ModalPlacementDirective extends ResourceOwner implements AfterViewI if (position.y) { this.renderer.setStyle(modalRef, 'top', `${position.y}px`); } + + this.renderer.setStyle(modalRef, 'visibility', 'visible'); } } diff --git a/frontend/src/app/framework/declarations.ts b/frontend/src/app/framework/declarations.ts index 2ab22f58c..ddbd1f270 100644 --- a/frontend/src/app/framework/declarations.ts +++ b/frontend/src/app/framework/declarations.ts @@ -47,6 +47,7 @@ export * from './angular/image-source.directive'; export * from './angular/image-url.directive'; export * from './angular/if-once.directive'; export * from './angular/language-selector.component'; +export * from './angular/loader.component'; export * from './angular/layout-container.directive'; export * from './angular/layout.component'; export * from './angular/list-view.component'; diff --git a/frontend/src/app/framework/module.ts b/frontend/src/app/framework/module.ts index aee7b74e7..d53886523 100644 --- a/frontend/src/app/framework/module.ts +++ b/frontend/src/app/framework/module.ts @@ -11,7 +11,7 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { ColorPickerModule } from 'ngx-color-picker'; -import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, CompensateScrollbarDirective, ConfirmClickDirective, ControlErrorsComponent, ControlErrorsMessagesComponent, CopyDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DropdownMenuComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, HighlightPipe, HoverBackgroundDirective, IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MonthPipe, OnboardingService, OnboardingTooltipComponent, PagerComponent, ParentLinkDirective, ProgressBarComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations'; +import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, CompensateScrollbarDirective, ConfirmClickDirective, ControlErrorsComponent, ControlErrorsMessagesComponent, CopyDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DropdownMenuComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, HighlightPipe, HoverBackgroundDirective, IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoaderComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MonthPipe, OnboardingService, OnboardingTooltipComponent, PagerComponent, ParentLinkDirective, ProgressBarComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations'; @NgModule({ imports: [ @@ -68,6 +68,7 @@ import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterc LayoutContainerDirective, LightenPipe, ListViewComponent, + LoaderComponent, LocalizedInputComponent, MarkdownDirective, MarkdownInlinePipe, @@ -155,6 +156,7 @@ import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterc LayoutContainerDirective, LightenPipe, ListViewComponent, + LoaderComponent, LocalizedInputComponent, MarkdownDirective, MarkdownInlinePipe, diff --git a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html index dff3f339d..78c36c422 100644 --- a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html +++ b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html @@ -2,7 +2,7 @@ diff --git a/frontend/src/app/shell/pages/internal/logo.component.html b/frontend/src/app/shell/pages/internal/logo.component.html index df9e37af4..c66dfe2ab 100644 --- a/frontend/src/app/shell/pages/internal/logo.component.html +++ b/frontend/src/app/shell/pages/internal/logo.component.html @@ -1,3 +1,6 @@ - +
+ +
+ \ No newline at end of file diff --git a/frontend/src/app/shell/pages/internal/logo.component.scss b/frontend/src/app/shell/pages/internal/logo.component.scss index 6fb859e52..d79a7a518 100644 --- a/frontend/src/app/shell/pages/internal/logo.component.scss +++ b/frontend/src/app/shell/pages/internal/logo.component.scss @@ -5,5 +5,8 @@ @include absolute(50%, null, null, 50%); margin-left: -14px; margin-top: -14px; - width: 28px; + + sqx-loader { + display: block; + } } \ No newline at end of file diff --git a/frontend/src/app/theme/_common.scss b/frontend/src/app/theme/_common.scss index cef9270c1..02da7bdb9 100644 --- a/frontend/src/app/theme/_common.scss +++ b/frontend/src/app/theme/_common.scss @@ -346,57 +346,4 @@ hr { hr { margin: .5rem 0; } -} - -// -// Animations -// -.spin { - animation: spin 3s infinite linear; -} - -.spin2 { - animation: spin2 1s infinite linear; -} - -i { - &.spin { - display: inline-block; - } - - &.spin2 { - display: inline-block; - } -} - -@keyframes spin2 { - 50% { - transform: rotate(180deg); - } - - 100% { - transform: rotate(360deg); - } -} - -@keyframes spin { - 20% { - transform: rotate(0deg); - } - - 30% { - transform: rotate(180deg); - } - - 70% { - transform: rotate(180deg); - } - - 80% { - transform: rotate(360deg); - } - - 100% { - transform: rotate(360deg); - } } \ No newline at end of file