Sfoglia il codice sorgente

Merge pull request #32 from qor5/support-sorting-autocomplete

Support sorting autocomplete
xin 1 anno fa
parent
commit
89f0c4940b

File diff suppressed because it is too large
+ 0 - 0
vuetify/dist/vuetify.min.css


+ 25 - 5
vuetifyx/autocomplete-fix.go

@@ -18,7 +18,7 @@ func VXAutocomplete(children ...h.HTMLComponent) (r *VXAutocompleteBuilder) {
 	r = &VXAutocompleteBuilder{
 		tag: h.Tag("vx-autocomplete").Children(children...),
 	}
-	r.Multiple(true)
+	r.Multiple(true).Chips(true).DeletableChips(true).Clearable(true)
 
 	return
 }
@@ -28,18 +28,38 @@ func (b *VXAutocompleteBuilder) ErrorMessages(v ...string) (r *VXAutocompleteBui
 	return b
 }
 
+func (b *VXAutocompleteBuilder) FieldName(v string) (r *VXAutocompleteBuilder) {
+	b.tag.Attr(web.VFieldName(v)...)
+	return b
+}
+
+func (b *VXAutocompleteBuilder) SelectedItems(v interface{}) (r *VXAutocompleteBuilder) {
+	b.selectedItems = v
+	return b
+}
+
+func (b *VXAutocompleteBuilder) HasIcon(v bool) (r *VXAutocompleteBuilder) {
+	b.tag.Attr("has-icon", v)
+	return b
+}
+
+func (b *VXAutocompleteBuilder) Sorting(v bool) (r *VXAutocompleteBuilder) {
+	b.tag.Attr("sorting", v)
+	return b
+}
+
 func (b *VXAutocompleteBuilder) Items(v interface{}) (r *VXAutocompleteBuilder) {
 	b.items = v
 	return b
 }
 
