more editor fields
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user