Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change street to address #68

Merged
merged 2 commits into from
Sep 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
342 changes: 237 additions & 105 deletions Tithe-Vue/src/components/AddressForm.vue

Large diffs are not rendered by default.

728 changes: 728 additions & 0 deletions Tithe-Vue/src/components/MultiSelectBox/Multiselect.vue

Large diffs are not rendered by default.

182 changes: 182 additions & 0 deletions Tithe-Vue/src/components/MultiSelectBox/composables/useA11y.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { toRefs, onMounted, ref, computed } from 'vue'

export default function useA11y (props, context, dep)
{
const {
placeholder, id, valueProp, label: labelProp, mode, groupLabel, aria, searchable ,
} = toRefs(props)

// ============ DEPENDENCIES ============

const pointer = dep.pointer
const iv = dep.iv
const hasSelected = dep.hasSelected
const multipleLabelText = dep.multipleLabelText

// ================ DATA ================

const label = ref(null)

// ============== COMPUTED ==============

const ariaAssist = computed(() => {
let texts = []

if (id && id.value) {
texts.push(id.value)
}

texts.push('assist')

return texts.join('-')
})

const ariaControls = computed(() => {
let texts = []

if (id && id.value) {
texts.push(id.value)
}

texts.push('multiselect-options')

return texts.join('-')
})

const ariaActiveDescendant = computed(() => {
let texts = []

if (id && id.value) {
texts.push(id.value)
}

if (pointer.value) {
texts.push(pointer.value.group ? 'multiselect-group' : 'multiselect-option')

texts.push(pointer.value.group ? pointer.value.index : pointer.value[valueProp.value])

return texts.join('-')
}
})



const ariaPlaceholder = computed(() => {
return placeholder.value
})

const ariaMultiselectable = computed(() => {
return mode.value !== 'single'
})

const ariaLabel = computed(() => {
let ariaLabel = ''

if (mode.value === 'single' && hasSelected.value) {
ariaLabel += iv.value[labelProp.value]
}

if (mode.value === 'multiple' && hasSelected.value) {
ariaLabel += multipleLabelText.value
}

if (mode.value === 'tags' && hasSelected.value) {
ariaLabel += iv.value.map(v => v[labelProp.value]).join(', ')
}

return ariaLabel
})

const arias = computed(() => {
let arias = { ...aria.value }

// Need to add manually because focusing
// the input won't read the selected value
if (searchable.value) {
arias['aria-labelledby'] = arias['aria-labelledby']
? `${ariaAssist.value} ${arias['aria-labelledby']}`
: ariaAssist.value

if (ariaLabel.value && arias['aria-label']) {
arias['aria-label'] = `${ariaLabel.value}, ${arias['aria-label']}`
}
}

return arias
})

// =============== METHODS ==============

const ariaOptionId = (option) => {
let texts = []

if (id && id.value) {
texts.push(id.value)
}

texts.push('multiselect-option')

texts.push(option[valueProp.value])

return texts.join('-')
}

const ariaGroupId = (option) => {
let texts = []

if (id && id.value) {
texts.push(id.value)
}

texts.push('multiselect-group')

texts.push(option.index)

return texts.join('-')
}

const ariaOptionLabel = (label) => {
let texts = []

texts.push(label)

return texts.join(' ')
}

const ariaGroupLabel = (label) => {
let texts = []

texts.push(label)

return texts.join(' ')
}

const ariaTagLabel = (label) => {
return `${label} ❎`
}

// =============== HOOKS ================

onMounted(() => {
/* istanbul ignore next */
if (id && id.value && document && document.querySelector) {
let forTag = document.querySelector(`[for="${id.value}"]`)
label.value = forTag ? forTag.innerText : null
}
})

return {
arias,
ariaLabel,
ariaAssist,
ariaControls,
ariaPlaceholder,
ariaMultiselectable,
ariaActiveDescendant,
ariaOptionId,
ariaOptionLabel,
ariaGroupId,
ariaGroupLabel,
ariaTagLabel,
}
}
160 changes: 160 additions & 0 deletions Tithe-Vue/src/components/MultiSelectBox/composables/useClasses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { computed, toRefs } from 'vue'

export default function useClasses (props, context, dependencies)
{const {
classes: classes_, disabled, openDirection, showOptions
} = toRefs(props)

// ============ DEPENDENCIES ============

const isOpen = dependencies.isOpen
const isPointed = dependencies.isPointed
const isSelected = dependencies.isSelected
const isDisabled = dependencies.isDisabled
const isActive = dependencies.isActive
const canPointGroups = dependencies.canPointGroups
const resolving = dependencies.resolving
const fo = dependencies.fo

const classes = computed(() => ({
container: 'multiselect',
containerDisabled: 'is-disabled',
containerOpen: 'is-open',
containerOpenTop: 'is-open-top',
containerActive: 'is-active',
wrapper: 'multiselect-wrapper',
singleLabel: 'multiselect-single-label',
singleLabelText: 'multiselect-single-label-text',
multipleLabel: 'multiselect-multiple-label',
search: 'multiselect-search',
tags: 'multiselect-tags',
tag: 'multiselect-tag',
tagDisabled: 'is-disabled',
tagRemove: 'multiselect-tag-remove',
tagRemoveIcon: 'multiselect-tag-remove-icon',
tagsSearchWrapper: 'multiselect-tags-search-wrapper',
tagsSearch: 'multiselect-tags-search',
tagsSearchCopy: 'multiselect-tags-search-copy',
placeholder: 'multiselect-placeholder',
caret: 'multiselect-caret',
caretOpen: 'is-open',
clear: 'multiselect-clear',
clearIcon: 'multiselect-clear-icon',
spinner: 'multiselect-spinner',
inifinite: 'multiselect-inifite',
inifiniteSpinner: 'multiselect-inifite-spinner',
dropdown: 'multiselect-dropdown',
dropdownTop: 'is-top',
dropdownHidden: 'is-hidden',
options: 'multiselect-options',
optionsTop: 'is-top',
group: 'multiselect-group',
groupLabel: 'multiselect-group-label',
groupLabelPointable: 'is-pointable',
groupLabelPointed: 'is-pointed',
groupLabelSelected: 'is-selected',
groupLabelDisabled: 'is-disabled',
groupLabelSelectedPointed: 'is-selected is-pointed',
groupLabelSelectedDisabled: 'is-selected is-disabled',
groupOptions: 'multiselect-group-options',
option: 'multiselect-option',
optionPointed: 'is-pointed',
optionSelected: 'is-selected',
optionDisabled: 'is-disabled',
optionSelectedPointed: 'is-selected is-pointed',
optionSelectedDisabled: 'is-selected is-disabled',
noOptions: 'multiselect-no-options',
noResults: 'multiselect-no-results',
fakeInput: 'multiselect-fake-input',
assist: 'multiselect-assistive-text',
spacer: 'multiselect-spacer',
...classes_.value,
}))

// ============== COMPUTED ==============

const showDropdown = computed(() => {
return !!(isOpen.value && showOptions.value && (!resolving.value || (resolving.value && fo.value.length)))
})

const classList = computed(() => {
const c = classes.value

return {
container: [c.container]
.concat(disabled.value ? c.containerDisabled : [])
.concat(showDropdown.value && openDirection.value === 'top' ? c.containerOpenTop : [])
.concat(showDropdown.value && openDirection.value !== 'top' ? c.containerOpen : [])
.concat(isActive.value ? c.containerActive : []),
wrapper: c.wrapper,
spacer: c.spacer,
singleLabel: c.singleLabel,
singleLabelText: c.singleLabelText,
multipleLabel: c.multipleLabel,
search: c.search,
tags: c.tags,
tag: [c.tag]
.concat(disabled.value ? c.tagDisabled : []),
tagDisabled: c.tagDisabled,
tagRemove: c.tagRemove,
tagRemoveIcon: c.tagRemoveIcon,
tagsSearchWrapper: c.tagsSearchWrapper,
tagsSearch: c.tagsSearch,
tagsSearchCopy: c.tagsSearchCopy,
placeholder: c.placeholder,
caret: [c.caret]
.concat(isOpen.value ? c.caretOpen : []),
clear: c.clear,
clearIcon: c.clearIcon,
spinner: c.spinner,
inifinite: c.inifinite,
inifiniteSpinner: c.inifiniteSpinner,
dropdown: [c.dropdown]
.concat(openDirection.value === 'top' ? c.dropdownTop : [])
.concat(!isOpen.value || !showOptions.value || !showDropdown.value ? c.dropdownHidden : []),
options: [c.options]
.concat(openDirection.value === 'top' ? c.optionsTop : []),
group: c.group,
groupLabel: (g) => {
let groupLabel = [c.groupLabel]

if (isPointed(g)) {
groupLabel.push(isSelected(g) ? c.groupLabelSelectedPointed : c.groupLabelPointed)
} else if (isSelected(g) && canPointGroups.value) {
groupLabel.push(isDisabled(g) ? c.groupLabelSelectedDisabled : c.groupLabelSelected)
} else if (isDisabled(g)) {
groupLabel.push(c.groupLabelDisabled)
}

if (canPointGroups.value) {
groupLabel.push(c.groupLabelPointable)
}

return groupLabel
},
groupOptions: c.groupOptions,
option: (o, g) => {
let option = [c.option]

if (isPointed(o)) {
option.push(isSelected(o) ? c.optionSelectedPointed : c.optionPointed)
} else if (isSelected(o)) {
option.push(isDisabled(o) ? c.optionSelectedDisabled : c.optionSelected)
} else if (isDisabled(o) || (g && isDisabled(g))) {
option.push(c.optionDisabled)
}

return option
},
noOptions: c.noOptions,
noResults: c.noResults,
assist: c.assist,
fakeInput: c.fakeInput,
}
})

return {
classList,
showDropdown,
}
}
Loading