mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-25 13:44:06 -08:00
Create Field Definitions helper control
This commit is contained in:
parent
f849fcca89
commit
a4b93d4bbd
|
@ -336,11 +336,8 @@ return [
|
|||
'label2_2d_type_help' => 'Format for 2D barcodes',
|
||||
'label2_2d_target' => '2D Barcode Target',
|
||||
'label2_2d_target_help' => 'The URL the 2D barcode points to when scanned',
|
||||
'label2_fields' => 'Fields Definition',
|
||||
'label2_fields_help' => 'Fields to show on the label in the format <code>Label=asset_field</code>',
|
||||
'label2_fields_help_semi' => 'Use <code>;</code> to separate fields',
|
||||
'label2_fields_help_pipe' => 'Use <code>|</code> to allow multiple options in each field. For example <code>Name=name|Nickname=_snipeit_my_custom_field_2</code> will use <code>name</code> if a value is set, otherwise it will use <code>_snipeit_my_custom_field_2</code>. This is useful to ensure field order',
|
||||
'label2_fields_help_once' => 'Each field will only be selected once per label',
|
||||
'label2_fields' => 'Field Definitions',
|
||||
'label2_fields_help' => 'Fields can be added, removed, and reordered in the left column. For each field, multiple options for Label and DataSource can be added, removed, and reordered in the right column.',
|
||||
|
||||
'help_asterisk_bold' => 'Text entered as <code>**text**</code> will be displayed as bold',
|
||||
'help_blank_to_use' => 'Leave blank to use the value from <code>:setting_name</code>',
|
||||
|
|
337
resources/views/partials/label2-field-definitions.blade.php
Normal file
337
resources/views/partials/label2-field-definitions.blade.php
Normal file
|
@ -0,0 +1,337 @@
|
|||
@once
|
||||
@push('js')
|
||||
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
@endpush
|
||||
@push('css')
|
||||
<style>
|
||||
:root {
|
||||
--l2fd-background-color: rgb(246, 250, 255);
|
||||
--l2fd-border-color: #d2d6de;
|
||||
--l2fd-font-color: #555555;
|
||||
|
||||
--list-padding: 2px;
|
||||
|
||||
--listitem-font-color: #555555;
|
||||
--listitem-padding: 8px;
|
||||
--listitem-spacing: 2px;
|
||||
--listitem-background-color: white;
|
||||
--listitem-border-color: #ccc;
|
||||
--listitem-border-radius: 2px;
|
||||
|
||||
--listitem-hover-font-color: var(--listitem-font-color);
|
||||
--listitem-hover-background-color: var(--listitem-background-color);
|
||||
--listitem-hover-border-color: rgb(102, 175, 233);
|
||||
|
||||
--listitem-selected-font-color: var(--listitem-font-color);
|
||||
--listitem-selected-background-color: rgb(236, 240, 245);
|
||||
--listitem-selected-border-color: var(--listitem-hover-border-color);
|
||||
|
||||
--buttonbar-button-font-color: #555555;
|
||||
--buttonbar-button-background-color: white;
|
||||
--buttonbar-button-border-color: #ccc;
|
||||
--buttonbar-button-border-radius: 2px;
|
||||
|
||||
--buttonbar-button-hover-font-color: var(--buttonbar-button-font-color);
|
||||
--buttonbar-button-hover-background-color: var(--buttonbar-button-background-color);
|
||||
--buttonbar-button-hover-border-color: var(--listitem-hover-border-color);
|
||||
|
||||
--buttonbar-button-disabled-font-color: var(--buttonbar-button-font-color);
|
||||
--buttonbar-button-disabled-background-color: #eee;
|
||||
--buttonbar-button-disabled-border-color: var(--buttonbar-button-border-color);
|
||||
}
|
||||
|
||||
.l2fd-root,
|
||||
.l2fd-root * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.l2fd-root {
|
||||
height: 400px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.l2fd-title {
|
||||
font-size: 1.4em;
|
||||
padding: 6px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.l2fd-list {
|
||||
overflow-y: scroll;
|
||||
padding: var(--list-padding);
|
||||
}
|
||||
|
||||
.l2fd-main {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'fields-title options-title'
|
||||
'fields-list options-list'
|
||||
'fields-buttons options-buttons';
|
||||
grid-template-columns: 50% 50%;
|
||||
grid-template-rows: max-content auto max-content;
|
||||
|
||||
background-color: var(--l2fd-background-color);
|
||||
border: 1px solid var(--l2fd-border-color);
|
||||
color: var(--l2fd-font-color);
|
||||
}
|
||||
|
||||
.l2fd-listitem {
|
||||
color: var(--listitem-font-color);
|
||||
cursor: pointer;
|
||||
padding: var(--listitem-padding);
|
||||
margin-bottom: var(--listitem-spacing);
|
||||
background-color: var(--listitem-background-color);
|
||||
border: 1px solid var(--listitem-border-color);
|
||||
border-radius: var(--listitem-border-radius);
|
||||
}
|
||||
.l2fd-listitem:hover {
|
||||
color: var(--listitem-hover-font-color);
|
||||
background-color: var(--listitem-hover-background-color);
|
||||
border: 1px solid var(--listitem-hover-border-color);
|
||||
}
|
||||
.l2fd-listitem.selected {
|
||||
color: var(--listitem-selected-font-color);
|
||||
background-color: var(--listitem-selected-background-color);
|
||||
border: 1px solid var(--listitem-selected-border-color);
|
||||
}
|
||||
|
||||
.l2fd-itemgrid {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'label-title source-title'
|
||||
'label-field source-field';
|
||||
grid-template-columns: 50% 50%;
|
||||
grid-template-rows: auto auto;
|
||||
}
|
||||
|
||||
.l2fd-listitem label {
|
||||
cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.l2fd-buttonbar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 35px;
|
||||
}
|
||||
.l2fd-buttonbar > button {
|
||||
flex: 1 1 100%;
|
||||
background-color: var(--buttonbar-button-background-color);
|
||||
border: 1px solid var(--buttonbar-button-border-color);
|
||||
border-radius: var(--buttonbar-button-border-radius);
|
||||
color: var(--buttonbar-button-font-color);
|
||||
}
|
||||
.l2fd-buttonbar > button:hover {
|
||||
background-color: var(--buttonbar-button-hover-background-color);
|
||||
border: 1px solid var(--buttonbar-button-hover-border-color);
|
||||
color: var(--buttonbar-button-hover-font-color);
|
||||
}
|
||||
.l2fd-buttonbar > button.disabled {
|
||||
background-color: var(--buttonbar-button-disabled-background-color);
|
||||
border: 1px solid var(--buttonbar-button-disabled-border-color);
|
||||
color: var(--buttonbar-button-disabled-font-color);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
</style>
|
||||
@endpush
|
||||
@endonce
|
||||
|
||||
@push('js')
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
|
||||
Alpine.data('{{ $name }}', () => ({
|
||||
|
||||
_name: '{{ $name }}',
|
||||
_defaultValue: '{{ $value }}',
|
||||
_init: function() {
|
||||
this.fields = this.fromString(this._defaultValue);
|
||||
this.$watch('valueString', () => {
|
||||
this.$refs.input.form.dispatchEvent(new Event('change'));
|
||||
});
|
||||
},
|
||||
|
||||
/* Fields */
|
||||
fields: [],
|
||||
get _templateField() { return ({ options: [ this._templateOption ] }); },
|
||||
_selectedField: null,
|
||||
get selectedField() { return this._selectedField; },
|
||||
set selectedField(field) {
|
||||
this._selectedField = field;
|
||||
this.selectedOption = null;
|
||||
},
|
||||
get selectedFieldIndex() {
|
||||
return this.selectedField ? this.fields.indexOf(this.selectedField) : -1;
|
||||
},
|
||||
shiftSelectedField: function(offset) {
|
||||
this.shiftArrayValue(this.fields, this.selectedField, offset);
|
||||
},
|
||||
trashSelectedField: function() {
|
||||
this.fields.splice(this.fields.indexOf(this.selectedField), 1);
|
||||
this.selectedField = null;
|
||||
},
|
||||
addField: function() {
|
||||
let newField = JSON.parse(JSON.stringify(this._templateField));
|
||||
this.fields.push(newField);
|
||||
this.selectedField = newField;
|
||||
},
|
||||
|
||||
/* Options */
|
||||
get _templateOption() { return ({ label: '', datasource: '' }); },
|
||||
selectedOption: null,
|
||||
get selectedOptionIndex() {
|
||||
return this.selectedOption ? this.selectedField.options.indexOf(this.selectedOption) : -1;
|
||||
},
|
||||
shiftSelectedOption: function(offset) {
|
||||
this.shiftArrayValue(this.selectedField.options, this.selectedOption, offset);
|
||||
},
|
||||
trashSelectedOption: function() {
|
||||
this.selectedField.options.splice(this.selectedField.options.indexOf(this.selectedOption), 1);
|
||||
this.selectedOption = null;
|
||||
},
|
||||
addOption: function() {
|
||||
let newOption = JSON.parse(JSON.stringify(this._templateOption));
|
||||
this.selectedField.options.push(newOption);
|
||||
this.selectedOption = newOption;
|
||||
},
|
||||
|
||||
|
||||
/* Helpers */
|
||||
|
||||
shiftArrayValue: function(array, value, offset) {
|
||||
let oldIndex = array.indexOf(value);
|
||||
let newIndex = oldIndex + offset;
|
||||
newIndex = Math.max(newIndex, 0);
|
||||
newIndex = Math.min(newIndex, array.length);
|
||||
|
||||
array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
|
||||
},
|
||||
|
||||
get valueString() { return this.toString(this.fields); },
|
||||
onTest: function(a) {
|
||||
console.log('test', a);
|
||||
},
|
||||
|
||||
getFieldLabel: function(field) {
|
||||
return field.options.map(option => option.label).join(' | ');
|
||||
},
|
||||
fromString: function(string) {
|
||||
return string
|
||||
.split(';').filter(fieldString => fieldString !== '')
|
||||
.map(fieldString => ({
|
||||
options: fieldString
|
||||
.split('|').filter(optionString => optionString !== '')
|
||||
.map(optionString => {
|
||||
let [l,d] = optionString.split('=');
|
||||
return { label: l, datasource: d };
|
||||
})
|
||||
}));
|
||||
},
|
||||
toString: function(fields) {
|
||||
return fields
|
||||
.map(field => field.options
|
||||
.map(option => option.label + '=' + option.datasource)
|
||||
.join('|')
|
||||
)
|
||||
.join(';');
|
||||
},
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@php
|
||||
$selector = '[x-data="'.$name.'"]';
|
||||
@endphp
|
||||
@push('css')
|
||||
<style>
|
||||
|
||||
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
<div x-data="{{ $name }}" x-init="_init" class="l2fd-root">
|
||||
<input type="hidden" name="{{ $name }}" x-model="valueString" x-ref="input" />
|
||||
<div class="l2fd-main">
|
||||
<h1 class="l2fd-title" style="grid-area: fields-title">Fields</h1>
|
||||
<div class="l2fd-list" style="grid-area: fields-list">
|
||||
<template x-for="(field, index) in fields">
|
||||
<div
|
||||
x-bind:key="'field-' + index"
|
||||
x-bind:class="{
|
||||
'l2fd-listitem': true,
|
||||
'selected': selectedField === field
|
||||
}"
|
||||
x-on:click="selectedField = field" >
|
||||
<label><span x-text="index+1"></span>: <span x-text="getFieldLabel(field)"></span></label>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="l2fd-buttonbar" style="grid-area: fields-buttons">
|
||||
<button
|
||||
x-on:click.prevent="if(!$event.target.classList.contains('disabled')) shiftSelectedField(-1)"
|
||||
x-bind:class="{ 'disabled': !selectedField || selectedFieldIndex == 0 }"
|
||||
><i class="fa-solid fa-caret-up"></i></button>
|
||||
<button
|
||||
x-on:click.prevent="if(!$event.target.classList.contains('disabled')) shiftSelectedField(+1)"
|
||||
x-bind:class="{ 'disabled': !selectedField || selectedFieldIndex == fields.length - 1 }"
|
||||
><i class="fa-solid fa-caret-down"></i></button>
|
||||
<button
|
||||
x-on:click.prevent="if(!$event.target.classList.contains('disabled')) addField()"
|
||||
x-bind:class="{}"
|
||||
><i class="fa-solid fa-plus"></i></button>
|
||||
<button
|
||||
x-on:click.prevent="if(!$event.target.classList.contains('disabled')) trashSelectedField()"
|
||||
x-bind:class="{ 'disabled': !selectedField }"
|
||||
><i class="fa-solid fa-trash"></i></button>
|
||||
</div>
|
||||
|
||||
<h1 class="l2fd-title" style="grid-area: options-title">Options</h1>
|
||||
<div class="l2fd-list" style="grid-area: options-list">
|
||||
<template x-if="selectedField">
|
||||
<template x-for="(option, index) in selectedField.options">
|
||||
<div
|
||||
x-bind:key="'option-' + index"
|
||||
x-bind:class="{
|
||||
'l2fd-listitem': true,
|
||||
'l2fd-itemgrid': true,
|
||||
'selected': selectedOption == option
|
||||
}"
|
||||
x-on:click="selectedOption = option" >
|
||||
<label style="grid-area: label-title">Label</label>
|
||||
<input style="grid-area: label-field" x-model="option.label" />
|
||||
<label style="grid-area: source-title">DataSource</label>
|
||||
<input style="grid-area: source-field" x-model="option.datasource" />
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template x-if="!selectedField">
|
||||
<div>Please select a field</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="l2fd-buttonbar" style="grid-area: options-buttons">
|
||||
<button
|
||||
x-on:click.prevent="if(!$event.target.classList.contains('disabled')) shiftSelectedOption(-1)"
|
||||
x-bind:class="{ 'disabled': !selectedOption || selectedOptionIndex == 0 }"
|
||||
><i class="fa-solid fa-caret-up"></i></button>
|
||||
<button
|
||||
x-on:click.prevent="if(!$event.target.classList.contains('disabled')) shiftSelectedOption(+1)"
|
||||
x-bind:class="{ 'disabled': !selectedOption || selectedOptionIndex == selectedField.options.length - 1 }"
|
||||
><i class="fa-solid fa-caret-down"></i></button>
|
||||
<button
|
||||
x-on:click.prevent="if(!$event.target.classList.contains('disabled')) addOption()"
|
||||
x-bind:class="{}"
|
||||
><i class="fa-solid fa-plus"></i></button>
|
||||
<button
|
||||
x-on:click.prevent="if(!$event.target.classList.contains('disabled')) trashSelectedOption()"
|
||||
x-bind:class="{ 'disabled': !selectedOption }"
|
||||
><i class="fa-solid fa-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -21,6 +21,7 @@
|
|||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
function refreshPreview() {
|
||||
var settingsOverride = {
|
||||
'settings': Object.assign({}, ...$('#settingsForm')
|
||||
|
@ -212,7 +213,7 @@
|
|||
<div class="col-md-9">
|
||||
{{ Form::select('label2_2d_target', ['hardware_id'=>'/hardware/{id} ('.trans('admin/settings/general.default').')', 'ht_tag'=>'/ht/{asset_tag}'], old('label2_2d_target', $setting->label2_2d_target), [ 'class'=>'select2 col-md-4', 'aria-label'=>'label2_2d_target' ]) }}
|
||||
{!! $errors->first('label2_2d_target', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
<p class="help-block">{!! trans('admin/settings/general.label2_2d_target_help') !!}</p>
|
||||
<p class="help-block">{{ trans('admin/settings/general.label2_2d_target_help') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -222,14 +223,9 @@
|
|||
{{ Form::label('label2_fields', trans('admin/settings/general.label2_fields')) }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{ Form::text('label2_fields', old('label2_fields', $setting->label2_fields), [ 'class'=>'form-control', 'aria-label'=>'label2_fields' ]) }}
|
||||
@include('partials.label2-field-definitions', [ 'name' => 'label2_fields', 'value' => old('label2_fields', $setting->label2_fields) ])
|
||||
{!! $errors->first('label2_fields', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<p class="help-block">{!! trans('admin/settings/general.label2_fields_help') !!}</p>
|
||||
<p class="help-block">
|
||||
{!! trans('admin/settings/general.label2_fields_help_semi') !!}.<br />
|
||||
{!! trans('admin/settings/general.label2_fields_help_pipe') !!}.<br />
|
||||
{!! trans('admin/settings/general.label2_fields_help_once') !!}.
|
||||
</p>
|
||||
<p class="help-block">{{ trans('admin/settings/general.label2_fields_help') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Reference in a new issue