-func (b *VXAutocompleteBuilder) FieldName(v string) (r *VXAutocompleteBuilder) {
-	b.tag.Attr(web.VFieldName(v)...)
+func (b *VXAutocompleteBuilder) ChipColor(v string) (r *VXAutocompleteBuilder) {
+	b.tag.Attr("chip-color", v)
 	return b
 }
 
-func (b *VXAutocompleteBuilder) SelectedItems(v interface{}) (r *VXAutocompleteBuilder) {
-	b.selectedItems = v
+func (b *VXAutocompleteBuilder) ChipTextColor(v string) (r *VXAutocompleteBuilder) {
+	b.tag.Attr("chip-text-color", v)
 	return b
 }
 

File diff suppressed because it is too large
+ 0 - 0
vuetifyx/vuetifyxjs/dist/vuetifyxjs.css


File diff suppressed because it is too large
+ 0 - 0
vuetifyx/vuetifyxjs/dist/vuetifyxjs.umd.min.js


+ 124 - 65
vuetifyx/vuetifyxjs/src/components/Autocomplete.tsx

@@ -1,4 +1,5 @@
 import Vue, { CreateElement, VNode, VNodeData } from 'vue';
+import draggable from 'vuedraggable';
 import { VAutocomplete, VPagination } from 'vuetify/lib';
 import { Core, SelectedItems, slotTemplates } from './Helpers';
 
@@ -7,6 +8,7 @@ export default Vue.extend({
 	components: {
 		vpagination: VPagination,
 		vautocomplete: VAutocomplete,
+		vdraggable: draggable,
 	},
 
 	props: {
@@ -17,6 +19,9 @@ export default Vue.extend({
 		cacheItems: Boolean,
 		hideSelected: Boolean,
 		hideDetails: Boolean,
+		sorting: Boolean,
+		chipColor: String,
+		chipTextColor: String,
 		items: {
 			type: Array,
 			default: () => ([]),
@@ -71,6 +76,33 @@ export default Vue.extend({
 				this.loadRemoteItems();
 			}
 		},
+		changeStatus(vals: any) {
+			const cachedSelectedItems: any[] = [];
+			vals.forEach((val: any) => {
+				this.listItems.forEach((item: any) => {
+					if (val == item.value) {
+						cachedSelectedItems.push(item);
+						return;
+					}
+				})
+			})
+
+			this.cachedSelectedItems = (cachedSelectedItems) as [];
+			this.value = vals;
+			this.$emit("change", vals);
+		},
+		removeItem(v: any) {
+			return () => {
+				this.value = this.value.filter(element => element != v);
+				this.changeStatus(this.value);
+			}
+		},
+		changeOrder(vs: any){
+			this.cachedSelectedItems = vs
+			const vals = this.cachedSelectedItems.map((item: any) => item.value)
+			this.value = vals as any;
+			this.$emit('change', vals);
+		},
 	},
 
 	created() {
@@ -106,7 +138,6 @@ export default Vue.extend({
 	render(h: CreateElement): VNode {
 		const {
 			remoteUrl,
-			multiple,
 			hideDetails,
 		} = this.$props;
 
@@ -116,6 +147,7 @@ export default Vue.extend({
 		} = this.$props
 
 		const slots: VNode[] = slotTemplates(h, this.$slots);
+
 		if (remoteUrl) {
 			hideSelected = true;
 			cacheItems = false;
@@ -170,84 +202,78 @@ export default Vue.extend({
 					</template>
 				)
 			}
+		}
 
-			if (this.hasIcon){
-				this.$scopedSlots["item"] = (props: any)  => {
-					const nodes: VNode[] = [];
-					nodes.push(
-						<v-list-item-avatar tile>
-							<img src={props.item.icon}/>
-						</v-list-item-avatar>
-					)
-					nodes.push(
-						<v-list-item-content>
-							<v-list-item-title v-html={props.item.text}>{props.item.text}</v-list-item-title>
-						</v-list-item-content>
-					)
-					return nodes;
+		if (this.hasIcon){
+			this.$scopedSlots["item"] = (props: any)  => {
+				const nodes: VNode[] = [];
+				nodes.push(
+					<v-list-item-avatar tile>
+						<img src={props.item.icon}/>
+					</v-list-item-avatar>
+				)
+				nodes.push(
+					<v-list-item-content>
+						<v-list-item-title v-html={props.item.text}>{props.item.text}</v-list-item-title>
+					</v-list-item-content>
+				)
+				return nodes;
+			}
+		}
+
+		if (this.$attrs.chips && (this.hasIcon || remoteUrl)){
+			this.$scopedSlots["selection"] = (props: any)  => {
+				const nodes: VNode[] = [];
+				if (this.sorting){
+					return nodes
 				}
 
-				this.$scopedSlots["selection"] = (props: any)  => {
-					const nodes: VNode[] = [];
-					const nodeData: VNodeData = {
-						props: {
-							...props.attrs,
-							close: true,
-						},
-						on: {
-							"click:close": () => {
-								this.value.splice(this.value.indexOf(props.item.value as never), 1)
-								this.$emit("change", this.value);
-							},
+				const nodeData: VNodeData = {
+					props: {
+						draggable: true,
+						close: true,
+						color: this.chipColor,
+						"text-color": this.chipTextColor,
+					},
+					on: {
+						"click:close": () => {
+							this.value.splice(this.value.indexOf(props.item.value as never), 1)
+							this.changeStatus(this.value);
 						},
-
-					}
-					nodes.push(
-					<v-chip {...nodeData}>
-						<v-avatar left>
-							<v-img src={props.item.icon}></v-img>
-						</v-avatar>
-						{props.item.text}
-					</v-chip>
-					)
-					return nodes;
+					},
 				}
+				nodes.push(
+				<v-chip {...nodeData}>
+					{ this.hasIcon ?
+					<v-avatar left>
+						<v-img src={props.item.icon}></v-img>
+					</v-avatar>
+					: null }
+					{props.item.text}
+				</v-chip>
+				)
+				return nodes;
 			}
 		}
 
-		const data: VNodeData = {
+		const autocompleteData: VNodeData = {
 			props: {
-				...{
-					// solo: true,
-					multiple,
-					chips: true,
-					deletableChips: multiple,
-					clearable: true,
-					hideSelected,
-					cacheItems,
-					hideDetails,
-				},
 				...this.$attrs,
+				...this.$props,
 				...{
 					items: this.listItems,
 					value: this.value,
 					loading: this.isLoading,
+					clearable: this.sorting ? false : this.$attrs.clearable,
+					hideSelected,
+					cacheItems,
+					hideDetails,
 				},
 			},
-
+			class:  this.sorting ? "v-autocomplete-sorting" : "",
 			on: {
 				...{
-					change: (vals: any) => {
-						const items: any[] = [];
-						this.listItems.forEach((item: any) => {
-							if (vals.includes(item.value)) {
-								items.push(item);
-							}
-						})
-						this.cachedSelectedItems = (items) as [];
-						this.value = vals;
-						this.$emit("change", vals);
-					},
+					change: this.changeStatus,
 					focus: (e: any) => {
 						this.searchKeyword = '';
 					},
@@ -260,11 +286,44 @@ export default Vue.extend({
 				...this.$scopedSlots,
 			}
 		};
+
 		return (
-			<vautocomplete {...data}>
-				{slots}
+		<div>
+			{(this.sorting && this.cachedSelectedItems.length>0)  ?
+			<v-card>
+				<v-list>
+					<vx-draggable animation='300' handle='.handle' value={this.cachedSelectedItems} onInput={this.changeOrder}>
+					{this.cachedSelectedItems.map((item: any, i) => {
+						return (
+						<div key={item.value}>
+							<v-list-item>
+							{this.hasIcon ?
+								<v-list-item-avatar tile>
+									<v-img src={item.icon}></v-img>
+								</v-list-item-avatar>
+								: null}
+								<v-list-item-content>
+									<v-list-item-title>{item.text}</v-list-item-title>
+								</v-list-item-content>
+								<v-list-item-icon>
+									<v-btn icon="true">
+										<v-icon onClick={this.removeItem(item.value)}>delete</v-icon>
+									</v-btn>
+									<v-icon class="handle">reorder</v-icon>
+								</v-list-item-icon>
+							</v-list-item>
+							{i < this.cachedSelectedItems.length - 1 ? <v-divider key={i}></v-divider> : null}
+						</div>
+						)
+					})}
+					</vx-draggable>
+				</v-list>
+			</v-card>
+			: null}
+			<vautocomplete {...autocompleteData}>
+			{slots}
 			</vautocomplete>
+		</div>
 		);
 	},
-});
-
+})

+ 59 - 31
vuetifyx/vuetifyxjs/src/components/SelectMany.vue

@@ -1,32 +1,41 @@
 <template>
 	<div>
 		<label class="v-label theme--light" v-html="label"></label>
-		<v-card v-if='internalSelectedItems.length > 0'>
+		<v-card v-if="internalSelectedItems.length > 0">
 			<v-list>
 				<vx-draggable
-					v-model='internalSelectedItems'
-					animation='300'
+					v-model="internalSelectedItems"
+					animation="300"
 					handle=".handle"
-					@change="changeOrder">
-					<div v-for='(item, index) in internalSelectedItems' :key='item.id'>
+					@change="changeOrder"
+				>
+					<div
+						v-for="(item, index) in internalSelectedItems"
+						:key="item.id"
+					>
 						<v-list-item>
 							<v-list-item-avatar tile>
 								<v-img :src="item[itemImage]"></v-img>
 							</v-list-item-avatar>
 							<v-list-item-content>
-								<v-list-item-title>{{ item[itemText] }}
+								<v-list-item-title
+									>{{ item[itemText] }}
 								</v-list-item-title>
 							</v-list-item-content>
 							<v-list-item-icon>
-								<v-btn :icon="true" @click="removeItem(item[itemValue])">
+								<v-btn
+									:icon="true"
+									@click="removeItem(item[itemValue])"
+								>
 									<v-icon>delete</v-icon>
 								</v-btn>
 								<v-icon class="handle">reorder</v-icon>
 							</v-list-item-icon>
 						</v-list-item>
 						<v-divider
-							v-if='index < internalSelectedItems.length - 1'
-							:key='index'></v-divider>
+							v-if="index < internalSelectedItems.length - 1"
+							:key="index"
+						></v-divider>
 					</div>
 				</vx-draggable>
 			</v-list>
@@ -49,11 +58,12 @@
 			<template v-slot:item="data">
 				<template>
 					<v-list-item-avatar tile>
-						<img :src="data.item[itemImage]">
+						<img :src="data.item[itemImage]" />
 					</v-list-item-avatar>
 					<v-list-item-content>
 						<v-list-item-title
-							v-html="data.item[itemText]"></v-list-item-title>
+							v-html="data.item[itemText]"
+						></v-list-item-title>
 					</v-list-item-content>
 				</template>
 			</template>
@@ -63,7 +73,7 @@
 
 <script>
 export default {
-	name: 'vx-selectmany',
+	name: "vx-selectmany",
 	props: {
 		items: {
 			type: Array,
@@ -78,23 +88,23 @@ export default {
 		},
 		itemValue: {
 			type: String,
-			default: 'id',
+			default: "id",
 		},
 		itemText: {
 			type: String,
-			default: 'text',
+			default: "text",
 		},
 		itemImage: {
 			type: String,
-			default: 'image',
+			default: "image",
 		},
 		label: {
 			type: String,
-			default: '',
+			default: "",
 		},
 		addItemLabel: {
 			type: String,
-			default: '',
+			default: "",
 		},
 	},
 	data() {
@@ -102,7 +112,7 @@ export default {
 			internalSelectedItems: [],
 			internalItems: [],
 			autocompleteValue: [],
-			searchKeyword: '',
+			searchKeyword: "",
 			isLoading: false,
 			noFilter: false,
 		};
@@ -124,22 +134,30 @@ export default {
 				return;
 			}
 			this.isLoading = true;
-			this.searchItemsFunc(val).then(r => {
-				this.internalItems = r.data || [];
-			}).finally(() => {
-				this.isLoading = false;
-			});
+			this.searchItemsFunc(val)
+				.then((r) => {
+					this.internalItems = r.data || [];
+				})
+				.finally(() => {
+					this.isLoading = false;
+				});
 		},
 	},
 	methods: {
 		addItem(event) {
 			this.autocompleteValue = [];
-			if (this.internalSelectedItems.find(
-				element => element[this.itemValue] == event)) {
+			if (
+				this.internalSelectedItems.find(
+					(element) => element[this.itemValue] == event
+				)
+			) {
 				return;
 			}
 			this.internalSelectedItems.push(
-				this.internalItems.find(element => element[this.itemValue] == event));
+				this.internalItems.find(
+					(element) => element[this.itemValue] == event
+				)
+			);
 			this.setValue();
 		},
 		changeOrder(event) {
@@ -147,7 +165,8 @@ export default {
 		},
 		removeItem(id) {
 			this.internalSelectedItems = this.internalSelectedItems.filter(
-				element => element[this.itemValue] != id);
+				(element) => element[this.itemValue] != id
+			);
 			this.setValue();
 		},
 		search(val) {
@@ -157,12 +176,15 @@ export default {
 			this.searchKeyword = val;
 		},
 		focus() {
-			this.search('');
+			this.search("");
 		},
 		setValue() {
-			this.$emit('input', this.internalSelectedItems.map((i) => {
-				return i[this.itemValue];
-			}));
+			this.$emit(
+				"input",
+				this.internalSelectedItems.map((i) => {
+					return i[this.itemValue];
+				})
+			);
 		},
 	},
 };
@@ -174,3 +196,9 @@ export default {
 	cursor: move;
 }
 </style>
+
+<style>
+.v-autocomplete-sorting .v-label--active {
+	transform: none;
+}
+</style>

Some files were not shown because too many files changed in this diff