Merge pull request #133 from Conor-Mack/feature/list-selectitems-store-context
Using store passed through context to manage ListItems
This commit is contained in:
commit
0acdd97a82
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount, onDestroy, getContext } from "svelte"
|
import { onMount, onDestroy } from "svelte"
|
||||||
import Formfield from "../Common/Formfield.svelte"
|
import Formfield from "../Common/Formfield.svelte"
|
||||||
import { fieldStore } from "../Common/FormfieldStore.js"
|
import { fieldStore } from "../Common/FormfieldStore.js"
|
||||||
import ClassBuilder from "../ClassBuilder.js"
|
import ClassBuilder from "../ClassBuilder.js"
|
||||||
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
export let onClick = item => {}
|
export let onClick = item => {}
|
||||||
|
|
||||||
|
export let _bb
|
||||||
|
|
||||||
export let id = ""
|
export let id = ""
|
||||||
export let label = ""
|
export let label = ""
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
@ -16,15 +18,15 @@
|
||||||
|
|
||||||
let instance = null
|
let instance = null
|
||||||
let checkbox = null
|
let checkbox = null
|
||||||
|
let context = _bb.getContext("BBMD:input:context")
|
||||||
let context = getContext("BBMD:input:context")
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (!!checkbox) {
|
if (!!checkbox) {
|
||||||
instance = new MDCCheckbox(checkbox)
|
instance = new MDCCheckbox(checkbox)
|
||||||
instance.indeterminate = indeterminate
|
instance.indeterminate = indeterminate
|
||||||
if (context !== "list-item") {
|
if (context !== "list-item") {
|
||||||
let fieldStore = getContext("BBMD:field-element")
|
//TODO: Fix this connected to Formfield context issue
|
||||||
|
let fieldStore = _bb.getContext("BBMD:field-element")
|
||||||
fieldStore.setInput(instance)
|
fieldStore.setInput(instance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,14 @@
|
||||||
import ClassBuilder from "../ClassBuilder.js"
|
import ClassBuilder from "../ClassBuilder.js"
|
||||||
import { fieldStore } from "./FormfieldStore.js"
|
import { fieldStore } from "./FormfieldStore.js"
|
||||||
import { MDCFormField } from "@material/form-field"
|
import { MDCFormField } from "@material/form-field"
|
||||||
import { onMount, onDestroy, setContext } from "svelte"
|
import { onMount, onDestroy } from "svelte"
|
||||||
|
|
||||||
const cb = new ClassBuilder("form-field")
|
const cb = new ClassBuilder("form-field")
|
||||||
|
|
||||||
let store
|
let store
|
||||||
const unsubscribe = fieldStore.subscribe(s => (store = s))
|
const unsubscribe = fieldStore.subscribe(s => (store = s))
|
||||||
|
|
||||||
|
export let _bb
|
||||||
export let id = ""
|
export let id = ""
|
||||||
export let label = ""
|
export let label = ""
|
||||||
export let alignEnd = false
|
export let alignEnd = false
|
||||||
|
@ -23,7 +24,8 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (!!formField) fieldStore.set(new MDCFormField(formField))
|
if (!!formField) fieldStore.set(new MDCFormField(formField))
|
||||||
setContext("BBMD:field-element", fieldStore)
|
//TODO: Fix this, _bb is coming back undefined
|
||||||
|
// _bb.setContext("BBMD:field-element", fieldStore)
|
||||||
})
|
})
|
||||||
|
|
||||||
onDestroy(unsubscribe)
|
onDestroy(unsubscribe)
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
|
function createItemsStore(componentOnSelect) {
|
||||||
|
const { subscribe, set, update } = writable([]);
|
||||||
|
|
||||||
|
function addItem(item) {
|
||||||
|
update(items => {
|
||||||
|
return [...items, item]
|
||||||
|
})
|
||||||
|
if (componentOnSelect) {
|
||||||
|
componentOnSelect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSingleItem(item) {
|
||||||
|
set([item])
|
||||||
|
if (componentOnSelect) {
|
||||||
|
componentOnSelect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeItem(itemId) {
|
||||||
|
update(items => {
|
||||||
|
let index = getItemIdx(items, itemId)
|
||||||
|
items.splice(index, 1);
|
||||||
|
return items;
|
||||||
|
})
|
||||||
|
if (componentOnSelect) {
|
||||||
|
componentOnSelect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearItems() {
|
||||||
|
set([]);
|
||||||
|
if (componentOnSelect) {
|
||||||
|
componentOnSelect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItemIdx(items, itemId) {
|
||||||
|
return items.findIndex(i => i._id === itemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
addItem,
|
||||||
|
addSingleItem,
|
||||||
|
removeItem,
|
||||||
|
clearItems,
|
||||||
|
getItemIdx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createItemsStore
|
|
@ -1,10 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount, getContext } from "svelte"
|
import { onMount, getContext, setContext } from "svelte"
|
||||||
import { MDCList } from "@material/list"
|
import { MDCList } from "@material/list"
|
||||||
|
import createItemsStore from "../Common/ItemStore.js"
|
||||||
import { MDCRipple } from "@material/ripple"
|
import { MDCRipple } from "@material/ripple"
|
||||||
import ListItem from "./ListItem.svelte"
|
import ListItem from "./ListItem.svelte"
|
||||||
import ClassBuilder from "../ClassBuilder.js"
|
import ClassBuilder from "../ClassBuilder.js"
|
||||||
|
|
||||||
|
let selectedItems
|
||||||
|
|
||||||
export let _bb
|
export let _bb
|
||||||
const cb = new ClassBuilder("list", ["one-line"])
|
const cb = new ClassBuilder("list", ["one-line"])
|
||||||
|
|
||||||
|
@ -13,20 +16,39 @@
|
||||||
|
|
||||||
export let onSelect = selectedItems => {}
|
export let onSelect = selectedItems => {}
|
||||||
|
|
||||||
export let variant = "one-line"
|
|
||||||
//items: [{text: string | {primary: string, secondary: string}, value: any, selected: bool}...n]
|
|
||||||
export let items = []
|
|
||||||
export let singleSelection = false
|
export let singleSelection = false
|
||||||
|
export let variant = "two-line"
|
||||||
export let inputElement = null
|
export let inputElement = null
|
||||||
|
|
||||||
|
let selectedItemsStore
|
||||||
|
|
||||||
let role = "listbox"
|
let role = "listbox"
|
||||||
|
|
||||||
|
function createOrAcceptItemStore() {
|
||||||
|
let store = _bb.getContext("BBMD:list:selectItemStore")
|
||||||
|
if (!!store) {
|
||||||
|
selectedItemsStore = store
|
||||||
|
} else {
|
||||||
|
selectedItemsStore = createItemsStore(() => onSelect($selectedItemsStore))
|
||||||
|
_bb.setContext("BBMD:list:selectItemStore", selectedItemsStore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
createOrAcceptItemStore()
|
||||||
|
|
||||||
|
_bb.setContext("BBMD:list:props", {
|
||||||
|
inputElement,
|
||||||
|
variant,
|
||||||
|
singleSelection,
|
||||||
|
})
|
||||||
if (!!list) {
|
if (!!list) {
|
||||||
|
if (!inputElement) {
|
||||||
instance = new MDCList(list)
|
instance = new MDCList(list)
|
||||||
instance.singleSelection = singleSelection
|
instance.singleSelection = singleSelection
|
||||||
instance.listElements.map(element => new MDCRipple(element))
|
instance.listElements.map(element => new MDCRipple(element))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let context = getContext("BBMD:list:context")
|
let context = getContext("BBMD:list:context")
|
||||||
if (context === "menu") {
|
if (context === "menu") {
|
||||||
|
@ -39,28 +61,6 @@
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function handleSelectedItem(item) {
|
|
||||||
if (!item.disabled) {
|
|
||||||
if (singleSelection || inputElement === "radiobutton") {
|
|
||||||
items.forEach(i => {
|
|
||||||
if (i.selected) i.selected = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let idx = items.indexOf(item)
|
|
||||||
if (!!item.selected) {
|
|
||||||
items[idx].selected = !item.selected
|
|
||||||
} else {
|
|
||||||
items[idx].selected = true
|
|
||||||
}
|
|
||||||
onSelect(items.filter(item => item.selected))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: useDoubleLine =
|
|
||||||
variant == "two-line" &&
|
|
||||||
items.every(i => typeof i.text == "object" && "primary" in i.text)
|
|
||||||
|
|
||||||
$: list && _bb.attachChildren(list)
|
$: list && _bb.attachChildren(list)
|
||||||
|
|
||||||
$: modifiers = { variant }
|
$: modifiers = { variant }
|
||||||
|
|
|
@ -3,15 +3,23 @@
|
||||||
import { Radiobutton } from "../Radiobutton"
|
import { Radiobutton } from "../Radiobutton"
|
||||||
import { Checkbox } from "../Checkbox"
|
import { Checkbox } from "../Checkbox"
|
||||||
import ClassBuilder from "../ClassBuilder.js"
|
import ClassBuilder from "../ClassBuilder.js"
|
||||||
|
import shortid from "shortid"
|
||||||
|
|
||||||
const cb = new ClassBuilder("list-item")
|
const cb = new ClassBuilder("list-item")
|
||||||
|
|
||||||
export let onClick = item => {}
|
export let onClick = item => {}
|
||||||
|
|
||||||
|
let _id
|
||||||
|
let listProps = null
|
||||||
|
|
||||||
|
let selectedItems
|
||||||
|
|
||||||
|
export let _bb
|
||||||
|
|
||||||
|
export let value = null
|
||||||
export let text = ""
|
export let text = ""
|
||||||
export let secondaryText = ""
|
export let secondaryText = ""
|
||||||
export let variant = "two-line"
|
|
||||||
export let inputElement = null
|
|
||||||
export let leadingIcon = ""
|
export let leadingIcon = ""
|
||||||
export let trailingIcon = ""
|
export let trailingIcon = ""
|
||||||
export let selected = false
|
export let selected = false
|
||||||
|
@ -20,32 +28,63 @@
|
||||||
let role = "option"
|
let role = "option"
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
let context = getContext("BBMD:list:context")
|
_id = shortid.generate()
|
||||||
|
|
||||||
|
selectedItems = _bb.getContext("BBMD:list:selectItemStore")
|
||||||
|
|
||||||
|
listProps = _bb.getContext("BBMD:list:props")
|
||||||
|
|
||||||
|
let context = _bb.getContext("BBMD:list:context")
|
||||||
if (context === "menu") {
|
if (context === "menu") {
|
||||||
role = "menuitem"
|
role = "menuitem"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
$: if (!!inputElement) {
|
function handleClick() {
|
||||||
setContext("BBMD:input:context", "list-item")
|
let item = {
|
||||||
|
_id,
|
||||||
|
value,
|
||||||
|
text,
|
||||||
|
secondaryText,
|
||||||
|
selected,
|
||||||
|
disabled,
|
||||||
|
}
|
||||||
|
if (!disabled) {
|
||||||
|
if (
|
||||||
|
listProps.singleSelection ||
|
||||||
|
listProps.inputElement === "radiobutton"
|
||||||
|
) {
|
||||||
|
selectedItems.addSingleItem(item)
|
||||||
|
} else {
|
||||||
|
let idx = selectedItems.getItemIdx($selectedItems, _id)
|
||||||
|
if (idx > -1) {
|
||||||
|
selectedItems.removeItem(_id)
|
||||||
|
} else {
|
||||||
|
selectedItems.addItem(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: if (listProps && !!listProps.inputElement) {
|
||||||
|
_bb.setContext("BBMD:input:context", "list-item")
|
||||||
|
}
|
||||||
|
|
||||||
|
$: isSelected =
|
||||||
|
$selectedItems && selectedItems.getItemIdx($selectedItems, _id) > -1
|
||||||
|
|
||||||
$: modifiers = {
|
$: modifiers = {
|
||||||
selected,
|
selected: isSelected && (!listProps || !listProps.inputElement),
|
||||||
disabled,
|
disabled,
|
||||||
}
|
}
|
||||||
$: props = { modifiers }
|
$: props = { modifiers }
|
||||||
$: listItemClass = cb.build({ props })
|
$: listItemClass = cb.build({ props })
|
||||||
|
|
||||||
$: useTwoLine = variant === "two-line" && !!secondaryText
|
$: useTwoLine =
|
||||||
|
listProps && listProps.variant === "two-line" && !!secondaryText
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<li
|
<li class={listItemClass} role="option" tabindex="0" on:click={handleClick}>
|
||||||
class={listItemClass}
|
|
||||||
role="option"
|
|
||||||
aria-selected={selected}
|
|
||||||
tabindex="0"
|
|
||||||
on:click={onClick}>
|
|
||||||
{#if leadingIcon}
|
{#if leadingIcon}
|
||||||
<span class="mdc-list-item__graphic material-icons" aria-hidden="true">
|
<span class="mdc-list-item__graphic material-icons" aria-hidden="true">
|
||||||
{leadingIcon}
|
{leadingIcon}
|
||||||
|
@ -58,11 +97,11 @@
|
||||||
{:else}{text}{/if}
|
{:else}{text}{/if}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{#if inputElement}
|
{#if listProps}
|
||||||
{#if inputElement === 'radiobutton'}
|
{#if listProps.inputElement === 'radiobutton'}
|
||||||
<Radiobutton checked={selected} {disabled} />
|
<Radiobutton checked={isSelected} {disabled} {_bb} />
|
||||||
{:else if inputElement === 'checkbox'}
|
{:else if listProps.inputElement === 'checkbox'}
|
||||||
<Checkbox checked={selected} {disabled} />
|
<Checkbox checked={isSelected} {disabled} {_bb} />
|
||||||
{/if}
|
{/if}
|
||||||
{:else if trailingIcon}
|
{:else if trailingIcon}
|
||||||
<!-- TODO: Adapt label to accept class prop to handle this. Context is insufficient -->
|
<!-- TODO: Adapt label to accept class prop to handle this. Context is insufficient -->
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount, onDestroy, getContext } from "svelte"
|
import { onMount, onDestroy } from "svelte"
|
||||||
import Formfield from "../Common/Formfield.svelte"
|
import Formfield from "../Common/Formfield.svelte"
|
||||||
import ClassBuilder from "../ClassBuilder.js"
|
import ClassBuilder from "../ClassBuilder.js"
|
||||||
import { MDCRadio } from "@material/radio"
|
import { MDCRadio } from "@material/radio"
|
||||||
|
|
||||||
export let onClick = item => {}
|
export let onClick = item => {}
|
||||||
|
export let _bb
|
||||||
|
|
||||||
export let id = ""
|
export let id = ""
|
||||||
export let label = ""
|
export let label = ""
|
||||||
|
@ -16,13 +17,13 @@
|
||||||
let instance = null
|
let instance = null
|
||||||
let radiobtn = null
|
let radiobtn = null
|
||||||
|
|
||||||
let context = getContext("BBMD:input:context")
|
let context = _bb.getContext("BBMD:input:context")
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (!!radiobtn) {
|
if (!!radiobtn) {
|
||||||
instance = new MDCRadio(radiobtn)
|
instance = new MDCRadio(radiobtn)
|
||||||
if (context !== "list-item") {
|
if (context !== "list-item") {
|
||||||
let fieldStore = getContext("BBMD:field-element")
|
let fieldStore = _bb.getContext("BBMD:field-element")
|
||||||
fieldStore.setInput(instance)
|
fieldStore.setInput(instance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,6 @@
|
||||||
Button,
|
Button,
|
||||||
BodyBoundToStore,
|
BodyBoundToStore,
|
||||||
Textfield,
|
Textfield,
|
||||||
Checkbox,
|
|
||||||
Checkboxgroup,
|
|
||||||
Radiobutton,
|
|
||||||
Radiobuttongroup,
|
|
||||||
Icon,
|
Icon,
|
||||||
Datatable,
|
Datatable,
|
||||||
CustomersIndexTable,
|
CustomersIndexTable,
|
||||||
|
|
|
@ -117,12 +117,13 @@ export const props = {
|
||||||
CustomersIndexTable: indexDatatable(templateOptions)[0].props,
|
CustomersIndexTable: indexDatatable(templateOptions)[0].props,
|
||||||
List: {
|
List: {
|
||||||
_component: "@budibase/materialdesign-components/List",
|
_component: "@budibase/materialdesign-components/List",
|
||||||
|
variant: "two-line",
|
||||||
|
singleSelection: false,
|
||||||
|
onSelect: selected => console.log(selected),
|
||||||
_children: [
|
_children: [
|
||||||
{
|
{
|
||||||
_component: "@budibase/materialdesign-components/ListItem",
|
_component: "@budibase/materialdesign-components/ListItem",
|
||||||
_children: [],
|
_children: [],
|
||||||
variant: "two-line",
|
|
||||||
singleSelection: true,
|
|
||||||
text: "Curry",
|
text: "Curry",
|
||||||
secondaryText: "Chicken or Beef",
|
secondaryText: "Chicken or Beef",
|
||||||
value: 0,
|
value: 0,
|
||||||
|
@ -131,8 +132,6 @@ export const props = {
|
||||||
{
|
{
|
||||||
_component: "@budibase/materialdesign-components/ListItem",
|
_component: "@budibase/materialdesign-components/ListItem",
|
||||||
_children: [],
|
_children: [],
|
||||||
variant: "two-line",
|
|
||||||
singleSelection: true,
|
|
||||||
text: "Pastie",
|
text: "Pastie",
|
||||||
secondaryText: "Bap with Mayo",
|
secondaryText: "Bap with Mayo",
|
||||||
value: 1,
|
value: 1,
|
||||||
|
@ -141,15 +140,10 @@ export const props = {
|
||||||
{
|
{
|
||||||
_component: "@budibase/materialdesign-components/ListItem",
|
_component: "@budibase/materialdesign-components/ListItem",
|
||||||
_children: [],
|
_children: [],
|
||||||
variant: "two-line",
|
|
||||||
singleSelection: true,
|
|
||||||
text: "Fish",
|
text: "Fish",
|
||||||
secondaryText: "Salmon or Cod",
|
secondaryText: "Salmon or Cod",
|
||||||
value: 2,
|
value: 2,
|
||||||
},
|
},
|
||||||
],
|
]
|
||||||
variant: "two-line",
|
|
||||||
singleSelection: true,
|
|
||||||
onSelect: selected => console.log(selected),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue