-
+
+
@@ -25,34 +55,100 @@
},
data: () => ({
- opened: false
+ opened: false,
+ current: null,
+ area: null,
+ areas: [],
+ areaItems: [],
+ treeConfig: {
+ parent: null,
+ active: null,
+ onSelect: (ev) => { console.info(ev); }
+ },
+ link: null,
+ template: {
+ area: null,
+ target: 'default',
+ urlSuffix: null,
+ label: null,
+ title: null,
+ values: {}
+ },
+ showOptions: false
}),
- computed: {
- disabledIds()
+
+ watch: {
+ current()
{
- return this.config.disabledIds || [];
+ this.reloadSelector();
}
},
mounted()
{
+ this.areas = this.zero.config.linkPicker.areas;
+ this.areaItems = this.areas.map(x =>
+ {
+ return {
+ key: x.alias,
+ value: x.name
+ };
+ });
+ this.area = this.areas[0];
+ this.current = this.area.alias;
+
+ this.link = JSON.parse(JSON.stringify(this.template));
+ this.link.area = this.current;
+
setTimeout(() => this.opened = true, 300);
},
methods: {
+ reloadSelector()
+ {
+ this.area = this.areas.find(x => x.alias === this.current);
+
+ if (!this.opened)
+ {
+ return;
+ }
+ if (this.area.display === 'tree' && this.$refs.tree)
+ {
+ this.$refs.tree.refresh();
+ }
+ else if (this.area.display === 'list')
+ {
+
+ }
+ else if (this.area.display === 'media')
+ {
+
+ }
+ else
+ {
+ // custom
+ }
+ },
+
onSelect(item)
{
this.config.confirm(item);
},
- // get tree items
- getItems(parent)
+ // get list items
+ getListItems(search)
{
- return PageTreeApi.getChildren(parent, this.model).then(res =>
+
+ },
+
+ // get tree items
+ getTreeItems(parent)
+ {
+ return PageTreeApi.getChildren(parent, null).then(res =>
{
res = res.filter(x => x.id !== 'recyclebin');
@@ -62,55 +158,73 @@
{
item.isSelected = true;
}
- if (this.disabledIds.indexOf(item.id) > -1)
- {
- item.disabled = true;
- }
item.hasActions = false;
});
return res;
});
+ },
+
+
+ onTargetChange(ev)
+ {
+ this.link.target = ev ? 'blank' : 'default';
}
}
}
\ No newline at end of file
diff --git a/zero.Web.UI/App/components/tabs/tabs.vue b/zero.Web.UI/App/components/tabs/tabs.vue
index 12c2c6f8..41b6c6c7 100644
--- a/zero.Web.UI/App/components/tabs/tabs.vue
+++ b/zero.Web.UI/App/components/tabs/tabs.vue
@@ -149,7 +149,7 @@
color: var(--color-text);
background: var(--color-box);
box-shadow: var(--shadow-short);
- border-top: 3px solid var(--color-primary);
+ //border-top: 3px solid var(--color-primary);
.ui-tabs-list-item-count
{
diff --git a/zero.Web.UI/App/components/tree/tree.vue b/zero.Web.UI/App/components/tree/tree.vue
index 6accfca6..b46f2ad9 100644
--- a/zero.Web.UI/App/components/tree/tree.vue
+++ b/zero.Web.UI/App/components/tree/tree.vue
@@ -98,6 +98,7 @@
})
.catch(error =>
{
+ console.error(error);
this.setStatus('error', this.items, error);
// TODO handle errors
});
diff --git a/zero.Web.UI/App/core/link-provider.js b/zero.Web.UI/App/core/link-provider.js
new file mode 100644
index 00000000..118407e4
--- /dev/null
+++ b/zero.Web.UI/App/core/link-provider.js
@@ -0,0 +1,32 @@
+//import MediaApi from 'zero/api/media.js';
+//import Strings from 'zero/helpers/strings.js';
+//import Localization from 'zero/helpers/localization.js';
+//class LinkProvider
+//{
+// key;
+// label;
+// icon;
+// autoclose = true;
+// action = () =>
+// {
+// console.warn(`[zero] A list action needs a "action" callback`);
+// };
+// constructor(alias: string, name: string, type: string)
+// {
+// this.key = key;
+// this.label = label;
+// this.icon = icon;
+// this.action = action;
+// }
+// /**
+// * Calls the action
+// * @returns {ListColumn}
+// */
+// call(options)
+// {
+// console.info(options);
+// this.action(options);
+// }
+//}
+//export default ListAction;
+//# sourceMappingURL=link-provider.js.map
\ No newline at end of file
diff --git a/zero.Web.UI/App/core/link-provider.js.map b/zero.Web.UI/App/core/link-provider.js.map
new file mode 100644
index 00000000..af4749f5
--- /dev/null
+++ b/zero.Web.UI/App/core/link-provider.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"link-provider.js","sourceRoot":"","sources":["link-provider.ts"],"names":[],"mappings":"AACA,2CAA2C;AAC3C,gDAAgD;AAChD,0DAA0D;AAE1D,oBAAoB;AACpB,GAAG;AACH,QAAQ;AACR,UAAU;AACV,SAAS;AACT,qBAAqB;AACrB,kBAAkB;AAClB,KAAK;AACL,qEAAqE;AACrE,MAAM;AAEN,0DAA0D;AAC1D,KAAK;AACL,qBAAqB;AACrB,yBAAyB;AACzB,uBAAuB;AACvB,2BAA2B;AAC3B,KAAK;AAEL,OAAO;AACP,uBAAuB;AACvB,4BAA4B;AAC5B,OAAO;AACP,iBAAiB;AACjB,KAAK;AACL,4BAA4B;AAC5B,2BAA2B;AAC3B,KAAK;AACL,GAAG;AAGH,4BAA4B"}
\ No newline at end of file
diff --git a/zero.Web.UI/App/core/link-provider.ts b/zero.Web.UI/App/core/link-provider.ts
new file mode 100644
index 00000000..0ddde1e9
--- /dev/null
+++ b/zero.Web.UI/App/core/link-provider.ts
@@ -0,0 +1,37 @@
+
+//import MediaApi from 'zero/api/media.js';
+//import Strings from 'zero/helpers/strings.js';
+//import Localization from 'zero/helpers/localization.js';
+
+//class LinkProvider
+//{
+// key;
+// label;
+// icon;
+// autoclose = true;
+// action = () =>
+// {
+// console.warn(`[zero] A list action needs a "action" callback`);
+// };
+
+// constructor(alias: string, name: string, type: string)
+// {
+// this.key = key;
+// this.label = label;
+// this.icon = icon;
+// this.action = action;
+// }
+
+// /**
+// * Calls the action
+// * @returns {ListColumn}
+// */
+// call(options)
+// {
+// console.info(options);
+// this.action(options);
+// }
+//}
+
+
+//export default ListAction;
\ No newline at end of file
diff --git a/zero.Web.UI/App/core/options.js b/zero.Web.UI/App/core/options.js
index 46df02cc..f76c148f 100644
--- a/zero.Web.UI/App/core/options.js
+++ b/zero.Web.UI/App/core/options.js
@@ -12,5 +12,20 @@ export default {
images_natural: ['.jpg', '.jpeg', '.webp'],
images_artificial: ['.png', '.webp', '.svg']
}
+ },
+
+ linkPicker: {
+ areas: [
+ {
+ alias: 'zero.pages',
+ name: '@zero.config.linkareas.pages',
+ display: 'tree'
+ },
+ {
+ alias: 'zero.media',
+ name: '@zero.config.linkareas.media',
+ display: 'media'
+ }
+ ]
}
}
\ No newline at end of file
diff --git a/zero.Web.UI/App/editor/fields/linkpicker.vue b/zero.Web.UI/App/editor/fields/linkpicker.vue
index 9549ae43..390a2db9 100644
--- a/zero.Web.UI/App/editor/fields/linkpicker.vue
+++ b/zero.Web.UI/App/editor/fields/linkpicker.vue
@@ -1,5 +1,5 @@
-
+
@@ -18,6 +18,26 @@
type: Number,
default: 1
},
+ title: {
+ type: Boolean,
+ default: true
+ },
+ label: {
+ type: Boolean,
+ default: false
+ },
+ target: {
+ type: Boolean,
+ default: true
+ },
+ suffix: {
+ type: Boolean,
+ default: false
+ },
+ areas: {
+ type: Array,
+ default: () => []
+ },
config: Object
},
}
diff --git a/zero.Web.UI/Sass/Settings/_dimensions.scss b/zero.Web.UI/Sass/Settings/_dimensions.scss
index 333d2436..52db0b94 100644
--- a/zero.Web.UI/Sass/Settings/_dimensions.scss
+++ b/zero.Web.UI/Sass/Settings/_dimensions.scss
@@ -7,5 +7,5 @@
--padding-l: 64px;
--height-top: 74px;
- --radius: 3px;
+ --radius: 4px;
}
\ No newline at end of file
diff --git a/zero.Web.UI/app/core/editor-field.ts b/zero.Web.UI/app/core/editor-field.ts
index bc012a56..9399af7a 100644
--- a/zero.Web.UI/app/core/editor-field.ts
+++ b/zero.Web.UI/app/core/editor-field.ts
@@ -411,6 +411,11 @@ class EditorField
* Render a link picker
* @param {object} [options] - Custom options
* @param {number} [options.limit=1] - Limit of selection
+ * @param {boolean} [options.title=true] - Allow input of custom link title
+ * @param {boolean} [options.target=true] - Allow selection of the link target
+ * @param {boolean} [options.label=false] - Allow input of a custom label for button generation
+ * @param {boolean} [options.suffix=false] - Allow input of custom link URL suffix (query or hash)
+ * @param {string[]} [options.areas] - Limit link areas to the specified values (built-in are zero.pages, zero.media and zero.url)
* @returns {EditorField}
*/
linkPicker(options)
diff --git a/zero.Web.UI/app/core/plugin.ts b/zero.Web.UI/app/core/plugin.ts
index ed87246c..bc642dd9 100644
--- a/zero.Web.UI/app/core/plugin.ts
+++ b/zero.Web.UI/app/core/plugin.ts
@@ -7,6 +7,7 @@ class Plugin
routes = [];
editors = [];
lists = [];
+ linkAreas = [];
constructor(name)
@@ -54,9 +55,9 @@ class Plugin
/*
* Add a new vue list renderer to the global configuration
*/
- addList(config)
+ addList(list)
{
- this.lists.push(config);
+ this.lists.push(list);
}
@@ -87,6 +88,15 @@ class Plugin
{
routes.forEach(route => this.addRoute(route));
}
+
+
+ /*
+ * Add a new link area
+ */
+ addLinkArea(area)
+ {
+ this.linkAreas.push(area);
+ }
};
export default Plugin;
\ No newline at end of file
diff --git a/zero.Web.UI/app/core/zero.ts b/zero.Web.UI/app/core/zero.ts
index c8fc1134..9db734c0 100644
--- a/zero.Web.UI/app/core/zero.ts
+++ b/zero.Web.UI/app/core/zero.ts
@@ -128,6 +128,8 @@ class Zero
// append lists
plugin.lists.forEach(x => this.addOrReplace(this.#lists, x, 'alias'));
+ // append
+
console.log(`[zero] Installed %c${plugin.name}%cplugin`, 'font-style:italic;');
}
diff --git a/zero.Web/Controllers/LinkPickerController.cs b/zero.Web/Controllers/LinkPickerController.cs
new file mode 100644
index 00000000..df55dc0e
--- /dev/null
+++ b/zero.Web/Controllers/LinkPickerController.cs
@@ -0,0 +1,31 @@
+using Microsoft.AspNetCore.Mvc;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using zero.Core.Api;
+using zero.Core.Entities;
+
+namespace zero.Web.Controllers
+{
+ public class LinkPickerController : BackofficeController
+ {
+ IPageTreeApi Api;
+ IPagesApi PagesApi;
+
+ public LinkPickerController(IPageTreeApi api, IPagesApi pagesApi)
+ {
+ Api = api;
+ PagesApi = pagesApi;
+ }
+
+
+ public async Task
> GetChildren([FromQuery] string areaAlias, [FromQuery] string parent = null, [FromQuery] string active = null)
+ {
+ return await Api.GetChildren(parent, active);
+ }
+
+ //public async Task> GetPreviews([FromQuery] List ids)
+ //{
+ // return Previews(await PagesApi.GetByIds(ids.ToArray()), PreviewTransform);
+ //}
+ }
+}
diff --git a/zero.Web/Resources/Localization/zero.en-us.json b/zero.Web/Resources/Localization/zero.en-us.json
index bfb92d46..11dcaacb 100644
--- a/zero.Web/Resources/Localization/zero.en-us.json
+++ b/zero.Web/Resources/Localization/zero.en-us.json
@@ -1,6 +1,13 @@
{
"zero": {
- "name": "zero"
+ "name": "zero",
+
+ "config": {
+ "linkareas": {
+ "pages": "Pages",
+ "media": "Media"
+ }
+ }
},
"ui": {