native-document 1.0.166 → 1.0.169
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vitepress/config.js +166 -0
- package/CHANGELOG.md +187 -0
- package/components.js +4 -1
- package/dist/native-document.components.min.js +593 -300
- package/dist/native-document.dev.js +99 -75
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/advanced-components.md +213 -608
- package/docs/anchor.md +173 -312
- package/docs/cache.md +95 -803
- package/docs/cli.md +179 -0
- package/docs/components/accordion.md +172 -0
- package/docs/components/alert.md +99 -0
- package/docs/components/avatar.md +160 -0
- package/docs/components/badge.md +102 -0
- package/docs/components/breadcrumb.md +89 -0
- package/docs/components/button.md +183 -0
- package/docs/components/card.md +69 -0
- package/docs/components/context-menu.md +118 -0
- package/docs/components/data-table.md +345 -0
- package/docs/components/dropdown.md +214 -0
- package/docs/components/form/autocomplete-field.md +81 -0
- package/docs/components/form/checkbox-field.md +41 -0
- package/docs/components/form/checkbox-group-field.md +54 -0
- package/docs/components/form/color-field.md +64 -0
- package/docs/components/form/date-field.md +92 -0
- package/docs/components/form/field-collection.md +63 -0
- package/docs/components/form/file-field.md +203 -0
- package/docs/components/form/form-control.md +87 -0
- package/docs/components/form/image-field.md +90 -0
- package/docs/components/form/index.md +115 -0
- package/docs/components/form/number-field.md +65 -0
- package/docs/components/form/radio-field.md +51 -0
- package/docs/components/form/select-field.md +123 -0
- package/docs/components/form/slider.md +136 -0
- package/docs/components/form/string-field.md +134 -0
- package/docs/components/form/textarea-field.md +65 -0
- package/docs/components/form-fields.md +372 -0
- package/docs/components/getting-started.md +264 -0
- package/docs/components/icons.md +321 -0
- package/docs/components/index.md +337 -0
- package/docs/components/layout.md +279 -0
- package/docs/components/list.md +73 -0
- package/docs/components/menu.md +215 -0
- package/docs/components/modal.md +156 -0
- package/docs/components/pagination.md +95 -0
- package/docs/components/popover.md +131 -0
- package/docs/components/progress.md +111 -0
- package/docs/components/shortcut-manager.md +221 -0
- package/docs/components/simple-table.md +107 -0
- package/docs/components/skeleton.md +155 -0
- package/docs/components/spinner.md +100 -0
- package/docs/components/splitter.md +133 -0
- package/docs/components/stepper.md +163 -0
- package/docs/components/switch.md +113 -0
- package/docs/components/tabs.md +153 -0
- package/docs/components/toast.md +119 -0
- package/docs/components/tooltip.md +151 -0
- package/docs/components/traits.md +261 -0
- package/docs/conditional-rendering.md +170 -588
- package/docs/contributing.md +300 -25
- package/docs/core-concepts.md +205 -374
- package/docs/elements.md +251 -367
- package/docs/extending-native-document-element.md +192 -207
- package/docs/filters.md +153 -1122
- package/docs/getting-started.md +193 -267
- package/docs/i18n.md +241 -0
- package/docs/index.md +76 -0
- package/docs/lifecycle-events.md +143 -75
- package/docs/list-rendering.md +227 -852
- package/docs/memory-management.md +134 -47
- package/docs/native-document-element.md +337 -186
- package/docs/native-fetch.md +99 -630
- package/docs/observable-resource.md +364 -0
- package/docs/observables.md +592 -526
- package/docs/routing.md +244 -653
- package/docs/state-management.md +134 -241
- package/docs/svg-elements.md +231 -0
- package/docs/theming.md +409 -0
- package/docs/tutorials/.gitkeep +0 -0
- package/docs/validation.md +95 -97
- package/docs/vitepress-conventions.md +219 -0
- package/package.json +34 -13
- package/readme.md +269 -89
- package/src/components/card/Card.js +93 -39
- package/src/components/card/index.js +1 -1
- package/src/components/form/field/types/FileField.js +3 -3
- package/src/components/icon/Icon.js +107 -0
- package/src/components/icon/icon-getters.js +142 -0
- package/src/components/icon/icons.js +171 -0
- package/src/components/icon/index.js +17 -0
- package/src/components/icon/types/Icon.d.ts +191 -0
- package/src/components/index.d.ts +6 -1
- package/src/components/list/HasListItem.js +171 -0
- package/src/components/list/List.js +41 -107
- package/src/components/list/ListDivider.js +39 -0
- package/src/components/list/ListGroup.js +76 -59
- package/src/components/list/ListItem.js +117 -69
- package/src/components/list/index.js +3 -1
- package/src/components/list/types/List.d.ts +45 -32
- package/src/components/list/types/ListDivider.ts +16 -0
- package/src/components/list/types/ListGroup.d.ts +51 -29
- package/src/components/list/types/ListItem.d.ts +40 -30
- package/src/components/spacer/Spacer.js +1 -1
- package/src/core/data/ObservableResource.js +5 -0
- package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
- package/src/core/elements/content-formatter.js +16 -2
- package/src/core/elements/form.js +1 -1
- package/src/core/elements/img.js +1 -1
- package/src/core/elements/medias.js +2 -2
- package/src/core/elements/meta-data.js +1 -1
- package/src/core/wrappers/AttributesWrapper.js +37 -22
- package/src/core/wrappers/ElementCreator.js +9 -16
- package/src/core/wrappers/HtmlElementWrapper.js +26 -7
- package/src/core/wrappers/NDElement.js +12 -1
- package/src/core/wrappers/prototypes/attributes-extensions.js +24 -24
- package/src/core/wrappers/prototypes/nd-element-extensions.js +1 -8
- package/src/ui/components/card/CardRender.js +133 -0
- package/src/ui/components/card/card.css +169 -0
- package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
- package/src/ui/components/icon/material/MaterialIconRender.js +71 -0
- package/src/ui/components/icon/material/material.css +15 -0
- package/src/ui/components/icon/material/material.map.js +170 -0
- package/src/ui/components/icon/phosphor/PhosphorIconRender.js +71 -0
- package/src/ui/components/icon/phosphor/phosphor.css +17 -0
- package/src/ui/components/icon/phosphor/phosphor.map.js +165 -0
- package/src/ui/components/icon/tabler/TablerIconRender.js +59 -0
- package/src/ui/components/icon/tabler/tabler.css +14 -0
- package/src/ui/components/icon/tabler/tabler.map.js +165 -0
- package/src/ui/components/list/ListRender.js +18 -0
- package/src/ui/components/list/divider/ListDividerRender.js +10 -0
- package/src/ui/components/list/divider/list-divider.css +12 -0
- package/src/ui/components/list/group/ListGroupRender.js +61 -0
- package/src/ui/components/list/group/list-group.css +62 -0
- package/src/ui/components/list/item/ListItemRender.js +238 -0
- package/src/ui/components/list/item/list-item.css +191 -0
- package/src/ui/components/list/list.css +24 -0
- package/src/ui/components/spacer/SpacerRender.js +10 -0
- package/src/ui/index.js +13 -0
package/src/core/elements/img.js
CHANGED
|
@@ -6,7 +6,7 @@ import NativeDocumentError from '../errors/NativeDocumentError';
|
|
|
6
6
|
* Creates an `<img>` element.
|
|
7
7
|
* @type {function(ImgAttributes=): HTMLImageElement}
|
|
8
8
|
*/
|
|
9
|
-
export const BaseImage = HtmlElementWrapper('img');
|
|
9
|
+
export const BaseImage = HtmlElementWrapper('img', null, true);
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Creates an `<img>` element.
|
|
@@ -16,13 +16,13 @@ export const Video = HtmlElementWrapper('video');
|
|
|
16
16
|
* Creates a `<source>` element.
|
|
17
17
|
* @type {function(SourceAttributes=): HTMLSourceElement}
|
|
18
18
|
*/
|
|
19
|
-
export const Source = HtmlElementWrapper('source');
|
|
19
|
+
export const Source = HtmlElementWrapper('source', null, true);
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Creates a `<track>` element.
|
|
23
23
|
* @type {function(TrackAttributes=): HTMLTrackElement}
|
|
24
24
|
*/
|
|
25
|
-
export const Track = HtmlElementWrapper('track');
|
|
25
|
+
export const Track = HtmlElementWrapper('track', null, true);
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Creates a `<canvas>` element.
|
|
@@ -14,27 +14,31 @@ import {Observable} from '../data/Observable';
|
|
|
14
14
|
export const bindClassAttribute = (element, data) => {
|
|
15
15
|
for(const className in data) {
|
|
16
16
|
const value = data[className];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
17
|
+
|
|
18
|
+
if (value.__$isObservableChecker) {
|
|
19
|
+
let lastClass = value.val();
|
|
20
|
+
if (typeof lastClass === 'string') {
|
|
21
|
+
element.classes.toggle(lastClass, true);
|
|
22
|
+
value.subscribe((currentValue) => {
|
|
23
|
+
element.classes.remove(lastClass);
|
|
24
|
+
element.classes.toggle(currentValue, true);
|
|
25
|
+
lastClass = currentValue;
|
|
26
|
+
});
|
|
27
|
+
continue;
|
|
29
28
|
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (value.__$Observable) {
|
|
30
32
|
element.classes.toggle(className, value.val());
|
|
31
33
|
value.subscribe((shouldAdd) => element.classes.toggle(className, shouldAdd));
|
|
32
34
|
continue;
|
|
33
35
|
}
|
|
34
|
-
|
|
36
|
+
|
|
37
|
+
if (value.$hydrate) {
|
|
35
38
|
value.$hydrate(element, className);
|
|
36
39
|
continue;
|
|
37
40
|
}
|
|
41
|
+
|
|
38
42
|
element.classes.toggle(className, value);
|
|
39
43
|
}
|
|
40
44
|
};
|
|
@@ -92,12 +96,12 @@ export const bindStyleAttribute = (element, data) => {
|
|
|
92
96
|
* @param {boolean|number|Observable} value
|
|
93
97
|
*/
|
|
94
98
|
export const bindBooleanAttribute = (element, attributeName, value) => {
|
|
95
|
-
const isObservable = value.__$
|
|
99
|
+
const isObservable = value.__$Observable;
|
|
96
100
|
const defaultValue = isObservable? value.val() : value;
|
|
97
101
|
|
|
98
102
|
const attributeRealName = BOOL_ATTRIBUTES_NAME[attributeName];
|
|
99
103
|
|
|
100
|
-
if(
|
|
104
|
+
if(typeof defaultValue === 'boolean') {
|
|
101
105
|
element[attributeRealName] = defaultValue;
|
|
102
106
|
}
|
|
103
107
|
else {
|
|
@@ -126,15 +130,14 @@ export const bindBooleanAttribute = (element, attributeName, value) => {
|
|
|
126
130
|
* @param {Observable} value
|
|
127
131
|
*/
|
|
128
132
|
export const bindAttributeWithObservable = (element, attributeName, value) => {
|
|
129
|
-
const applyValue = attributeName === 'value' ? (newValue) => element.value = newValue : (newValue) => element.setAttribute(attributeName, newValue);
|
|
130
|
-
value.subscribe(applyValue);
|
|
131
|
-
|
|
132
133
|
if(attributeName === 'value') {
|
|
133
134
|
element.value = value.val();
|
|
134
135
|
element.addEventListener('input', () => value.set(element.value));
|
|
136
|
+
value.subscribe((newValue) => element.value = newValue);
|
|
135
137
|
return;
|
|
136
138
|
}
|
|
137
139
|
element.setAttribute(attributeName, value.val());
|
|
140
|
+
value.subscribe((newValue) => element.setAttribute(attributeName, newValue));
|
|
138
141
|
};
|
|
139
142
|
|
|
140
143
|
/**
|
|
@@ -149,16 +152,25 @@ const AttributesWrapper = (element, attributes) => {
|
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
for(const originalAttributeName in attributes) {
|
|
152
|
-
const attributeName = originalAttributeName.toLowerCase();
|
|
153
155
|
const value = attributes[originalAttributeName];
|
|
154
156
|
if(value == null) {
|
|
155
157
|
continue;
|
|
156
158
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
const type = typeof value;
|
|
160
|
+
if(type === 'string' || type === 'number') {
|
|
161
|
+
element.setAttribute(originalAttributeName, value);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const attributeName = originalAttributeName.toLowerCase();
|
|
165
|
+
if(value.__$Observable) {
|
|
166
|
+
if(BOOLEAN_ATTRIBUTES.has(attributeName)) {
|
|
167
|
+
bindBooleanAttribute(element, attributeName, value);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
bindAttributeWithObservable(element, originalAttributeName, value);
|
|
159
171
|
continue;
|
|
160
172
|
}
|
|
161
|
-
if(
|
|
173
|
+
if(type === 'object') {
|
|
162
174
|
if(attributeName === 'class') {
|
|
163
175
|
bindClassAttribute(element, value);
|
|
164
176
|
continue;
|
|
@@ -172,6 +184,9 @@ const AttributesWrapper = (element, attributes) => {
|
|
|
172
184
|
bindBooleanAttribute(element, attributeName, value);
|
|
173
185
|
continue;
|
|
174
186
|
}
|
|
187
|
+
if(value.__$isTemplateBinding) {
|
|
188
|
+
value.$hydrate(element, attributeName);
|
|
189
|
+
}
|
|
175
190
|
|
|
176
191
|
element.setAttribute(attributeName, value);
|
|
177
192
|
}
|
|
@@ -89,30 +89,23 @@ export const ElementCreator = {
|
|
|
89
89
|
|
|
90
90
|
},
|
|
91
91
|
getChild: (child) => {
|
|
92
|
-
if(child == null)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if(child
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return child;
|
|
100
|
-
}
|
|
101
|
-
} while (child.toNdElement);
|
|
92
|
+
if (child == null) return null;
|
|
93
|
+
|
|
94
|
+
child = child.toNdElement();
|
|
95
|
+
if (child instanceof Node) return child;
|
|
96
|
+
|
|
97
|
+
while (child != null && !(child instanceof Node)) {
|
|
98
|
+
child = child.toNdElement?.();
|
|
102
99
|
}
|
|
103
100
|
|
|
104
|
-
return
|
|
101
|
+
return child instanceof Node ? child : null;
|
|
105
102
|
},
|
|
106
103
|
/**
|
|
107
104
|
*
|
|
108
105
|
* @param {HTMLElement} element
|
|
109
106
|
* @param {Object} attributes
|
|
110
107
|
*/
|
|
111
|
-
processAttributes:
|
|
112
|
-
if (attributes) {
|
|
113
|
-
AttributesWrapper(element, attributes);
|
|
114
|
-
}
|
|
115
|
-
},
|
|
108
|
+
processAttributes: AttributesWrapper,
|
|
116
109
|
/**
|
|
117
110
|
*
|
|
118
111
|
* @param {HTMLElement} element
|
|
@@ -3,7 +3,6 @@ import {ElementCreator} from './ElementCreator';
|
|
|
3
3
|
import {normalizeComponentArgs} from '../utils/args-types';
|
|
4
4
|
import './NdPrototype';
|
|
5
5
|
|
|
6
|
-
import './NdPrototype';
|
|
7
6
|
import '../../core/utils/prototypes.js';
|
|
8
7
|
import '../wrappers/prototypes/nd-element-extensions';
|
|
9
8
|
import '../wrappers/prototypes/bind-class-extensions';
|
|
@@ -27,6 +26,21 @@ export const createTextNode = (value) => {
|
|
|
27
26
|
};
|
|
28
27
|
|
|
29
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Applies attributes to an existing HTMLElement.
|
|
31
|
+
* Used internally by HtmlElementWrapper on each cloned node.
|
|
32
|
+
*
|
|
33
|
+
* @internal
|
|
34
|
+
* @param {HTMLElement} element - Element to configure
|
|
35
|
+
* @param {Object|null} attributes - Attributes object or children if no attrs provided
|
|
36
|
+
* @returns {HTMLElement} The configured element
|
|
37
|
+
*/
|
|
38
|
+
export const createVoidHtmlElement = (element, attributes) => {
|
|
39
|
+
ElementCreator.processAttributes(element, attributes);
|
|
40
|
+
return element;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const OBJECT_PROTOTYPE = Object.prototype;
|
|
30
44
|
/**
|
|
31
45
|
* Applies attributes and children to an existing HTMLElement.
|
|
32
46
|
* Used internally by HtmlElementWrapper on each cloned node.
|
|
@@ -38,7 +52,11 @@ export const createTextNode = (value) => {
|
|
|
38
52
|
* @returns {HTMLElement} The configured element
|
|
39
53
|
*/
|
|
40
54
|
export const createHtmlElement = (element, _attributes, _children = null) => {
|
|
41
|
-
|
|
55
|
+
let attributes = _attributes, children = _children;
|
|
56
|
+
if((!_attributes || !_children) && (typeof _attributes !== 'object' || Array.isArray(_attributes) || _attributes === null || Object.getPrototypeOf(_attributes) !== OBJECT_PROTOTYPE || _attributes.$hydrate)) { // IF it's not a JSON
|
|
57
|
+
attributes = _children;
|
|
58
|
+
children = _attributes;
|
|
59
|
+
}
|
|
42
60
|
|
|
43
61
|
ElementCreator.processAttributes(element, attributes);
|
|
44
62
|
ElementCreator.processChildren(children, element);
|
|
@@ -63,16 +81,17 @@ export const createHtmlElement = (element, _attributes, _children = null) => {
|
|
|
63
81
|
* return el;
|
|
64
82
|
* });
|
|
65
83
|
*/
|
|
66
|
-
export default function HtmlElementWrapper(name, customWrapper = null) {
|
|
84
|
+
export default function HtmlElementWrapper(name, customWrapper = null, isVoid = false) {
|
|
85
|
+
const elementCreator = isVoid ? createVoidHtmlElement : createHtmlElement;
|
|
67
86
|
if(name) {
|
|
68
87
|
if(customWrapper) {
|
|
69
88
|
let node = null;
|
|
70
89
|
let createElement = (attr, children) => {
|
|
71
90
|
node = document.createElement(name);
|
|
72
91
|
createElement = (attr, children) => {
|
|
73
|
-
return
|
|
92
|
+
return elementCreator(customWrapper(node.cloneNode()), attr, children);
|
|
74
93
|
};
|
|
75
|
-
return
|
|
94
|
+
return elementCreator(customWrapper(node.cloneNode()), attr, children);;
|
|
76
95
|
};
|
|
77
96
|
|
|
78
97
|
return (attr, children) => createElement(attr, children);
|
|
@@ -82,9 +101,9 @@ export default function HtmlElementWrapper(name, customWrapper = null) {
|
|
|
82
101
|
let createElement = (attr, children) => {
|
|
83
102
|
node = document.createElement(name);
|
|
84
103
|
createElement = (attr, children) => {
|
|
85
|
-
return
|
|
104
|
+
return elementCreator(node.cloneNode(), attr, children);
|
|
86
105
|
};
|
|
87
|
-
return
|
|
106
|
+
return elementCreator(node.cloneNode(), attr, children);
|
|
88
107
|
};
|
|
89
108
|
|
|
90
109
|
return (attr, children) => createElement(attr, children);
|
|
@@ -38,8 +38,19 @@ NDElement.$getChild = (el) => el;
|
|
|
38
38
|
NDElement.prototype.ghostDom = function(element) {
|
|
39
39
|
if(!this.$attachements) {
|
|
40
40
|
this.$attachements = document.createDocumentFragment();
|
|
41
|
+
this.toNdElement = () => {
|
|
42
|
+
const fragment = document.createDocumentFragment();
|
|
43
|
+
if(!this.$attachements.contains(this.$element)) {
|
|
44
|
+
fragment.appendChild(this.$element);
|
|
45
|
+
}
|
|
46
|
+
fragment.appendChild(this.$attachements);
|
|
47
|
+
return fragment;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const child = NDElement.$getChild(element);
|
|
51
|
+
if(child) {
|
|
52
|
+
this.$attachements.appendChild(child);
|
|
41
53
|
}
|
|
42
|
-
this.$attachements.appendChild(NDElement.$getChild(element));
|
|
43
54
|
return this;
|
|
44
55
|
};
|
|
45
56
|
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '../AttributesWrapper';
|
|
5
|
-
import ObservableItem from '../../data/ObservableItem';
|
|
6
|
-
import TemplateBinding from '../TemplateBinding';
|
|
7
|
-
import {BOOLEAN_ATTRIBUTES} from '../constants';
|
|
8
|
-
import ObservableChecker from '../../data/ObservableChecker';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
ObservableItem.prototype.handleNdAttribute = function(element, attributeName) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
ObservableChecker.prototype.handleNdAttribute = ObservableItem.prototype.handleNdAttribute;
|
|
21
|
-
|
|
22
|
-
TemplateBinding.prototype.handleNdAttribute = function(element, attributeName) {
|
|
23
|
-
|
|
24
|
-
};
|
|
1
|
+
// import {
|
|
2
|
+
// bindAttributeWithObservable,
|
|
3
|
+
// bindBooleanAttribute,
|
|
4
|
+
// } from '../AttributesWrapper';
|
|
5
|
+
// import ObservableItem from '../../data/ObservableItem';
|
|
6
|
+
// import TemplateBinding from '../TemplateBinding';
|
|
7
|
+
// import {BOOLEAN_ATTRIBUTES} from '../constants';
|
|
8
|
+
// import ObservableChecker from '../../data/ObservableChecker';
|
|
9
|
+
//
|
|
10
|
+
//
|
|
11
|
+
// ObservableItem.prototype.handleNdAttribute = function(element, attributeName) {
|
|
12
|
+
// if(BOOLEAN_ATTRIBUTES.has(attributeName)) {
|
|
13
|
+
// bindBooleanAttribute(element, attributeName, this);
|
|
14
|
+
// return;
|
|
15
|
+
// }
|
|
16
|
+
//
|
|
17
|
+
// bindAttributeWithObservable(element, attributeName, this);
|
|
18
|
+
// };
|
|
19
|
+
//
|
|
20
|
+
// ObservableChecker.prototype.handleNdAttribute = ObservableItem.prototype.handleNdAttribute;
|
|
21
|
+
//
|
|
22
|
+
// TemplateBinding.prototype.handleNdAttribute = function(element, attributeName) {
|
|
23
|
+
// this.$hydrate(element, attributeName);
|
|
24
|
+
// };
|
|
@@ -88,14 +88,7 @@ ObservableChecker.prototype.toNdElement = ObservableItem.prototype.toNdElement;
|
|
|
88
88
|
* @returns {HTMLElement|DocumentFragment} The underlying DOM node
|
|
89
89
|
*/
|
|
90
90
|
NDElement.prototype.toNdElement = function () {
|
|
91
|
-
|
|
92
|
-
if(this.$attachements) {
|
|
93
|
-
if(!this.$attachements.contains(this.$element)) {
|
|
94
|
-
this.$attachements.append(this.$element);
|
|
95
|
-
}
|
|
96
|
-
return this.$attachements;
|
|
97
|
-
}
|
|
98
|
-
return element;
|
|
91
|
+
return this.$element;
|
|
99
92
|
};
|
|
100
93
|
|
|
101
94
|
/**
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Div, Img, Span } from '../../../core/elements';
|
|
2
|
+
|
|
3
|
+
import './card.css';
|
|
4
|
+
import {Button} from '../../../components/button';
|
|
5
|
+
|
|
6
|
+
export default function CardRender($desc, instance) {
|
|
7
|
+
const imageSlot = buildImage($desc, instance);
|
|
8
|
+
const headerSlot = buildHeader($desc, instance);
|
|
9
|
+
const contentSlot = buildContent($desc, instance);
|
|
10
|
+
const actionsSlot = buildActions($desc, instance);
|
|
11
|
+
const footerSlot = buildFooter($desc, instance, actionsSlot);
|
|
12
|
+
|
|
13
|
+
if ($desc.layout) {
|
|
14
|
+
return $desc.layout(
|
|
15
|
+
{ image: imageSlot, header: headerSlot, content: contentSlot, actions: actionsSlot, footer: footerSlot },
|
|
16
|
+
instance,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const props = instance.getEditableProps();
|
|
21
|
+
|
|
22
|
+
props.class.add('card');
|
|
23
|
+
|
|
24
|
+
if ($desc.variant) {
|
|
25
|
+
props.class.add('is-' + $desc.variant);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
props.class.add({
|
|
29
|
+
'is-horizontal': $desc.horizontal,
|
|
30
|
+
'is-hoverable': $desc.hoverable,
|
|
31
|
+
'is-clickable': instance.hasListeners('click'),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if ($desc.loading) {
|
|
35
|
+
props.class.add({ 'is-loading': $desc.loading });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const children = [
|
|
39
|
+
imageSlot,
|
|
40
|
+
Div({ class: 'card-content'}, [
|
|
41
|
+
Div({ class: 'card-body' }, [headerSlot, contentSlot]),
|
|
42
|
+
footerSlot,
|
|
43
|
+
]),
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const el = Div(instance.resolveProps(), children);
|
|
47
|
+
|
|
48
|
+
el.nd.onClick((e) => instance.emit('click', e));
|
|
49
|
+
el.nd.onMouseEnter((e) => instance.emit('hover', e));
|
|
50
|
+
|
|
51
|
+
return el;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function buildImage($desc, instance) {
|
|
55
|
+
if ($desc.renderImage) {
|
|
56
|
+
return $desc.renderImage($desc, instance);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!$desc.image) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return Div({ class: 'card-image-wrapper' }, [
|
|
64
|
+
Img($desc.image, {
|
|
65
|
+
class: `card-image is-${$desc.imagePosition}`,
|
|
66
|
+
alt: '',
|
|
67
|
+
})
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function buildHeader($desc, instance) {
|
|
72
|
+
if ($desc.renderHeader) {
|
|
73
|
+
return $desc.renderHeader($desc, instance);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const parts = [];
|
|
77
|
+
|
|
78
|
+
if ($desc.title) {
|
|
79
|
+
parts.push(Span({ class: 'card-title' }, $desc.title));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if ($desc.subtitle) {
|
|
83
|
+
parts.push(Span({ class: 'card-subtitle' }, $desc.subtitle));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!parts.length) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return Div({ class: 'card-header' }, parts);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function buildContent($desc, instance) {
|
|
94
|
+
if ($desc.renderContent) {
|
|
95
|
+
return $desc.renderContent($desc, instance);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!$desc.content) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return Div({ class: 'card-content' }, $desc.content);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function buildActions($desc, instance) {
|
|
106
|
+
if ($desc.renderActions) {
|
|
107
|
+
return $desc.renderActions($desc, instance);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!$desc.actions?.length) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const buttons = $desc.actions.map(({ label, callback }) => {
|
|
115
|
+
const btn = (typeof label === 'string') ? Button(label, { class: 'card-action' }) : label;
|
|
116
|
+
btn.nd.onClick(() => callback?.());
|
|
117
|
+
return btn;
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return Div({ class: 'card-actions' }, buttons);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function buildFooter($desc, instance, actionsSlot) {
|
|
124
|
+
if ($desc.renderFooter) {
|
|
125
|
+
return $desc.renderFooter($desc, instance);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!actionsSlot) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return Div({ class: 'card-footer' }, actionsSlot);
|
|
133
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--card-bg: var(--background);
|
|
3
|
+
--card-border: var(--gray-lite-3);
|
|
4
|
+
--card-radius: var(--radius-card);
|
|
5
|
+
--card-shadow: var(--shadow-card);
|
|
6
|
+
--card-shadow-elevated: var(--shadow-lg);
|
|
7
|
+
--card-shadow-hover: var(--shadow-md);
|
|
8
|
+
--card-font-size: var(--description-size);
|
|
9
|
+
--card-padding: var(--space-comfortable);
|
|
10
|
+
--card-img-height: 200px;
|
|
11
|
+
--card-title-size: var(--text-size);
|
|
12
|
+
--card-title-weight: 600;
|
|
13
|
+
--card-title-color: var(--text-color);
|
|
14
|
+
--card-subtitle-size: var(--note-size);
|
|
15
|
+
--card-subtitle-color: var(--gray);
|
|
16
|
+
--card-footer-padding: var(--space-cozy) var(--space-comfortable);
|
|
17
|
+
--card-footer-border: var(--gray-lite-3);
|
|
18
|
+
--card-footer-gap: var(--space-cozy);
|
|
19
|
+
--card-transition: box-shadow 0.2s ease;
|
|
20
|
+
--card-loading-bg: var(--gray-lite-5);
|
|
21
|
+
--card-horizontal-img-width: 175px;
|
|
22
|
+
--card-clickable-cursor: pointer;
|
|
23
|
+
--card-header-row-gap: .5rem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
.card {
|
|
28
|
+
background: var(--card-bg);
|
|
29
|
+
border: 1px solid var(--card-border);
|
|
30
|
+
border-radius: var(--card-radius);
|
|
31
|
+
overflow: hidden;
|
|
32
|
+
font-size: var(--card-font-size);
|
|
33
|
+
transition: var(--card-transition);
|
|
34
|
+
display: inline-flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
|
|
37
|
+
&.is-elevated {
|
|
38
|
+
border: none;
|
|
39
|
+
box-shadow: var(--card-shadow-elevated);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&.is-flat {
|
|
43
|
+
border: none;
|
|
44
|
+
box-shadow: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
&.is-outlined {
|
|
48
|
+
border-width: 2px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&.is-hoverable:hover {
|
|
52
|
+
box-shadow: var(--card-shadow-hover);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&.is-clickable {
|
|
56
|
+
cursor: var(--card-clickable-cursor);
|
|
57
|
+
|
|
58
|
+
&:hover {
|
|
59
|
+
box-shadow: var(--card-shadow-hover);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
&:active {
|
|
63
|
+
opacity: 0.95;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&.is-horizontal {
|
|
68
|
+
flex-direction: row;
|
|
69
|
+
|
|
70
|
+
.card-image-wrapper {
|
|
71
|
+
width: var(--card-horizontal-img-width);
|
|
72
|
+
}
|
|
73
|
+
.card-image {
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
}
|
|
76
|
+
.card-content {
|
|
77
|
+
display: flex;
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
align-items: flex-start;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
&.is-loading {
|
|
85
|
+
.card-title,
|
|
86
|
+
.card-subtitle,
|
|
87
|
+
.card-content {
|
|
88
|
+
background: var(--card-loading-bg);
|
|
89
|
+
border-radius: 4px;
|
|
90
|
+
color: transparent;
|
|
91
|
+
user-select: none;
|
|
92
|
+
|
|
93
|
+
* {
|
|
94
|
+
visibility: hidden;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.card-title { width: 60%; }
|
|
99
|
+
.card-subtitle { width: 40%; }
|
|
100
|
+
.card-image { background: var(--card-loading-bg); }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
.card-header {
|
|
104
|
+
display: flex;
|
|
105
|
+
flex-direction: column;
|
|
106
|
+
align-items: flex-start;
|
|
107
|
+
justify-content: flex-start;
|
|
108
|
+
row-gap: var(--card-header-row-gap);
|
|
109
|
+
}
|
|
110
|
+
.card-actions {
|
|
111
|
+
width: 100%;
|
|
112
|
+
display: flex;
|
|
113
|
+
justify-content: space-between;
|
|
114
|
+
column-gap: var(--space-tiny);
|
|
115
|
+
> * {
|
|
116
|
+
flex: 1;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
.card-image-wrapper {
|
|
120
|
+
width: 100%;
|
|
121
|
+
height: var(--card-img-height);
|
|
122
|
+
}
|
|
123
|
+
.card-image {
|
|
124
|
+
width: 100%;
|
|
125
|
+
height: 100%;
|
|
126
|
+
object-fit: cover;
|
|
127
|
+
display: block;
|
|
128
|
+
|
|
129
|
+
&.is-top { order: -1; }
|
|
130
|
+
&.is-bottom { order: 10; }
|
|
131
|
+
|
|
132
|
+
.card.is-horizontal &.is-left { order: -1; }
|
|
133
|
+
.card.is-horizontal &.is-right { order: 10; }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.card-body {
|
|
137
|
+
padding: var(--card-padding);
|
|
138
|
+
flex: 1;
|
|
139
|
+
display: flex;
|
|
140
|
+
flex-direction: column;
|
|
141
|
+
gap: var(--space-cozy);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.card-title {
|
|
145
|
+
font-size: var(--card-title-size);
|
|
146
|
+
font-weight: var(--card-title-weight);
|
|
147
|
+
color: var(--card-title-color);
|
|
148
|
+
margin: 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.card-subtitle {
|
|
152
|
+
font-size: var(--card-subtitle-size);
|
|
153
|
+
color: var(--card-subtitle-color);
|
|
154
|
+
margin: 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.card-content {
|
|
158
|
+
flex: 1;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.card-footer {
|
|
162
|
+
width: 100%;
|
|
163
|
+
display: flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
justify-content: flex-end;
|
|
166
|
+
gap: var(--card-footer-gap);
|
|
167
|
+
padding: var(--card-footer-padding);
|
|
168
|
+
border-top: 1px solid var(--card-footer-border);
|
|
169
|
+
}
|