Skip to content

Commit

Permalink
Add singleSelect support
Browse files Browse the repository at this point in the history
  • Loading branch information
Tarpsvo committed Nov 5, 2019
1 parent ca298f2 commit 196aa52
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 30 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,12 @@ Possible options you can pass to the field using the option name as a function,
| `options` | Array | [] | Options in an array as key-value pairs (`['id' => 'value']`). |
| `placeholder` | String | Field name | The placeholder string for the input. |
| `max` | Number | Infinite | The maximum number of options a user can select. |
| `singleSelect` | Boolean | false | Makes the field act as a single select which also means the saved value will not be an array. |
| `saveAsJSON` | Boolean | false | When you have a SQL JSON column, you can force the field to save the values as JSON. By default, values are saved as a stringified array. |
| `optionsLimit` | Number | 1000 | The maximum number of options displayed at once. Other options are still accessible through searching. |
| `resolveForPageResponseUsing` | Callable | null | Only for use in conjunction with [Page Manager](https://github.com/optimistdigital/nova-page-manager). Allows you to format the value before it is returned through the API. |
| `nullable` | Boolean | false | If the field is nullable an empty select will result in `null` else an empty array (`[]`) is stored. |
| `reorderable` | Boolean | false | Enables (or disables) the reordering functionality of the multiselect field. |
| `resolveForPageResponseUsing` | Callable | null | Only for use in conjunction with [Page Manager](https://github.com/optimistdigital/nova-page-manager). Allows you to format the value before it is returned through the API. |

## Localization

Expand Down
2 changes: 1 addition & 1 deletion dist/js/multiselect-field.js

Large diffs are not rendered by default.

19 changes: 14 additions & 5 deletions resources/js/components/DetailField.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
<template>
<panel-item :field="field">
<template slot="value">
<div class="relative rounded-t-lg rounded-b-lg shadow bg-30 border border-60" style="background-clip: border-box;" v-if="values">
<div class="bg-white overflow-hidden rounded-b-lg rounded-t-lg">
<div class="border-b border-50 cursor-text font-mono text-sm py-2 px-4" v-for="(value, i) of values" :key="i">
{{ value }}
<div v-if="isMultiselect">
<div class="relative rounded-t-lg rounded-b-lg shadow bg-30 border border-60" style="background-clip: border-box;" v-if="values">
<div class="bg-white overflow-hidden rounded-b-lg rounded-t-lg">
<div class="border-b border-50 cursor-text font-mono text-sm py-2 px-4" v-for="(value, i) of values" :key="i">
{{ value }}
</div>
</div>
</div>

<div v-else>—</div>
</div>

<div v-else>—</div>
<div v-else>
{{ (value && value.label) || '' }}
</div>
</template>
</panel-item>
</template>
Expand All @@ -32,6 +38,9 @@ export default {
.filter(val => !!val)
.map(val => val.label);
},
value() {
return this.field.options.find(opt => String(opt.value) === String(this.field.value));
},
},
};
</script>
36 changes: 22 additions & 14 deletions resources/js/components/FormField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
:options="options"
:class="errorClasses"
:placeholder="field.placeholder || field.name"
:close-on-select="field.max === 1"
:close-on-select="field.max === 1 || !isMultiselect"
:clear-on-select="false"
:multiple="true"
:multiple="isMultiselect"
:max="field.max || null"
:optionsLimit="field.optionsLimit || 1000"
:limitText="count => __('novaMultiselect.limitText', { count })"
Expand Down Expand Up @@ -94,26 +94,34 @@ export default {
methods: {
setInitialValue() {
const valuesArray = this.getInitialFieldValuesArray();
if (this.isMultiselect) {
const valuesArray = this.getInitialFieldValuesArray();
if (valuesArray) {
this.value = valuesArray
.map(val => this.field.options.find(opt => String(opt.value) === String(val)))
.filter(Boolean);
if (valuesArray) {
this.value = valuesArray
.map(val => this.field.options.find(opt => String(opt.value) === String(val)))
.filter(Boolean);
} else {
this.value = [];
}
} else {
this.value = [];
this.value = this.field.value;
}
},
fill(formData) {
let value;
if (this.value && this.value.length) {
value = JSON.stringify(this.value.map(v => v.value));
if (this.isMultiselect) {
let value;
if (this.value && this.value.length) {
value = JSON.stringify(this.value.map(v => v.value));
} else {
value = this.field.nullable ? '' : JSON.stringify([]);
}
formData.append(this.field.attribute, value);
} else {
value = this.field.nullable ? '' : JSON.stringify([]);
formData.append(this.field.attribute, (this.value && this.value.value) || '');
}
return formData.append(this.field.attribute, value);
},
handleChange(value) {
Expand Down
23 changes: 14 additions & 9 deletions resources/js/components/IndexField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,23 @@ export default {
computed: {
value() {
const valuesArray = this.getInitialFieldValuesArray();
if (!valuesArray) return '-';
if (this.isMultiselect) {
const valuesArray = this.getInitialFieldValuesArray();
if (!valuesArray) return '-';
const values = valuesArray
.map(val => this.field.options.find(opt => String(opt.value) === val))
.filter(val => !!val)
.map(val => val.label);
const values = valuesArray
.map(val => this.field.options.find(opt => String(opt.value) === String(val)))
.filter(val => !!val)
.map(val => val.label);
const joinedValues = values.join(', ');
if (joinedValues.length <= 40) return joinedValues;
const joinedValues = values.join(', ');
if (joinedValues.length <= 40) return joinedValues;
return this.__('novaMultiselect.nItemsSelected', { count: values.length });
return this.__('novaMultiselect.nItemsSelected', { count: values.length });
} else {
const value = this.field.options.find(opt => String(opt.value) === String(this.field.value));
return (value && value.label) || '';
}
},
},
};
Expand Down
5 changes: 5 additions & 0 deletions resources/js/mixins/HandlesFieldValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ export default {
}
},
},
computed: {
isMultiselect() {
return !this.field.singleSelect;
},
},
};
13 changes: 13 additions & 0 deletions src/Multiselect.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ public function reorderable($reorderable = true)
return $this->withMeta(['reorderable' => $reorderable]);
}

/**
* Enables the field to be used as a single select.
*
* This forces the value saved to be a single value and not an array.
*
* @param bool $singleSelect
* @return \OptimistDigital\MultiselectField\Multiselect
**/
public function singleSelect($singleSelect = true)
{
return $this->withMeta(['singleSelect' => $singleSelect]);
}

public function resolveResponseValue($value, $templateModel)
{
$parsedValue = isset($value) ? $this->saveAsJSON ? $value : json_decode($value) : null;
Expand Down

0 comments on commit 196aa52

Please sign in to comment.