Browse Source

Support class names in editor.

pull/1039/head
Sebastian 3 years ago
parent
commit
552acb5db0
  1. 2
      backend/i18n/frontend_en.json
  2. 2
      backend/i18n/frontend_fr.json
  3. 2
      backend/i18n/frontend_it.json
  4. 2
      backend/i18n/frontend_nl.json
  5. 2
      backend/i18n/frontend_pt.json
  6. 2
      backend/i18n/frontend_zh.json
  7. 2
      backend/i18n/source/frontend_en.json
  8. 2
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs
  9. 5
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs
  10. 14
      backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerServices.cs
  11. 2
      backend/src/Squidex/wwwroot/editor/squidex-editor.css
  12. 250
      backend/src/Squidex/wwwroot/editor/squidex-editor.js
  13. 3
      frontend/src/app/declarations.d.ts
  14. 2
      frontend/src/app/features/content/shared/forms/field-editor.component.html
  15. 12
      frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.html
  16. 4
      frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.ts
  17. 6
      frontend/src/app/shared/components/forms/rich-editor.component.ts
  18. 1
      frontend/src/app/shared/services/schemas.types.ts
  19. 1
      frontend/src/app/shared/state/schemas.forms.ts

2
backend/i18n/frontend_en.json

@ -921,6 +921,8 @@
"schemas.fieldTypes.string.characters": "Characters",
"schemas.fieldTypes.string.charactersMax": "Max Characters",
"schemas.fieldTypes.string.charactersMin": "Min Characters",
"schemas.fieldTypes.string.classNames": "Class Names",
"schemas.fieldTypes.string.classNamesHint": "The allowed CSS classes that the content creator can choose from.",
"schemas.fieldTypes.string.contentType": "Content Type",
"schemas.fieldTypes.string.description": "Titles, names, paragraphs.",
"schemas.fieldTypes.string.folderId": "Asset folder",

2
backend/i18n/frontend_fr.json

@ -921,6 +921,8 @@
"schemas.fieldTypes.string.characters": "Personnages",
"schemas.fieldTypes.string.charactersMax": "Caractères maximum",
"schemas.fieldTypes.string.charactersMin": "Caractères minimum",
"schemas.fieldTypes.string.classNames": "Class Names",
"schemas.fieldTypes.string.classNamesHint": "The allowed CSS classes that the content creator can choose from.",
"schemas.fieldTypes.string.contentType": "Type de contenu",
"schemas.fieldTypes.string.description": "Titres, noms, paragraphes.",
"schemas.fieldTypes.string.folderId": "Dossier d'actifs",

2
backend/i18n/frontend_it.json

@ -921,6 +921,8 @@
"schemas.fieldTypes.string.characters": "Caratteri",
"schemas.fieldTypes.string.charactersMax": "Max numero di Caratteri",
"schemas.fieldTypes.string.charactersMin": "Min numero di Caratteri",
"schemas.fieldTypes.string.classNames": "Class Names",
"schemas.fieldTypes.string.classNamesHint": "The allowed CSS classes that the content creator can choose from.",
"schemas.fieldTypes.string.contentType": "Content Type",
"schemas.fieldTypes.string.description": "Titoli, nomi, paragrafi.",
"schemas.fieldTypes.string.folderId": "Asset folder",

2
backend/i18n/frontend_nl.json

@ -921,6 +921,8 @@
"schemas.fieldTypes.string.characters": "Karakters",
"schemas.fieldTypes.string.charactersMax": "Max. karakters",
"schemas.fieldTypes.string.charactersMin": "Min. karakters",
"schemas.fieldTypes.string.classNames": "Class Names",
"schemas.fieldTypes.string.classNamesHint": "The allowed CSS classes that the content creator can choose from.",
"schemas.fieldTypes.string.contentType": "Inhoudstype",
"schemas.fieldTypes.string.description": "Titels, namen, alinea's.",
"schemas.fieldTypes.string.folderId": "Documenten map",

2
backend/i18n/frontend_pt.json

