more editor fields

This commit is contained in:
2022-01-10 16:13:59 +01:00
parent 56a1f7cd62
commit 9d58e4c191
13 changed files with 201 additions and 20 deletions
@@ -5,7 +5,6 @@
</span>
<span class="ui-select-button-icon is-image" v-if="isImage">
<ui-thumbnail :media="source" :alt="icon" />
<img :src="source" :alt="icon" />
</span>
<div class="ui-select-button-content" v-if="label || description">
<strong class="ui-select-button-label" v-localize:html="{ key: label, tokens: tokens }"></strong>
+1 -1
View File
@@ -29,7 +29,7 @@ import * as zeroOptions from '../options';
import plugins from '../plugins.generated';
plugins.push(
countryPlugin, applicationPlugin, settingsPlugin, languagePlugin,
editorPlugin, countryPlugin, applicationPlugin, settingsPlugin, languagePlugin,
mediaPlugin, spacePlugin, pagePlugin, mailTemplatePlugin,
translationPlugin, integrationPlugin, userPlugin
);
+19 -1
View File
@@ -38,6 +38,15 @@ export interface ZeroCompiledEditorFieldset
}
export interface ZeroCompiledEditorChangeEvent
{
value: any;
oldValue: any;
model: any;
component: Component;
}
export interface ZeroCompiledEditorField
{
path: string;
@@ -58,6 +67,7 @@ export interface ZeroCompiledEditorField
sort: number;
columns: number;
preview?: ZeroEditorFieldFilterPreview;
onChange: (event: ZeroCompiledEditorChangeEvent) => void;
}
@@ -128,7 +138,15 @@ export function compileField(zero: Zero, editor: ZeroEditor, field: ZeroEditorFi
icon: field.configuration.preview.icon || 'fth-square',
selected: field.configuration.preview.selected || (x => !!x),
value: field.configuration.preview.value || (x => x ? x : null)
} : null
} : null,
onChange(event: ZeroCompiledEditorChangeEvent)
{
(field.configuration.changeHandlers || []).forEach(handler =>
{
handler(event);
});
}
} as ZeroCompiledEditorField;
+18 -2
View File
@@ -96,6 +96,17 @@ export class ZeroEditorFieldImpl implements ZeroEditorField
this.options = options;
return this;
}
/**
* The expression argument is called when the value of the field changes
* @param {function} callback - function which is called
*/
onChange(callback: Function): ZeroEditorField
{
this.configuration.changeHandlers.push(callback);
return this;
}
}
@@ -113,7 +124,8 @@ export function createDefaultFieldConfiguration(): ZeroEditorFieldConfiguration
classes: null,
horizontal: false,
sort: 0,
preview: undefined
preview: undefined,
changeHandlers: []
} as ZeroEditorFieldConfiguration;
}
@@ -163,7 +175,11 @@ export interface ZeroEditorFieldConfiguration
/**
* Sort order for fields within the editor canvas
**/
preview?: ZeroEditorFieldFilterPreview
preview?: ZeroEditorFieldFilterPreview,
/**
* Handlers which get called on value change
**/
changeHandlers?: Array<Function>
}
+6
View File
@@ -79,5 +79,11 @@ declare module 'zero/schemas'
* @param {T} [options] = Custom options to pass to this editor
*/
component(component: Component, options?: any): ZeroEditorField;
/**
* The expression argument is called when the value of the field changes
* @param {function} callback - function which is called
*/
onChange(callback: Function): ZeroEditorField;
}
}
@@ -16,4 +16,5 @@ export default function createFields(app: ZeroPluginOptions): void
app.fieldType('checklist', defineAsyncComponent(() => import('./checklist.vue')));
app.fieldType('inputlist', defineAsyncComponent(() => import('./inputlist.vue')));
app.fieldType('nested', defineAsyncComponent(() => import('./nested.vue')));
app.fieldType('datePicker', defineAsyncComponent(() => import('./datePicker.vue')));
}
@@ -0,0 +1,36 @@
<template>
<div class="ui-box">
<ui-datepicker :value="value" @input="$emit('input', $event)" :disabled="config.disabled"
v-bind="{ format, time: pickTime, maxDate, minDate, amPm }" />
</div>
</template>
<script>
export default {
props: {
value: [String, Date],
config: Object,
format: {
type: String,
default: null
},
pickTime: {
type: Boolean,
default: false,
},
maxDate: {
type: [String, Date],
default: null
},
minDate: {
type: [String, Date],
default: null
},
amPm: {
type: Boolean,
default: false
}
}
}
</script>
@@ -75,9 +75,24 @@ declare module 'zero/schemas'
* @param {NestedFieldOptions} options - Custom options
*/
nested(options: NestedFieldOptions): ZeroEditorField;
/**
* Create a date picker
* @param {DatePickerFieldOptions} options - Custom options
*/
datePicker(options?: DatePickerFieldOptions): ZeroEditorField;
}
export interface DatePickerFieldOptions
{
format?: string;
pickTime?: boolean;
maxDate?: string | Date;
minDate?: string | Date;
amPm?: boolean;
}
export interface ToggleFieldOptions
{
negative?: boolean | null;
@@ -151,15 +151,12 @@
this.$emit('input', this.value);
// TODO
//if (typeof this.field.configuration.onChange === 'function')
//{
// this.field.configuration.onChange(value, {
// oldValue,
// model: this.value,
// component: this
// });
//}
this.field.onChange({
value: value,
model: this.value,
oldValue,
component: this
})
},
@@ -3,7 +3,7 @@
<ui-trinity class="ui-editor-overlay">
<template v-slot:header>
<ui-header-bar :title="model.title" :back-button="false" :close-button="true" />
<ui-header-bar :title="model.title" :back-button="false" :close-button="true" @close="config.close(true)" />
</template>
<template v-slot:footer>
+21 -3
View File
@@ -71,7 +71,7 @@ export function confirmDelete(title?: string, text?: string, warningText?: strin
}
export function confirm(title: string, text?: string)
export function confirm(title: string, text?: string, softdismiss?: boolean)
{
return open({
alias: 'confirm',
@@ -80,10 +80,10 @@ export function confirm(title: string, text?: string)
text,
confirmLabel: '@ui.confirm',
confirmType: 'default',
closeLabel: '@ui.close',
closeLabel: '@ui.cancel',
},
autoclose: false,
softdismiss: true,
softdismiss: typeof softdismiss !== 'undefined' ? softdismiss : true,
component: () => import('../ui/overlays/confirm.vue')
});
}
@@ -104,6 +104,24 @@ export function message(title: string, text?: string)
}
export function editor(editorAlias: string, model: any, title?: string, options?: OverlayOptions)
{
return open(extendObject({
alias: 'editor',
display: 'editor',
model: {
editor: editorAlias,
value: model,
title: title || '@ui.edit.title',
},
autoclose: false,
softdismiss: false,
component: () => import('../editor/ui-editor-overlay.vue'),
width: 820
}, options || {}));
}
export function closeAll()
{
emitter.emit(event_closeOverlays);
@@ -43,7 +43,7 @@
.ui-tabs + .ui-box.is-connected
{
margin-top: -32px;
border-top: 1px solid var(--color-line-onbg);
border-top: 1px solid var(--color-line);
border-top-left-radius: 0;
border-top-right-radius: 0;
}
+76 -1
View File
@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Diagnostics;
using System.IO;
using System.Text.Encodings.Web;
@@ -19,6 +20,8 @@ public class RazorRenderer : IRazorRenderer, IDisposable
{
protected IRazorViewEngine ViewEngine { get; set; }
protected IRazorPageActivator PageActivator { get; set; }
protected ITempDataDictionaryFactory TempDataDictionaryFactory { get; set; }
protected IModelMetadataProvider ModelMetadataProvider { get; set; }
@@ -30,10 +33,11 @@ public class RazorRenderer : IRazorRenderer, IDisposable
protected HtmlHelperOptions HtmlHelperOptions { get; set; }
public RazorRenderer(IRazorViewEngine viewEngine, IHttpContextAccessor httpContextAccessor, ITempDataDictionaryFactory tempDataDictionaryFactory,
public RazorRenderer(IRazorViewEngine viewEngine, IRazorPageActivator pageActivator, IHttpContextAccessor httpContextAccessor, ITempDataDictionaryFactory tempDataDictionaryFactory,
IModelMetadataProvider modelMetadataProvider, IServiceProvider serviceProvider, IOptions<MvcViewOptions> mvcHelperOptions)
{
ViewEngine = viewEngine;
PageActivator = pageActivator;
HttpContextAccessor = httpContextAccessor;
TempDataDictionaryFactory = tempDataDictionaryFactory;
ModelMetadataProvider = modelMetadataProvider;
@@ -118,6 +122,43 @@ public class RazorRenderer : IRazorRenderer, IDisposable
}
/// <summary>
/// Renders a razor page to a string
/// </summary>
public async Task<string> PageAsync(string view, object model = null)
{
return await PageAsync(BuildActionContext(), view, model);
}
/// <summary>
/// Renders a razor page to a string
/// </summary>
public async Task<string> PageAsync(ActionContext context, string page, object model = null)
{
IRazorPage pageResult = FindPage(context, page);
using StringWriter stringWriter = new();
RazorView view = new RazorView(ViewEngine, PageActivator, new List<IRazorPage>(), pageResult, HtmlEncoder.Default, new DiagnosticListener("ViewRenderService"));
ViewContext viewContext = BuildViewContext(context, stringWriter, view);
viewContext.RouteData = context.RouteData;
viewContext.ViewData.Model = model;
Microsoft.AspNetCore.Mvc.RazorPages.Page razorPage = (Microsoft.AspNetCore.Mvc.RazorPages.Page)pageResult;
razorPage.PageContext = new Microsoft.AspNetCore.Mvc.RazorPages.PageContext(viewContext);
razorPage.ViewContext = viewContext;
PageActivator.Activate(razorPage, viewContext);
await razorPage.ExecuteAsync();
await stringWriter.FlushAsync();
return stringWriter.ToString();
}
/// <summary>
/// Renders a razor view to a string
/// </summary>
@@ -216,6 +257,30 @@ public class RazorRenderer : IRazorRenderer, IDisposable
}
/// <summary>
/// Tries to find a page
/// </summary>
protected virtual IRazorPage FindPage(ActionContext actionContext, string pageName)
{
RazorPageResult getPageResult = ViewEngine.GetPage(executingFilePath: null, pagePath: pageName);
if (getPageResult.Page != null)
{
return getPageResult.Page;
}
RazorPageResult findPageResult = ViewEngine.FindPage(actionContext, pageName: pageName);
if (findPageResult.Page != null)
{
return findPageResult.Page;
}
IEnumerable<string> searchedLocations = getPageResult.SearchedLocations.Concat(findPageResult.SearchedLocations);
string errorMessage = String.Join(Environment.NewLine, new[] { $"Unable to find page '{pageName}'. The following locations were searched:" }.Concat(searchedLocations));
throw new InvalidOperationException(errorMessage);
}
class GenericController : ControllerBase { }
@@ -271,6 +336,16 @@ public interface IRazorRenderer
/// </summary>
Task<string> ComponentAsync(string componentName, ActionContext context, object args = null);
/// <summary>
/// Renders a razor page to a string
/// </summary>
Task<string> PageAsync(string view, object model = null);
/// <summary>
/// Renders a razor page to a string
/// </summary>
Task<string> PageAsync(ActionContext context, string page, object model = null);
/// <summary>
/// Renders a razor view to a string
/// </summary>