active_element 0.0.12 → 0.0.14
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +17 -17
- data/app/assets/javascripts/active_element/highlight.js +311 -0
- data/app/assets/javascripts/active_element/json_field.js +51 -20
- data/app/assets/javascripts/active_element/popover.js +6 -4
- data/app/assets/stylesheets/active_element/_dark.scss +1 -1
- data/app/assets/stylesheets/active_element/application.scss +39 -1
- data/app/controllers/concerns/active_element/default_controller_actions.rb +7 -7
- data/app/views/active_element/_title.html.erb +1 -0
- data/app/views/active_element/components/fields/_json.html.erb +3 -2
- data/app/views/active_element/components/form/_field.html.erb +2 -1
- data/app/views/active_element/components/form/_json.html.erb +2 -0
- data/app/views/active_element/components/form/_label.html.erb +7 -0
- data/app/views/active_element/components/form.html.erb +2 -2
- data/app/views/layouts/active_element.html.erb +29 -6
- data/example_app/Gemfile.lock +1 -1
- data/lib/active_element/components/form.rb +1 -8
- data/lib/active_element/components/util/display_value_mapping.rb +0 -2
- data/lib/active_element/components/util/form_field_mapping.rb +2 -1
- data/lib/active_element/components/util.rb +7 -0
- data/lib/active_element/controller_interface.rb +2 -1
- data/lib/active_element/controller_state.rb +1 -1
- data/lib/active_element/default_controller/actions.rb +3 -0
- data/lib/active_element/default_controller/controller.rb +145 -0
- data/lib/active_element/default_controller/json_params.rb +48 -0
- data/lib/active_element/default_controller/params.rb +97 -0
- data/lib/active_element/default_controller/search.rb +112 -0
- data/lib/active_element/default_controller.rb +10 -132
- data/lib/active_element/version.rb +1 -1
- data/lib/active_element.rb +0 -2
- data/rspec-documentation/_head.html.erb +2 -0
- data/rspec-documentation/pages/000-Introduction.md +8 -5
- data/rspec-documentation/pages/005-Setup.md +21 -28
- data/rspec-documentation/pages/010-Components/Form Fields.md +35 -0
- data/rspec-documentation/pages/015-Custom Controllers.md +32 -0
- data/rspec-documentation/pages/016-Default Controller.md +132 -0
- data/rspec-documentation/pages/Themes.md +3 -0
- metadata +12 -4
- data/lib/active_element/default_record_params.rb +0 -62
- data/lib/active_element/default_search.rb +0 -110
@@ -4,11 +4,26 @@ ActiveElement.JsonField = (() => {
|
|
4
4
|
const humanize = ({ string, singular = false }) => {
|
5
5
|
if (!string) return '';
|
6
6
|
|
7
|
-
const humanized = string.split('_')
|
7
|
+
const humanized = string.split('_')
|
8
|
+
.map(item => item.charAt(0).toUpperCase() + item.substring(1)).join(' ')
|
9
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2');
|
8
10
|
|
9
11
|
if (!singular) return humanized;
|
10
12
|
|
11
|
-
|
13
|
+
// FIXME: Expose translations from back-end to make this more useful.
|
14
|
+
return humanized.replace(/[^u]s$/, '');
|
15
|
+
};
|
16
|
+
|
17
|
+
// Adapted from: https://stackoverflow.com/a/7557433
|
18
|
+
const isVisible = (element) => {
|
19
|
+
const boundingClientRect = element.getBoundingClientRect();
|
20
|
+
|
21
|
+
return (
|
22
|
+
boundingClientRect.top >= 0 &&
|
23
|
+
boundingClientRect.left >= 0 &&
|
24
|
+
boundingClientRect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
25
|
+
boundingClientRect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
26
|
+
);
|
12
27
|
};
|
13
28
|
|
14
29
|
const isObject = (object) => object && typeof object === 'object';
|
@@ -44,7 +59,6 @@ ActiveElement.JsonField = (() => {
|
|
44
59
|
};
|
45
60
|
|
46
61
|
store.state = defaultState({ schema, path: [], defaultValue: data });
|
47
|
-
console.log(store.state)
|
48
62
|
|
49
63
|
const stateChangedCallbacks = [];
|
50
64
|
const stateChanged = (callback, state) => stateChangedCallbacks.push([callback, state]);
|
@@ -211,10 +225,12 @@ ActiveElement.JsonField = (() => {
|
|
211
225
|
};
|
212
226
|
|
213
227
|
const Component = ({ store, stateChanged, connectState, schema, element, fieldName }) => {
|
214
|
-
const ObjectField = ({ schema, state, path, omitLabel = false }) => {
|
228
|
+
const ObjectField = ({ schema, state, path, omitLabel = false, depth = 0 }) => {
|
215
229
|
const getPath = () => schema.name ? path.concat(schema.name) : path;
|
216
230
|
const currentPath = getPath();
|
217
231
|
|
232
|
+
depth = depth + 1;
|
233
|
+
|
218
234
|
let element;
|
219
235
|
|
220
236
|
switch (schema.type) {
|
@@ -235,7 +251,9 @@ ActiveElement.JsonField = (() => {
|
|
235
251
|
case 'decimal':
|
236
252
|
return DecimalField({ state, omitLabel, schema, path: currentPath });
|
237
253
|
case 'object':
|
238
|
-
element = cloneElement('form-group
|
254
|
+
element = cloneElement('form-group');
|
255
|
+
element.classList.add(`depth-${depth}`);
|
256
|
+
if (schema.name) element.append(Label({ schema }));
|
239
257
|
(schema.shape.fields).forEach((field) => {
|
240
258
|
element.append(
|
241
259
|
ObjectField({
|
@@ -243,6 +261,7 @@ ActiveElement.JsonField = (() => {
|
|
243
261
|
schema: field,
|
244
262
|
state: state ? state[field.name] : null,
|
245
263
|
path: currentPath,
|
264
|
+
depth,
|
246
265
|
})
|
247
266
|
);
|
248
267
|
});
|
@@ -250,10 +269,10 @@ ActiveElement.JsonField = (() => {
|
|
250
269
|
return element;
|
251
270
|
case 'array':
|
252
271
|
element = cloneElement('form-group');
|
253
|
-
const list = ArrayField({ schema, state, path: currentPath });
|
272
|
+
const list = ArrayField({ schema, state, path: currentPath, depth });
|
254
273
|
if (schema.shape?.type === 'object') list.classList.add('array-of-objects');
|
255
274
|
element.append(AppendButton({ list, schema, state, path: currentPath }));
|
256
|
-
element.append(Label({
|
275
|
+
element.append(Label({ schema }));
|
257
276
|
element.append(list);
|
258
277
|
return element;
|
259
278
|
}
|
@@ -270,12 +289,12 @@ ActiveElement.JsonField = (() => {
|
|
270
289
|
const element = cloneElement('form-check');
|
271
290
|
|
272
291
|
element.append(checkbox);
|
273
|
-
element.append(Label({
|
292
|
+
element.append(Label({ schema, template: 'form-check-label', labelFor: checkbox }));
|
274
293
|
|
275
294
|
return element;
|
276
295
|
};
|
277
296
|
|
278
|
-
const ArrayField = ({ schema, state, path: objectPath }) => {
|
297
|
+
const ArrayField = ({ schema, state, depth, path: objectPath }) => {
|
279
298
|
const element = cloneElement('list-group');
|
280
299
|
|
281
300
|
if (schema.focus) element.classList.add('focus');
|
@@ -284,21 +303,22 @@ ActiveElement.JsonField = (() => {
|
|
284
303
|
if (state) {
|
285
304
|
state.forEach((eachState, index) => {
|
286
305
|
const path = objectPath.concat([index]);
|
287
|
-
element.append(ArrayItem({ state: eachState, path, schema }));
|
306
|
+
element.append(ArrayItem({ state: eachState, path, schema, depth }));
|
288
307
|
});
|
289
308
|
}
|
290
309
|
|
291
310
|
return element;
|
292
311
|
};
|
293
312
|
|
294
|
-
const ArrayItem = ({ state, path, schema, newItem = false }) => {
|
313
|
+
const ArrayItem = ({ state, path, schema, depth, newItem = false }) => {
|
295
314
|
const element = cloneElement('list-item');
|
296
315
|
const wrapper = document.createElement('div');
|
297
316
|
const objectField = ObjectField({
|
298
317
|
path,
|
299
318
|
omitLabel: true,
|
300
319
|
schema: { ...schema.shape },
|
301
|
-
state: state
|
320
|
+
state: state,
|
321
|
+
depth,
|
302
322
|
});
|
303
323
|
|
304
324
|
// TODO: Use same template etc. for all delete buttons, use presentation layer to
|
@@ -366,6 +386,7 @@ ActiveElement.JsonField = (() => {
|
|
366
386
|
valueElement.href = '#';
|
367
387
|
modalBody.append(group);
|
368
388
|
modalBody.classList.add('json-field');
|
389
|
+
group.classList.add('depth-1');
|
369
390
|
titleElement.append(deleteObjectButton);
|
370
391
|
modalHeader.append(deleteObjectButton);
|
371
392
|
deleteObjectButton.addEventListener('click', () => bootstrapModal.hide());
|
@@ -382,10 +403,10 @@ ActiveElement.JsonField = (() => {
|
|
382
403
|
return element;
|
383
404
|
};
|
384
405
|
|
385
|
-
const Label = ({
|
406
|
+
const Label = ({ schema, template, labelFor }) => {
|
386
407
|
const element = cloneElement(template || 'label');
|
387
408
|
|
388
|
-
element.append(humanize({ string:
|
409
|
+
element.append(schema.label || humanize({ string: schema.name }));
|
389
410
|
|
390
411
|
if (labelFor) {
|
391
412
|
element.htmlFor = labelFor.id;
|
@@ -443,7 +464,7 @@ ActiveElement.JsonField = (() => {
|
|
443
464
|
const group = cloneElement('form-group-floating');
|
444
465
|
|
445
466
|
group.append(element);
|
446
|
-
group.append(Label({
|
467
|
+
group.append(Label({ schema, labelFor: element }));
|
447
468
|
|
448
469
|
return group;
|
449
470
|
};
|
@@ -464,9 +485,9 @@ ActiveElement.JsonField = (() => {
|
|
464
485
|
|
465
486
|
if (floating) {
|
466
487
|
group.append(element);
|
467
|
-
group.append(Label({
|
488
|
+
group.append(Label({ schema, labelFor: element }));
|
468
489
|
} else {
|
469
|
-
group.append(Label({
|
490
|
+
group.append(Label({ schema, labelFor: element }));
|
470
491
|
group.append(element);
|
471
492
|
}
|
472
493
|
|
@@ -513,9 +534,8 @@ ActiveElement.JsonField = (() => {
|
|
513
534
|
|
514
535
|
const AppendButton = ({ list, schema, state, path: objectPath }) => {
|
515
536
|
const element = cloneElement('append-button');
|
516
|
-
const humanName = humanize({ string: schema.name || fieldName, singular: true });
|
517
537
|
|
518
|
-
element.append(`Add
|
538
|
+
element.append(`Add Item`);
|
519
539
|
element.classList.add('append-button', 'float-end');
|
520
540
|
element.onclick = (ev) => {
|
521
541
|
ev.preventDefault();
|
@@ -524,7 +544,8 @@ ActiveElement.JsonField = (() => {
|
|
524
544
|
const item = ArrayItem({ path, state: appendState, schema, newItem: true })
|
525
545
|
|
526
546
|
list.append(item);
|
527
|
-
|
547
|
+
|
548
|
+
if (!isVisible(item)) item.scrollIntoView({ block: 'center' });
|
528
549
|
|
529
550
|
return false;
|
530
551
|
};
|
@@ -540,19 +561,29 @@ ActiveElement.JsonField = (() => {
|
|
540
561
|
const formId = element.dataset.formId;
|
541
562
|
const formFieldElement = document.querySelector(`#${element.dataset.fieldId}`);
|
542
563
|
const schemaFieldElement = document.querySelector(`#${element.dataset.schemaFieldId}`);
|
564
|
+
const jsonViewModal = document.querySelector(`#${element.dataset.jsonViewModalId}`);
|
565
|
+
const jsonViewModalTrigger = document.querySelector(`#${element.dataset.jsonViewModalTriggerId}`);
|
543
566
|
const fieldName = element.dataset.fieldName;
|
544
567
|
const schema = getSchema(element);
|
545
568
|
const { store, stateChanged, connectState } = createStore({ data, schema });
|
569
|
+
let currentState = null;
|
546
570
|
|
547
571
|
schemaFieldElement.value = JSON.stringify(schema);
|
548
572
|
|
549
573
|
stateChanged(({ getState }) => {
|
550
574
|
const state = getState();
|
551
575
|
|
576
|
+
currentState = state;
|
552
577
|
formFieldElement.value = JSON.stringify(state);
|
553
578
|
ActiveElement.log.debug(state);
|
554
579
|
});
|
555
580
|
|
581
|
+
jsonViewModalTrigger.addEventListener('click', () => {
|
582
|
+
const highlighted = hljs.highlight(JSON.stringify(currentState, null, 2), { language: 'json' }).value;
|
583
|
+
jsonViewModal.querySelector('[data-field-type="modal-body"]').innerHTML = `<pre>${highlighted}</pre>`;
|
584
|
+
return true;
|
585
|
+
});
|
586
|
+
|
556
587
|
connectState({ element });
|
557
588
|
|
558
589
|
const component = Component({ store, stateChanged, connectState, schema, element, fieldName });
|
@@ -1,6 +1,8 @@
|
|
1
1
|
(() => {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
window.addEventListener('DOMContentLoaded', () => {
|
3
|
+
const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
|
4
|
+
const popoverList = popoverTriggerList.map(function (element) {
|
5
|
+
return new bootstrap.Popover(element)
|
6
|
+
})
|
7
|
+
});
|
6
8
|
})();
|
@@ -2,12 +2,23 @@
|
|
2
2
|
@import "bootstrap";
|
3
3
|
@import "dark";
|
4
4
|
|
5
|
+
|
6
|
+
@keyframes fade-in {
|
7
|
+
from {
|
8
|
+
opacity: 0;
|
9
|
+
}
|
10
|
+
|
11
|
+
to {
|
12
|
+
opacity: 1;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
5
16
|
.application-menu {
|
6
17
|
height: 5rem;
|
7
18
|
padding-left: 2rem;
|
8
19
|
top: 0;
|
9
20
|
transition: height 0.5s ease-in-out, background-position 0.8s ease-in-out;
|
10
|
-
background-color: #
|
21
|
+
background-color: #508ea1 !important;
|
11
22
|
z-index: 2000;
|
12
23
|
.dropdown-toggle::after {
|
13
24
|
color: #{$blue};
|
@@ -77,6 +88,12 @@ form {
|
|
77
88
|
margin-top: 5rem;
|
78
89
|
}
|
79
90
|
|
91
|
+
.modal-content pre, .modal-content div.json-highlight {
|
92
|
+
font-size: 0.875rem;
|
93
|
+
line-height: 1.2rem;
|
94
|
+
font-family: monospace;
|
95
|
+
}
|
96
|
+
|
80
97
|
.json-field {
|
81
98
|
ol.json-array-field {
|
82
99
|
margin-top: 1rem;
|
@@ -105,6 +122,11 @@ form {
|
|
105
122
|
.form-group {
|
106
123
|
padding: 1rem;
|
107
124
|
background-color: #58575755;
|
125
|
+
|
126
|
+
&.depth-1 {
|
127
|
+
padding: 0;
|
128
|
+
background-color: transparent;
|
129
|
+
}
|
108
130
|
}
|
109
131
|
|
110
132
|
.form-check {
|
@@ -176,6 +198,16 @@ form {
|
|
176
198
|
}
|
177
199
|
|
178
200
|
|
201
|
+
.json-array-field {
|
202
|
+
li, .json-delete-button {
|
203
|
+
opacity: 0;
|
204
|
+
animation: fade-in ease-in 1;
|
205
|
+
animation-fill-mode: forwards;
|
206
|
+
animation-duration: 0.5s;
|
207
|
+
animation-delay: 0;
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|
179
211
|
.json-array-field {
|
180
212
|
li .json-text-field,
|
181
213
|
li .json-select-field,
|
@@ -185,12 +217,18 @@ form {
|
|
185
217
|
li .json-date-field,
|
186
218
|
li .json-time-field,
|
187
219
|
li .json-datetime-field {
|
220
|
+
|
188
221
|
&.deletable {
|
189
222
|
width: calc(100% - 3.2rem);
|
190
223
|
}
|
191
224
|
}
|
192
225
|
}
|
193
226
|
|
227
|
+
.append-button {
|
228
|
+
position: relative;
|
229
|
+
z-index: 300;
|
230
|
+
}
|
231
|
+
|
194
232
|
.form-control, .form-select {
|
195
233
|
display: inline;
|
196
234
|
.append-button {
|
@@ -8,31 +8,31 @@ module ActiveElement
|
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
10
|
def index
|
11
|
-
ActiveElement::DefaultController.new(controller: self).index
|
11
|
+
ActiveElement::DefaultController::Controller.new(controller: self).index
|
12
12
|
end
|
13
13
|
|
14
14
|
def show
|
15
|
-
ActiveElement::DefaultController.new(controller: self).show
|
15
|
+
ActiveElement::DefaultController::Controller.new(controller: self).show
|
16
16
|
end
|
17
17
|
|
18
18
|
def new
|
19
|
-
ActiveElement::DefaultController.new(controller: self).new
|
19
|
+
ActiveElement::DefaultController::Controller.new(controller: self).new
|
20
20
|
end
|
21
21
|
|
22
22
|
def create
|
23
|
-
ActiveElement::DefaultController.new(controller: self).create
|
23
|
+
ActiveElement::DefaultController::Controller.new(controller: self).create
|
24
24
|
end
|
25
25
|
|
26
26
|
def edit
|
27
|
-
ActiveElement::DefaultController.new(controller: self).edit
|
27
|
+
ActiveElement::DefaultController::Controller.new(controller: self).edit
|
28
28
|
end
|
29
29
|
|
30
30
|
def update
|
31
|
-
ActiveElement::DefaultController.new(controller: self).update
|
31
|
+
ActiveElement::DefaultController::Controller.new(controller: self).update
|
32
32
|
end
|
33
33
|
|
34
34
|
def destroy
|
35
|
-
ActiveElement::DefaultController.new(controller: self).destroy
|
35
|
+
ActiveElement::DefaultController::Controller.new(controller: self).destroy
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<title><%= active_element.application_name.humanize %> - <%= controller_name.humanize %></title>
|
@@ -1,9 +1,10 @@
|
|
1
1
|
<a data-modal-id="<%= "#json-modal-#{field_id}" %>"
|
2
|
+
id="<%= "json-view-modal-trigger-#{field_id}" %>"
|
2
3
|
data-json-modal-link="true"
|
3
4
|
data-bs-toggle="modal"
|
4
5
|
data-bs-target="#json-modal-<%= field_id %>"
|
5
|
-
class="text-decoration-none"
|
6
|
-
href="#">
|
6
|
+
class="text-decoration-none text-nowrap"
|
7
|
+
href="#">JSON <i class="fa-solid fa-magnifying-glass"></i></a>
|
7
8
|
<div id="json-modal-<%= field_id %>" class="modal fade"
|
8
9
|
tabindex="-1"
|
9
10
|
aria-hidden="true">
|
@@ -6,7 +6,8 @@
|
|
6
6
|
locals: { form_id: id, form: form, field: field, options: options, component: component } %>
|
7
7
|
<% elsif type == :json_field %>
|
8
8
|
<%= render partial: 'active_element/components/form/json',
|
9
|
-
locals: { form_id: id, form: form, field: field, field_id:
|
9
|
+
locals: { form_id: id, form: form, field: field, field_id: "#{id}-json-field-#{field}",
|
10
|
+
options: options, component: component } %>
|
10
11
|
<% elsif type == :text_search_field %>
|
11
12
|
<%= render partial: 'active_element/components/form/text_search',
|
12
13
|
locals: { form_id: id, form: form, field: field, options: options, component: component } %>
|
@@ -4,6 +4,8 @@
|
|
4
4
|
data-form-id="<%= form_id %>"
|
5
5
|
data-field-id="<%= field_id %>"
|
6
6
|
data-schema-field-id="<%= field_id %>-schema"
|
7
|
+
data-json-view-modal-id="<%= "json-modal-#{form_id}-#{field}-json-view" %>"
|
8
|
+
data-json-view-modal-trigger-id="<%= "json-view-modal-trigger-#{form_id}-#{field}-json-view" %>"
|
7
9
|
>
|
8
10
|
|
9
11
|
</div>
|
@@ -26,3 +26,10 @@
|
|
26
26
|
<%= render partial: 'active_element/components/form/option_groups_summary',
|
27
27
|
locals: { option_groups: options[:option_groups] } %>
|
28
28
|
<% end %>
|
29
|
+
|
30
|
+
<% if type == :json_field %>
|
31
|
+
<div>
|
32
|
+
<%= render partial: 'active_element/components/fields/json',
|
33
|
+
locals: { value: component.value_for(field), field_id: "#{id}-#{field}-json-view" } %>
|
34
|
+
</div>
|
35
|
+
<% end %>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<% if destroy %>
|
2
|
-
<div class="container w-100 text-end">
|
2
|
+
<div class="container me-0 w-100 text-end">
|
3
3
|
<%= active_element.component.destroy_button(record) %>
|
4
4
|
</div>
|
5
5
|
<% end %>
|
@@ -51,7 +51,7 @@
|
|
51
51
|
<% field_group.each do |field, type, options| %>
|
52
52
|
<div class="col-sm-3">
|
53
53
|
<%= render partial: 'active_element/components/form/label',
|
54
|
-
locals: { type: type, form: form, field: field, options: options } %>
|
54
|
+
locals: { component: component, id: id, type: type, form: form, field: field, options: options } %>
|
55
55
|
</div>
|
56
56
|
|
57
57
|
|
@@ -1,6 +1,15 @@
|
|
1
1
|
<html>
|
2
2
|
<head>
|
3
3
|
<%= render_active_element_hook 'active_element/before_head' %>
|
4
|
+
<%= render_active_element_hook 'active_element/favicon' %>
|
5
|
+
<%= render_active_element_hook 'active_element/title' %>
|
6
|
+
|
7
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
8
|
+
<link rel="stylesheet"
|
9
|
+
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css">
|
10
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js" integrity="sha512-fD9DI5bZwQxOi7MhYWnnNPlvXdp/2Pj3XSTRrFs5FQa4mizyGLnJcN6tuvUS6LbmgN1ut+XGSABKvjN0H6Aoow==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
11
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.9.2/umd/popper.min.js" integrity="sha512-2rNj2KJ+D8s1ceNasTIex6z4HWyOnEYLVC3FigGOmyQCZc2eBXKgOxQmo3oKLHyfcj53uz4QMsRCWNbLd32Q1g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
12
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
|
4
13
|
|
5
14
|
<style>
|
6
15
|
<%= Rouge::Theme.find('tulip').render(scope: '.json-highlight') %>
|
@@ -12,14 +21,28 @@
|
|
12
21
|
color: #6b7399
|
13
22
|
}
|
14
23
|
|
24
|
+
.json-highlight .kc, .json-highlight .c {
|
25
|
+
color: #695;
|
26
|
+
}
|
27
|
+
|
15
28
|
.json-highlight {
|
16
29
|
background-color: transparent;
|
17
30
|
}
|
18
|
-
</style>
|
19
31
|
|
20
|
-
|
21
|
-
|
22
|
-
|
32
|
+
.hljs-punctuation {
|
33
|
+
color: #7e4b6f;
|
34
|
+
}
|
35
|
+
|
36
|
+
.hljs-attr {
|
37
|
+
color: #9f93e6;
|
38
|
+
font-weight: bold;
|
39
|
+
}
|
40
|
+
|
41
|
+
.hljs-string {
|
42
|
+
color: #6b7399;
|
43
|
+
font-weight: bold;
|
44
|
+
}
|
45
|
+
</style>
|
23
46
|
|
24
47
|
<script>
|
25
48
|
window.ActiveElement = window.ActiveElement || {};
|
@@ -27,7 +50,7 @@
|
|
27
50
|
|
28
51
|
<%= stylesheet_link_tag 'active_element/application', 'data-turbolinks-track': 'reload' %>
|
29
52
|
|
30
|
-
<% if Rails.application.assets
|
53
|
+
<% if Rails.application.assets&.find_asset('application.css').present? %>
|
31
54
|
<%= stylesheet_link_tag 'application', 'data-turbolinks-track': 'reload' %>
|
32
55
|
<% end %>
|
33
56
|
|
@@ -75,7 +98,7 @@
|
|
75
98
|
<%= render_active_element_hook 'active_element/after_content' %>
|
76
99
|
|
77
100
|
<%= javascript_include_tag 'active_element/application', 'data-turbolinks-track': 'reload' %>
|
78
|
-
<% if Rails.application.assets
|
101
|
+
<% if Rails.application.assets&.find_asset('application.js').present? %>
|
79
102
|
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
|
80
103
|
<% end %>
|
81
104
|
</body>
|
data/example_app/Gemfile.lock
CHANGED
@@ -81,14 +81,7 @@ module ActiveElement
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def schema_for(field, options)
|
84
|
-
options.key?(:schema) ? options.fetch(:schema) :
|
85
|
-
end
|
86
|
-
|
87
|
-
def schema_from_yaml(field)
|
88
|
-
YAML.safe_load(
|
89
|
-
Rails.root.join("config/forms/#{record.class.name.underscore}/#{field}.yml").read,
|
90
|
-
symbolize_names: true
|
91
|
-
)
|
84
|
+
options.key?(:schema) ? options.fetch(:schema) : Util.json_schema(model: record.class, field: field)
|
92
85
|
end
|
93
86
|
|
94
87
|
def display_value_for_select(field, options)
|
@@ -16,8 +16,6 @@ module ActiveElement
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def json_value
|
19
|
-
return ActiveElement.json_pretty_print(value_from_record) unless component.is_a?(CollectionTable)
|
20
|
-
|
21
19
|
component.controller.render_to_string(
|
22
20
|
partial: 'active_element/components/fields/json',
|
23
21
|
locals: { value: value_from_record, field_id: ActiveElement.element_id }
|
@@ -136,8 +136,9 @@ module ActiveElement
|
|
136
136
|
end
|
137
137
|
|
138
138
|
def searchable_fields(field)
|
139
|
+
# FIXME: Use database column type to only include strings/numbers.
|
139
140
|
(Util.relation_controller(model, controller, field)&.active_element&.state&.searchable_fields || [])
|
140
|
-
.reject { |
|
141
|
+
.reject { |searchable_field| searchable_field.to_s.end_with?('_at') }
|
141
142
|
end
|
142
143
|
|
143
144
|
def relation_primary_key(field)
|
@@ -41,6 +41,13 @@ module ActiveElement
|
|
41
41
|
"#{namespace.classify}::#{base}".safe_constantize || base.safe_constantize
|
42
42
|
end
|
43
43
|
|
44
|
+
def self.json_schema(model:, field:)
|
45
|
+
YAML.safe_load(
|
46
|
+
Rails.root.join("config/forms/#{model.name.underscore}/#{field}.yml").read,
|
47
|
+
symbolize_names: true
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
44
51
|
def self.json_pretty_print(json)
|
45
52
|
formatter = Rouge::Formatters::HTML.new
|
46
53
|
lexer = Rouge::Lexers::JSON.new
|
@@ -7,7 +7,7 @@ module ActiveElement
|
|
7
7
|
class ControllerState
|
8
8
|
attr_reader :permissions, :listable_fields, :viewable_fields, :editable_fields, :searchable_fields
|
9
9
|
attr_accessor :sign_in_path, :sign_in, :sign_in_method, :sign_out_path, :sign_out_method,
|
10
|
-
:deletable, :authorizor, :authenticator
|
10
|
+
:deletable, :authorizor, :authenticator, :list_order
|
11
11
|
|
12
12
|
def initialize(controller:)
|
13
13
|
@controller = controller
|