active_element 0.0.12 → 0.0.14
Sign up to get free protection for your applications and to get access to all the features.
- 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
|