@ -921,6 +921,8 @@
"schemas.fieldTypes.string.characters": "Personagens",
"schemas.fieldTypes.string.charactersMax": "Personagens Max",
"schemas.fieldTypes.string.charactersMin": "Personagens de Min",
"schemas.fieldTypes.string.classNames": "Class Names",
"schemas.fieldTypes.string.classNamesHint": "The allowed CSS classes that the content creator can choose from.",
"schemas.fieldTypes.string.contentType": "Tipo de Conteúdo",
"schemas.fieldTypes.string.description": "Títulos, nomes, parágrafos.",
"schemas.fieldTypes.string.folderId": "Pasta de ativos",

2
backend/i18n/frontend_zh.json

@ -921,6 +921,8 @@
"schemas.fieldTypes.string.characters": "字符",
"schemas.fieldTypes.string.charactersMax": "最大字符数",
"schemas.fieldTypes.string.charactersMin": "最小字符数",
"schemas.fieldTypes.string.classNames": "Class Names",
"schemas.fieldTypes.string.classNamesHint": "The allowed CSS classes that the content creator can choose from.",
"schemas.fieldTypes.string.contentType": "内容类型",
"schemas.fieldTypes.string.description": "标题、名称、段落。",
"schemas.fieldTypes.string.folderId": "资源文件夹",

2
backend/i18n/source/frontend_en.json

@ -921,6 +921,8 @@
"schemas.fieldTypes.string.characters": "Characters",
"schemas.fieldTypes.string.charactersMax": "Max Characters",
"schemas.fieldTypes.string.charactersMin": "Min Characters",
"schemas.fieldTypes.string.classNames": "Class Names",
"schemas.fieldTypes.string.classNamesHint": "The allowed CSS classes that the content creator can choose from.",
"schemas.fieldTypes.string.contentType": "Content Type",
"schemas.fieldTypes.string.description": "Titles, names, paragraphs.",
"schemas.fieldTypes.string.folderId": "Asset folder",

2
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs

