enhance iconify usage and tune editor styles
This commit is contained in:
parent
3c8ac17617
commit
0a28944eea
|
|
@ -1,11 +1,64 @@
|
||||||
|
@mixin button-base {
|
||||||
|
appearance: none;
|
||||||
|
user-select: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
outline: 0;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin normal-button {
|
||||||
|
@include button-base;
|
||||||
|
|
||||||
|
background-color: #eee;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 0px 8px;
|
||||||
|
line-height: 22px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #e1e1e1;
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
border-color: #555;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin text-button {
|
||||||
|
@include button-base;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1;
|
||||||
|
color: #555;
|
||||||
|
&:hover {
|
||||||
|
border-bottom: 1px solid #999;
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
border-color: transparent;
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.editor-container {
|
.editor-container {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
--gap: 8px;
|
--gap: 8px;
|
||||||
|
--blue-0: #D0EBFF;
|
||||||
|
--blue-1: #A5D8FF;
|
||||||
|
--red-0: #FFE3E3;
|
||||||
|
--red-1: #FFC9C9;
|
||||||
|
--yellow-0: #FFF3BF;
|
||||||
|
--yellow-1: #FFEC99;
|
||||||
|
--green-0: #D3F9D8;
|
||||||
|
--green-1: #B2F2BB;
|
||||||
|
|
||||||
h1 {}
|
h1 {}
|
||||||
h2 {}
|
h2 {}
|
||||||
h3 {}
|
h3 {
|
||||||
|
margin: 12px 0 12px 0;
|
||||||
|
}
|
||||||
p {
|
p {
|
||||||
margin: 0 0 var(--gap) 0;
|
margin: 0 0 var(--gap) 0;
|
||||||
}
|
}
|
||||||
|
|
@ -18,12 +71,46 @@
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
padding: var(--gap);
|
padding: var(--gap);
|
||||||
}
|
}
|
||||||
|
input[type=text],
|
||||||
|
input[type=email],
|
||||||
|
textarea {
|
||||||
|
border: 1px solid #888;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
@include normal-button;
|
||||||
|
|
||||||
|
margin-right: var(--gap);
|
||||||
|
// last-of-type should not be used here
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 14px;
|
||||||
|
position: relative;
|
||||||
|
bottom: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iconify-icon {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.je-header {
|
||||||
|
label {
|
||||||
|
font-weight: 700;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// override theme-default
|
// override theme-default
|
||||||
.je-indented-panel {
|
.je-indented-panel {
|
||||||
padding-left: var(--gap);
|
padding-left: calc(var(--gap) * 2);
|
||||||
margin-left: var(--gap);
|
padding-bottom: var(--gap);
|
||||||
border-left: 1px solid #ccc
|
margin-left: 0;
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
margin-bottom: var(--gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
.je-indented-panel--top {
|
.je-indented-panel--top {
|
||||||
|
|
@ -31,10 +118,6 @@
|
||||||
margin-left: var(--gap);
|
margin-left: var(--gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
.je-object__container {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
margin-bottom: var(--gap);
|
margin-bottom: var(--gap);
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +125,7 @@
|
||||||
.je-form-input-label {
|
.je-form-input-label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: calc(var(--gap) / 2);
|
margin-bottom: calc(var(--gap) / 2);
|
||||||
font-weight: bold;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.je-form-input-description {
|
.je-form-input-description {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
@ -63,6 +146,36 @@
|
||||||
border-bottom: 1px solid #ccc;
|
border-bottom: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* buttons */
|
||||||
|
.je-object__controls {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
margin-left: calc(var(--gap) * 2);
|
||||||
|
top: 2px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
@include text-button;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.json-editor-btn-add {
|
||||||
|
background-color: var(--green-0);
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--green-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.json-editor-btn-subtract {
|
||||||
|
background-color: var(--yellow-0);
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--yellow-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.json-editor-btn-delete {
|
||||||
|
background-color: var(--red-0);
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--red-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.je-textarea {
|
.je-textarea {
|
||||||
height: 150px;
|
height: 150px;
|
||||||
min-height: 150px;
|
min-height: 150px;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { AbstractIconLib } from '@json-editor/json-editor/src/iconlib.js';
|
||||||
|
|
||||||
|
import { getIconSVG } from '../icons';
|
||||||
|
|
||||||
|
const iconMapping = {
|
||||||
|
collapse: 'mdi:chevron-down',
|
||||||
|
expand: 'mdi:chevron-right',
|
||||||
|
delete: 'mdi:delete',
|
||||||
|
edit: 'mdi:pen',
|
||||||
|
add: 'mdi:plus',
|
||||||
|
subtract: 'mdi:minus',
|
||||||
|
cancel: 'mdi:cancel',
|
||||||
|
save: 'mdi:content-save',
|
||||||
|
moveup: 'mdi:arrow-up',
|
||||||
|
moveright: 'mdi:arrow-right',
|
||||||
|
movedown: 'mdi:arrow-down',
|
||||||
|
moveleft: 'mdi:arrow-left',
|
||||||
|
copy: 'mdi:content-copy',
|
||||||
|
clear: 'mdi:close-circle',
|
||||||
|
time: 'mdi:clock',
|
||||||
|
calendar: 'mdi:calendar',
|
||||||
|
edit_properties: 'mdi:format-list-bulleted',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class MyIconLib extends AbstractIconLib {
|
||||||
|
|
||||||
|
getIcon(key) {
|
||||||
|
const svg = getIconSVG(iconMapping[key], {dom: true})
|
||||||
|
return svg
|
||||||
|
// const i = document.createElement('iconify-icon')
|
||||||
|
// i.setAttribute('icon', )
|
||||||
|
// return i
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerIconLib(JSONEditor) {
|
||||||
|
JSONEditor.defaults.iconlibs['myiconlib'] = MyIconLib
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
|
import 'iconify-icon'; // import only
|
||||||
|
|
||||||
import objectPath from 'object-path';
|
import objectPath from 'object-path';
|
||||||
|
|
||||||
import { JSONEditor } from '@json-editor/json-editor/dist/jsoneditor';
|
import { JSONEditor } from '@json-editor/json-editor/dist/jsoneditor';
|
||||||
|
|
||||||
import * as exampleData from '../sample.resume.json';
|
import * as exampleData from '../sample.resume.json';
|
||||||
import * as jsoncvSchema from '../schema/jsoncv.schema.json';
|
import * as jsoncvSchemaModule from '../schema/jsoncv.schema.json';
|
||||||
|
import { registerIconLib } from './iconlib';
|
||||||
import { registerTheme } from './theme';
|
import { registerTheme } from './theme';
|
||||||
import { createElement } from './utils';
|
import {
|
||||||
|
createElement,
|
||||||
|
traverseDownObject,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
const propertiesInOrder = ['basics', 'education', 'work', 'skills', 'projects', 'languages', 'interests', 'references', 'awards', 'publications', 'volunteer']
|
const propertiesInOrder = ['basics', 'education', 'work', 'skills', 'projects', 'languages', 'interests', 'references', 'awards', 'publications', 'volunteer']
|
||||||
const basicsPropertiesInOrder = ['name', 'label', 'email', 'phone', 'url', 'summary', 'image', 'location', 'profiles']
|
const basicsPropertiesInOrder = ['name', 'label', 'email', 'phone', 'url', 'summary', 'image', 'location', 'profiles']
|
||||||
|
|
@ -19,7 +25,9 @@ const basicsUl = createElement('ul', {
|
||||||
parent: tocUl
|
parent: tocUl
|
||||||
})
|
})
|
||||||
|
|
||||||
const attrSchemaPathTo = 'data-schemapath-to'
|
|
||||||
|
// copy the object to remove the readonly restriction on module
|
||||||
|
const jsoncvSchema = {...jsoncvSchemaModule.default}
|
||||||
|
|
||||||
// add propertyOrder to schema, and add links to toc
|
// add propertyOrder to schema, and add links to toc
|
||||||
propertiesInOrder.forEach((name, index) => {
|
propertiesInOrder.forEach((name, index) => {
|
||||||
|
|
@ -50,6 +58,15 @@ basicsPropertiesInOrder.forEach((name, index) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// add headerTemplate for each type:array in schema
|
||||||
|
traverseDownObject(jsoncvSchema, (key, obj) => {
|
||||||
|
let noun = key
|
||||||
|
if (noun.endsWith('s')) noun = noun.slice(0, -1)
|
||||||
|
if (obj.type === 'array' && obj.items) {
|
||||||
|
obj.items.headerTemplate = `${noun} {{i1}}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// add format to schema
|
// add format to schema
|
||||||
const keyFormatMap = {
|
const keyFormatMap = {
|
||||||
'basics.properties.summary': 'textarea',
|
'basics.properties.summary': 'textarea',
|
||||||
|
|
@ -58,12 +75,17 @@ for (const [key, format] of Object.entries(keyFormatMap)) {
|
||||||
objectPath.get(jsoncvSchema.properties, key).format = format
|
objectPath.get(jsoncvSchema.properties, key).format = format
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// change schema title
|
||||||
|
jsoncvSchema.title = 'Resume'
|
||||||
|
|
||||||
// initialize editor
|
// initialize editor
|
||||||
registerTheme(JSONEditor)
|
registerTheme(JSONEditor)
|
||||||
|
registerIconLib(JSONEditor)
|
||||||
const elEditorContainer = document.querySelector('.editor-container')
|
const elEditorContainer = document.querySelector('.editor-container')
|
||||||
const editor = new JSONEditor(elEditorContainer, {
|
const editor = new JSONEditor(elEditorContainer, {
|
||||||
schema: jsoncvSchema,
|
schema: jsoncvSchema,
|
||||||
theme: 'mytheme',
|
theme: 'mytheme',
|
||||||
|
iconlib: 'myiconlib',
|
||||||
});
|
});
|
||||||
editor.on('ready',() => {
|
editor.on('ready',() => {
|
||||||
editor.setValue(exampleData)
|
editor.setValue(exampleData)
|
||||||
|
|
@ -72,6 +94,5 @@ editor.on('ready',() => {
|
||||||
document.querySelectorAll('[data-schemapath]').forEach(el => {
|
document.querySelectorAll('[data-schemapath]').forEach(el => {
|
||||||
const schemapath = el.getAttribute('data-schemapath')
|
const schemapath = el.getAttribute('data-schemapath')
|
||||||
el.id = schemapath
|
el.id = schemapath
|
||||||
console.log('el', schemapath)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,13 @@ export const createElement = function(tagName, {className, text, attrs, parent})
|
||||||
}
|
}
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const traverseDownObject = function(obj, callback) {
|
||||||
|
for (const key in obj) {
|
||||||
|
const value = obj[key]
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
callback(key, value)
|
||||||
|
traverseDownObject(value, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,11 @@
|
||||||
"name": "jsoncv",
|
"name": "jsoncv",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@iconify/json": "^2.2.15",
|
||||||
"ajv": "^8.12.0",
|
"ajv": "^8.12.0",
|
||||||
"ajv-formats": "^2.1.1",
|
"ajv-formats": "^2.1.1",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"iconify-icon": "^1.0.0",
|
"iconify-icon": "^1.0.3",
|
||||||
"object-path": "^0.11.8"
|
"object-path": "^0.11.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -86,6 +87,15 @@
|
||||||
"@iconify/types": "*"
|
"@iconify/types": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@iconify/json": {
|
||||||
|
"version": "2.2.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@iconify/json/-/json-2.2.15.tgz",
|
||||||
|
"integrity": "sha512-+BVLIjTJpBiEOGD3xhCY7/ajH+7QTl/jzF59gf9Hf5y/HyU8D+HUmOsXEGLIsCZErEQB66wZ36AziOlYf3wuPA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@iconify/types": "*",
|
||||||
|
"pathe": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@iconify/types": {
|
"node_modules/@iconify/types": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
|
||||||
|
|
@ -847,14 +857,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/iconify-icon": {
|
"node_modules/iconify-icon": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-1.0.3.tgz",
|
||||||
"integrity": "sha512-UY4PDCKQPpIGgDIx2yxM8wiOdMdLz0mb93dTV0Ox5hThwO8OA2zy8gDmjRReKqkPHK/mY7p/ivDaDGHE8O9xIw==",
|
"integrity": "sha512-pyWLbx8IBfD2G3M0hULuvUBwoowrZtXEKyeAXhD2AlbNYTSDPmWGhgPYaRAnVIuBjMAZ4dCyEHmaYgnlDZc0XQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/types": "^2.0.0"
|
"@iconify/types": "^2.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "http://github.com/sponsors/cyberalien"
|
"url": "https://github.com/sponsors/cyberalien"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/immutable": {
|
"node_modules/immutable": {
|
||||||
|
|
@ -1196,6 +1206,11 @@
|
||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/pathe": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w=="
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
|
|
@ -1975,6 +1990,15 @@
|
||||||
"@iconify/types": "*"
|
"@iconify/types": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@iconify/json": {
|
||||||
|
"version": "2.2.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@iconify/json/-/json-2.2.15.tgz",
|
||||||
|
"integrity": "sha512-+BVLIjTJpBiEOGD3xhCY7/ajH+7QTl/jzF59gf9Hf5y/HyU8D+HUmOsXEGLIsCZErEQB66wZ36AziOlYf3wuPA==",
|
||||||
|
"requires": {
|
||||||
|
"@iconify/types": "*",
|
||||||
|
"pathe": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@iconify/types": {
|
"@iconify/types": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
|
||||||
|
|
@ -2436,9 +2460,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"iconify-icon": {
|
"iconify-icon": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-1.0.3.tgz",
|
||||||
"integrity": "sha512-UY4PDCKQPpIGgDIx2yxM8wiOdMdLz0mb93dTV0Ox5hThwO8OA2zy8gDmjRReKqkPHK/mY7p/ivDaDGHE8O9xIw==",
|
"integrity": "sha512-pyWLbx8IBfD2G3M0hULuvUBwoowrZtXEKyeAXhD2AlbNYTSDPmWGhgPYaRAnVIuBjMAZ4dCyEHmaYgnlDZc0XQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@iconify/types": "^2.0.0"
|
"@iconify/types": "^2.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -2692,6 +2716,11 @@
|
||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"pathe": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w=="
|
||||||
|
},
|
||||||
"picocolors": {
|
"picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,11 @@
|
||||||
"vite-plugin-handlebars": "^1.6.0"
|
"vite-plugin-handlebars": "^1.6.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@iconify/json": "^2.2.15",
|
||||||
"ajv": "^8.12.0",
|
"ajv": "^8.12.0",
|
||||||
"ajv-formats": "^2.1.1",
|
"ajv-formats": "^2.1.1",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"iconify-icon": "^1.0.0",
|
"iconify-icon": "^1.0.3",
|
||||||
"object-path": "^0.11.8"
|
"object-path": "^0.11.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue