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:
Conor_Mack 2020-02-27 17:29:45 +00:00 committed by GitHub
commit 0acdd97a82
8 changed files with 158 additions and 70 deletions

View File

@ -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)
} }
} }

View File

@ -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)

View File

@ -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

View File

@ -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 }

View File

@ -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 -->

View File

@ -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)
} }
} }

View File

@ -32,10 +32,6 @@
Button, Button,
BodyBoundToStore, BodyBoundToStore,
Textfield, Textfield,
Checkbox,
Checkboxgroup,
Radiobutton,
Radiobuttongroup,
Icon, Icon,
Datatable, Datatable,
CustomersIndexTable, CustomersIndexTable,

View File

@ -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),
}, },
} }