@ -14,6 +14,8 @@ public sealed record StringFieldProperties : FieldProperties
{
public ReadonlyList<string>? AllowedValues { get; init; }
public ReadonlyList<string>? ClassNames { get; set; }
public LocalizedValue<string?> DefaultValues { get; init; }
public string? DefaultValue { get; init; }

5
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs

@ -71,6 +71,11 @@ public sealed class StringFieldPropertiesDto : FieldPropertiesDto
/// </summary>
public int? MaxWords { get; set; }
/// <summary>
/// The class names for the editor.
/// </summary>
public ReadonlyList<string>? ClassNames { get; set; }
/// <summary>
/// The allowed values for the field value.
/// </summary>

14
backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerServices.cs

@ -81,6 +81,12 @@ public static class IdentityServerServices
})
.AddServer(builder =>
{
builder.AddEventHandler<ValidateTokenRequestContext>(builder =>
{
builder.UseSingletonHandler<TestHandler>()
.SetOrder(int.MinValue);
});
builder.AddEventHandler<ProcessSignInContext>(builder =>
{
builder.UseSingletonHandler<AlwaysAddScopeHandler>()
@ -171,3 +177,11 @@ public static class IdentityServerServices
endpointUris.Add(uri);
}
}
public sealed class TestHandler : IOpenIddictServerHandler<ValidateTokenRequestContext>
{
public ValueTask HandleAsync(ValidateTokenRequestContext context)
{
return default;
}
}

2
backend/src/Squidex/wwwroot/editor/squidex-editor.css

@ -1 +1 @@
.remirror-theme{width:100%}.remirror-theme{position:relative}.remirror-theme *{box-sizing:border-box}.remirror-editor{min-height:300px!important;max-height:500px}.MuiStack-root{padding:5px}.MuiStack-root>.MuiBox-root{margin-right:5px}.MuiBox-root>.MuiButtonBase-root{border-color:#dedfe3!important;border-radius:0}.MuiBox-root>.MuiButtonBase-root{border-left-width:0}.MuiBox-root>.MuiBox-root:first-child>.MuiButtonBase-root{border-left-width:1px}.MuiButtonBase-root.Mui-selected:hover{background-color:#3284f4!important}.remirror-editor-wrapper{padding-top:0!important}.custom-icon path{fill:#0000008a}.remirror-theme div.ProseMirror{border:1px solid #dedfe3!important;border-radius:0!important;box-shadow:none!important}.MuiTooltip-popper>div{background-color:#1a2129;border-radius:0;font-size:85%;font-weight:400;padding:.5rem}.squidex-editor-disabled{pointer-events:none}.squidex-editor-menu{border:1px solid #dedfe3;border-bottom:0}.squidex-editor-counter{border:1px solid #dedfe3;border-top:0;font-size:85%;font-weight:400;opacity:.8;padding:4px 10px 4px 4px;text-align:right}.squidex-editor-image-view{border:1px solid #dedfe3;border-radius:0;display:inline-block;margin-top:15px;margin-bottom:15px;overflow:hidden}.squidex-editor-image-view .squidex-editor-button{bottom:10px;left:10px;position:absolute}.squidex-editor-image-element{display:block;max-width:400px;max-height:400px}.squidex-editor-image-info{left:10px;top:10px;position:absolute;background-color:#3284f4;color:#fff;font-size:85%;font-weight:400;padding:2px 6px}.squidex-editor-content-link{align-items:center;border-radius:2px;border:1px solid #dedfe3;padding:10px;display:flex;flex-direction:row;flex-wrap:nowrap;margin-top:10px;margin-bottom:10px}.squidex-editor-content-schema{display:block;overflow-x:hidden;overflow-y:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;min-width:0;width:auto;border-right:1px solid #c2c4cc;color:#8b8f9d;flex-shrink:0;padding-left:10px;padding-right:10px;width:200px}.squidex-editor-content-name{display:block;overflow-x:hidden;overflow-y:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;min-width:0;width:auto;padding-left:10px;padding-right:0}.squidex-editor-html{margin-bottom:10px;margin-top:10px;position:relative}.squidex-editor-html-label{left:6px;top:0;position:absolute;color:#8b8f9d;font-size:85%;font-weight:400}.squidex-editor-html textarea{font-family:monospace;padding:30px 20px 20px}.squidex-editor-button{background-color:#fff;border-radius:0;border:1px solid #dedfe3;bottom:10px;font-size:85%;font-weight:400;line-height:1;padding:6px 12px}.squidex-editor-button:hover{background-color:#f5f5f5}.squidex-editor-input{border:1px solid #dedfe3;border-radius:0;box-sizing:border-box;height:30px;margin-left:0;margin-right:5px;outline:none;padding:6px 12px}.squidex-editor-input:active{border-color:#3284f4}.squidex-editor-floating{border:1px solid #dedfe3}.squidex-editor-floating .MuiBox-root{margin-right:0!important}.squidex-editor-floating .MuiButtonBase-root{height:30px}.squidex-editor-modal-wrapper,.squidex-editor-modal-backdrop{bottom:0;left:0;right:0;top:0;position:absolute}.squidex-editor-modal-backdrop{background-color:#00000003}.squidex-editor-modal-window{left:50%;top:50%;background-color:#fff;border:1px solid #dedfe3;border-radius:0;box-sizing:border-box;margin-top:-50px;margin-left:-150px;position:absolute;width:350px}.squidex-editor-modal-body{display:flex;flex-direction:row;flex-grow:1;padding-top:0!important}.squidex-editor-modal-body,.squidex-editor-modal-title{padding:15px}.squidex-editor-modal-title{font-size:85%}.squidex-editor-modal-window input{flex-grow:1}
.remirror-theme{width:100%}.remirror-theme{position:relative}.remirror-theme *{box-sizing:border-box}.remirror-editor{min-height:300px!important;max-height:500px}.MuiStack-root>.MuiBox-root{margin-right:5px}.MuiBox-root>.MuiButtonBase-root{border-color:#dedfe3!important;border-radius:0}.MuiBox-root>.MuiButtonBase-root{border-left-width:0!important}.MuiBox-root>.MuiBox-root:first-child>.MuiButtonBase-root{border-left-width:1px!important}.MuiStack-root>.MuiBox-root>.MuiButtonBase-root:first-child{border-left-width:1px!important}.MuiButtonBase-root.Mui-selected:hover{background-color:#3284f4!important}.remirror-editor-wrapper{padding-top:0!important}.custom-icon path{fill:#0000008a}.remirror-theme div.ProseMirror{border:1px solid #dedfe3!important;border-radius:0!important;box-shadow:none!important}.MuiTooltip-popper>div{background-color:#1a2129;border-radius:0;font-size:85%;font-weight:400;padding:.5rem}.MuiMenu-paper{transform:none!important}.squidex-editor-disabled{pointer-events:none}.squidex-editor-menu{border:1px solid #dedfe3;border-bottom:0;padding:5px}.squidex-editor-counter{border:1px solid #dedfe3;border-top:0;font-size:85%;font-weight:400;opacity:.8;padding:4px 10px 4px 4px;text-align:right}.squidex-editor-image-view{border:1px solid #dedfe3;border-radius:0;display:inline-block;margin-top:15px;margin-bottom:15px;overflow:hidden}.squidex-editor-image-view .squidex-editor-button{bottom:10px;left:10px;position:absolute}.squidex-editor-image-element{display:block;max-width:400px;max-height:400px}.squidex-editor-image-info{left:10px;top:10px;position:absolute;background-color:#3284f4;color:#fff;font-size:85%;font-weight:400;padding:2px 6px}.squidex-editor-content-link{align-items:center;border-radius:2px;border:1px solid #dedfe3;padding:10px;display:flex;flex-direction:row;flex-wrap:nowrap;margin-top:10px;margin-bottom:10px}.squidex-editor-content-schema{display:block;overflow-x:hidden;overflow-y:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;min-width:0;width:auto;border-right:1px solid #c2c4cc;color:#8b8f9d;flex-shrink:0;padding-left:10px;padding-right:10px;width:200px}.squidex-editor-content-name{display:block;overflow-x:hidden;overflow-y:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;min-width:0;width:auto;padding-left:10px;padding-right:0}.squidex-editor-html{margin-bottom:10px;margin-top:10px;position:relative}.squidex-editor-html-label{left:6px;top:0;position:absolute;color:#8b8f9d;font-size:85%;font-weight:400}.squidex-editor-html textarea{font-family:monospace;padding:30px 20px 20px}.squidex-editor-button{background-color:#fff;border-radius:0;border:1px solid #dedfe3;bottom:10px;font-size:85%;font-weight:400;line-height:1;padding:6px 12px}.squidex-editor-button:hover{background-color:#f5f5f5}.squidex-editor-input{border:1px solid #dedfe3;border-radius:0;box-sizing:border-box;height:30px;margin-left:0;margin-right:5px;outline:none;padding:6px 12px}.squidex-editor-input:active{border-color:#3284f4}.squidex-editor-floating{border:1px solid #dedfe3}.squidex-editor-floating .MuiBox-root{margin-right:0!important}.squidex-editor-floating .MuiButtonBase-root{height:30px}.squidex-editor-modal-wrapper,.squidex-editor-modal-backdrop{bottom:0;left:0;right:0;top:0;position:absolute}.squidex-editor-modal-backdrop{background-color:#00000003}.squidex-editor-modal-window{left:50%;top:50%;background-color:#fff;border:0;border-radius:.25rem;box-sizing:border-box;box-shadow:0 3px 16px #0003;margin-top:-50px;margin-left:-150px;position:absolute;width:350px}.squidex-editor-modal-body{display:flex;flex-direction:row;flex-grow:1;padding-top:0!important}.squidex-editor-modal-body,.squidex-editor-modal-title{padding:15px}.squidex-editor-modal-title{font-size:85%}.squidex-editor-modal-window input{flex-grow:1}.b:before{content:"<a>";font-family:monospace;font-size:90%}.b:after{content:"</a>";font-family:monospace;font-size:90%}

250
backend/src/Squidex/wwwroot/editor/squidex-editor.js

File diff suppressed because one or more lines are too long

3
frontend/src/app/declarations.d.ts

@ -74,6 +74,9 @@ interface EditorProps {
// The name to the app.
appName: string;
// The class names.
classNames?: ReadonlyArray<string>;
// Called when the value has been changed.
onChange?: OnChange;

2
frontend/src/app/features/content/shared/forms/field-editor.component.html

@ -195,6 +195,7 @@
<ng-container *ngSwitchCase="'RichText'">
<sqx-rich-editor #editor
mode="Html"
[classNames]="field.rawProperties.classNames"
[formControl]="$any(fieldForm)"
[folderId]="field.rawProperties.folderId"
[hasChatBot]="hasChatBot"
@ -209,6 +210,7 @@
<ng-container *ngSwitchCase="'Markdown'">
<sqx-rich-editor #editor
mode="Markdown"
[classNames]="undefined"
[formControl]="$any(fieldForm)"
[folderId]="field.rawProperties.folderId"
[hasChatBot]="hasChatBot"

12
frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.html

@ -25,6 +25,18 @@
</div>
</div>
<div class="form-group row" [class.hidden]="hideClassNames | async">
<label class="col-3 col-form-label">{{ 'schemas.fieldTypes.string.classNames' | sqxTranslate }}</label>
<div class="col-9">
<sqx-tag-editor formControlName="classNames"></sqx-tag-editor>
<sqx-form-hint>
{{ 'schemas.fieldTypes.string.classNamesHint' | sqxTranslate }}
</sqx-form-hint>
</div>
</div>
<div class="form-group row" [class.hidden]="hideAllowedValues | async">
<label class="col-3 col-form-label">{{ 'schemas.field.allowedValues' | sqxTranslate }}</label>

4
frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.ts

@ -30,6 +30,7 @@ export class StringUIComponent {
public properties!: StringFieldPropertiesDto;
public hideAllowedValues?: Observable<boolean>;
public hideClassNames?: Observable<boolean>;
public hideInlineEditable?: Observable<boolean>;
public hideSchemaIds?: Observable<boolean>;
@ -47,6 +48,9 @@ export class StringUIComponent {
this.hideAllowedValues =
valueProjection$(editor, x => !(x && (x === 'Radio' || x === 'Dropdown')));
this.hideClassNames =
valueProjection$(editor, x => !(x && (x === 'RichText')));
this.hideInlineEditable =
valueProjection$(editor, x => !(x && (x === 'Input' || x === 'Dropdown' || x === 'Slug')));

6
frontend/src/app/shared/components/forms/rich-editor.component.ts

@ -48,6 +48,9 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
@Input()
public folderId = '';
@Input({ required: true })
public classNames?: ReadonlyArray<string>;
@Input({ required: true })
public mode: SquidexEditorMode = 'Html';
@ -128,6 +131,7 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
canSelectAIText: this.hasChatBot,
canSelectAssets: true,
canSelectContents: !!this.schemaIds,
classNames: this.classNames,
mode: this.mode,
});
});
@ -212,7 +216,7 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
}
private buildAsset(asset: AssetDto): Asset {
return { type: asset.mimeType, src: asset.fullUrl(this.apiUrl), fileName: asset.fileName };
return { ...asset, src: asset.fullUrl(this.apiUrl) };
}
private buildContent(content: ContentDto): Content {

1
frontend/src/app/shared/services/schemas.types.ts

@ -433,6 +433,7 @@ export class StringFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'String';
public readonly allowedValues?: ReadonlyArray<string>;
public readonly classNames?: ReadonlyArray<string>;
public readonly contentType?: StringContentType;
public readonly createEnum: boolean = false;
public readonly defaultValue?: string;

1
frontend/src/app/shared/state/schemas.forms.ts

@ -333,6 +333,7 @@ export class EditFieldFormVisitor implements FieldPropertiesVisitor<any> {
public visitString() {
this.config['allowedValues'] = new UntypedFormControl(undefined);
this.config['classNames'] = new UntypedFormControl(undefined);
this.config['contentType'] = new UntypedFormControl(undefined);
this.config['createEnum'] = new UntypedFormControl(undefined);
this.config['defaultValue'] = new UntypedFormControl(undefined);

Loading…
Cancel
Save