Skip to content

Commit

Permalink
refactor: Replaced makeId with SSR friendly useId, Interactable inter…
Browse files Browse the repository at this point in the history
…face renamed to Interactive
  • Loading branch information
cmath10 committed Jan 2, 2025
1 parent 6d3f2e5 commit 6414c81
Show file tree
Hide file tree
Showing 21 changed files with 333 additions and 194 deletions.
2 changes: 1 addition & 1 deletion m3-foundation/types/dom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ export interface Focusable {
blur (): void;
}

export interface Interactable extends Clickable, Focusable {}
export interface Interactive extends Clickable, Focusable {}
6 changes: 3 additions & 3 deletions m3-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"lodash.isequal": "^4.5.0"
},
"peerDependencies": {
"vue": "^3.4"
"vue": "^3.5"
},
"devDependencies": {
"@chromatic-com/storybook": "^1.1.10",
Expand All @@ -60,7 +60,7 @@
"@typescript-eslint/eslint-plugin": "^8.11.0",
"@typescript-eslint/parser": "^8.11.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/compiler-sfc": "^3.4.19",
"@vue/compiler-sfc": "^3.5.13",
"@vue/language-server": "^2.0.19",
"eslint": "^9.13.0",
"eslint-plugin-import": "^2.31.0",
Expand All @@ -81,7 +81,7 @@
"vite": "^5.4.10",
"vite-svg-loader": "^5.1.0",
"vitest": "^2.1.3",
"vue": "^3.4.19",
"vue": "^3.5.13",
"vue-router": "^4.3.0"
},
"contributors": [
Expand Down
4 changes: 2 additions & 2 deletions m3-vue/src/components/button/M3Button.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Interactable } from '@modulify/m3-foundation'
import type { Interactive } from '@modulify/m3-foundation'
import type { PropType, VNode } from 'vue'
import type { RouteLocationRaw } from 'vue-router'

Expand Down Expand Up @@ -58,7 +58,7 @@ export default defineComponent({
click: () => root.value?.click(),
focus: () => root.value?.focus(),
blur: () => root.value?.blur(),
} satisfies Interactable)
} satisfies Interactive)

const onKeyup = (event: KeyboardEvent) => {
if (event.code === 'Enter') {
Expand Down
27 changes: 18 additions & 9 deletions m3-vue/src/components/card/M3Card.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<section
:id="id"
:id="_id"
:class="{
['m3-card']: true,
['m3-card_' + appearance]: true,
Expand All @@ -11,7 +11,7 @@
role: 'region',
...(interactive ? { tabindex: 0 } : {}),
...(!('aria-label' in $attrs) && !('content' in $slots) && ('heading' in $slots || heading.length) ? {
'aria-labelledby': id + '-heading',
'aria-labelledby': _id + '-heading',
} : {}),
...$attrs,
}"
Expand All @@ -37,7 +37,7 @@
>
<div
v-if="'heading' in $slots || heading.length"
:id="id + '-heading'"
:id="_id + '-heading'"
class="m3-card__heading"
>
<slot name="heading">
Expand All @@ -59,19 +59,26 @@
</template>

<script lang="ts" setup>
import type { PropType } from 'vue'
import type { Appearance } from '@modulify/m3-foundation/types/components/card'
import type { PropType } from 'vue'
import { M3Ripple } from '@/components/ripple'
import { ref } from 'vue'
import { computed, ref } from 'vue'
import {
isId,
isUndefined,
Or,
} from '@modulify/m3-foundation/lib/predicates'
import makeId from '@/utils/id'
import useId from '@/composables/id'
defineProps({
const props = defineProps({
id: {
type: String,
default: () => makeId('m3-card'),
type: null as unknown as PropType<string | undefined>,
validator: Or(isId, isUndefined),
default: undefined,
},
appearance: {
Expand Down Expand Up @@ -100,6 +107,8 @@ defineProps({
},
})
const _id = useId('m3-card', computed(() => props.id))
const state = ref<HTMLElement | null>(null)
const ripple = ref<InstanceType<typeof M3Ripple> | null>(null)
</script>
31 changes: 15 additions & 16 deletions m3-vue/src/components/checkbox/M3Checkbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
<M3Ripple :owner="ref(root)" />

<input
:id="id"
ref="input"
:id="_id"
ref="_input"
:aria-checked="checked ? 'true' : 'false'"
:aria-invalid="invalid ? 'true' : 'false'"
:name="name"
:name="_name"
:value="value"
:checked="checked"
:disabled="disabled"
Expand All @@ -35,6 +35,7 @@
</template>

<script lang="ts" setup>
import type { Interactive } from '@modulify/m3-foundation'
import type { PropType } from 'vue'
import IconCheckmark from '@modulify/m3-foundation/assets/sprites/checkbox/checkmark.svg'
Expand All @@ -53,7 +54,7 @@ import {
Or,
} from '@modulify/m3-foundation/lib/predicates'
import makeId from '@/utils/id'
import useId from '@/composables/id'
const props = defineProps({
id: {
Expand All @@ -63,8 +64,8 @@ const props = defineProps({
},
name: {
type: String,
default: () => makeId('m3-checkbox'),
type: null as unknown as PropType<string | undefined>,
default: undefined,
},
model: {
Expand Down Expand Up @@ -116,17 +117,16 @@ const emit = defineEmits([
])
const root = ref<HTMLElement | null>(null)
const input = ref<HTMLInputElement | null>(null)
const click = () => input.value?.click()
const focus = () => input.value?.focus()
const blur = () => input.value?.blur()
const _id = useId('m3-checkbox', computed(() => props.id))
const _name = computed(() => props.name ?? _id.value)
const _input = ref<HTMLInputElement | null>(null)
defineExpose({
click,
focus,
blur,
})
click: () => _input.value?.click(),
focus: () => _input.value?.focus(),
blur: () => _input.value?.blur(),
} satisfies Interactive)
const equals = (a: unknown, b: unknown) => props.equalsFn.call(null, a, b)
const contains = (array: unknown[], value: unknown) => array.some(v => equals(v, value))
Expand All @@ -148,8 +148,7 @@ const calculate = (checked: boolean) => {
}
const onChange = (event: Event) => {
const input = event.target as HTMLInputElement
const value = calculate(input.checked)
const value = calculate((event.target as HTMLInputElement).checked)
emit('change', value)
emit('update:model', value)
Expand Down
4 changes: 2 additions & 2 deletions m3-vue/src/components/link/M3Link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
resolveComponent,
} from 'vue'

import type { Interactable } from '@modulify/m3-foundation'
import type { Interactive } from '@modulify/m3-foundation'

import type { RouteLocationRaw } from 'vue-router'

Expand Down Expand Up @@ -58,7 +58,7 @@ export type M3LinkProps = {
href?: string;
}

export interface M3LinkMethods extends Interactable {
export interface M3LinkMethods extends Interactive {
el (): HTMLElement | null;
}

Expand Down
26 changes: 19 additions & 7 deletions m3-vue/src/components/navigation/M3NavigationTab.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div
:id="id"
:id="_id"
:class="{
['m3-navigation-tab']: true,
['m3-navigation-tab_in-' + appearance]: true,
Expand Down Expand Up @@ -72,9 +72,13 @@
</template>

<script lang="ts" setup>
import type { Ref } from 'vue'
import type { Appearance } from '@modulify/m3-foundation/types/components/navigation'
import type {
PropType,
Ref,
} from 'vue'
import { M3Badge } from '@/components/badge'
import { M3Link } from '@/components/link'
import { M3Ripple } from '@/components/ripple'
Expand All @@ -87,15 +91,21 @@ import {
ref,
} from 'vue'
import makeId from '@/utils/id'
import {
isId,
isUndefined,
Or,
} from '@modulify/m3-foundation/lib/predicates'
import { provideM3IconAppearance } from '@/components/icon/injections'
import { useBreakpoint } from '@/composables/breakpoint'
import useId from '@/composables/id'
const props = defineProps({
id: {
type: String,
default: () => makeId('m3-navigation-item'),
type: null as unknown as PropType<string | undefined>,
validator: Or(isId, isUndefined),
default: undefined,
},
href: {
Expand Down Expand Up @@ -127,15 +137,17 @@ const props = defineProps({
const emit = defineEmits(['navigate'])
const _id = useId('m3-navigation-item', computed(() => props.id))
const appearance = inject<Ref<Appearance>>(M3NavigationAppearance, ref('auto'))
const breakpoint = useBreakpoint()
const button = ref<(typeof M3Link) | null>(null)
const buttonElement = computed(() => button.value?.el())
const inDrawer = computed(() => breakpoint.value.ge('large') || appearance.value === 'drawer')
const labelIdForDrawer = computed(() => props.id + '-label-for-drawer')
const labelIdForRail = computed(() => props.id + '-label-for-rail')
const labelIdForDrawer = computed(() => _id.value + '-label-for-drawer')
const labelIdForRail = computed(() => _id.value + '-label-for-rail')
const labelId = computed(() => inDrawer.value ? labelIdForDrawer.value : labelIdForRail.value)
provideM3IconAppearance(() => props.active ? 'filled' : 'outlined')
Expand Down
57 changes: 42 additions & 15 deletions m3-vue/src/components/select/M3Select.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div
ref="root"
:aria-controls="id + '-menu'"
:aria-controls="_id + '-menu'"
:aria-expanded="expanded ? 'true' : 'false'"
:aria-disabled="disabled ? 'true' : 'false'"
:aria-readonly="readonly ? 'true' : 'false'"
Expand All @@ -13,7 +13,7 @@
role="combobox"
>
<M3TextField
:id="id"
:id="_id"
:value="text"
:label="label"
:placeholder="placeholder"
Expand All @@ -35,15 +35,13 @@
</M3TextField>

<M3Menu
:id="id + '-menu'"
:id="_id + '-menu'"
v-model:shown="shouldBeExpanded"
:target="ref(root)"
:placement="placement"
:aria-hidden="expanded ? 'false' : 'true'"
:disabled="disabled || readonly"
:style="{
width: rootWidth + 'px',
}"
:style="{ width: rootWidth + 'px' }"
role="listbox"
@shown="expanded = true"
@hide="expanded = false"
Expand Down Expand Up @@ -98,7 +96,13 @@ import {
ref,
} from 'vue'
import makeId from '@/utils/id'
import {
isId,
isUndefined,
Or,
} from '@modulify/m3-foundation/lib/predicates'
import useId from '@/composables/id'
type Maybe<T> = T | null
Expand All @@ -109,8 +113,9 @@ interface Option {
const props = defineProps({
id: {
type: String,
default: () => makeId('m3-select'),
type: null as unknown as PropType<string | undefined>,
validator: Or(isId, isUndefined),
default: undefined,
},
value: {
Expand Down Expand Up @@ -168,6 +173,8 @@ const emit = defineEmits([
'update:value',
])
const _id = useId('m3-select', computed(() => props.id))
const root = ref<HTMLElement | null>(null)
const rootWidth = ref(0)
Expand All @@ -183,19 +190,39 @@ const pick = (option: Option) => {
shouldBeExpanded.value = false
}
let observer: ResizeObserver | null = null
let resizeObserver: ResizeObserver | null = null
let resizeUpdateId: number | null = null
onMounted(() => {
observer = new ResizeObserver(([entry]) => {
const requestResizeUpdate = (entry: ResizeObserverEntry) => {
requestAnimationFrame(() => {
rootWidth.value = entry.contentRect.width
})
observer.observe(root.value as HTMLElement)
}
const cancelResizeUpdate = () => {
if (resizeUpdateId) {
cancelAnimationFrame(resizeUpdateId)
resizeUpdateId = null
}
}
onMounted(() => {
resizeObserver = new ResizeObserver(([entry]) => {
cancelResizeUpdate()
requestResizeUpdate(entry)
})
resizeObserver.observe(root.value as HTMLElement)
rootWidth.value = root.value?.offsetWidth ?? 0
})
onBeforeUnmount(() => {
observer?.disconnect()
observer = null
resizeObserver?.disconnect()
resizeObserver = null
if (resizeUpdateId) {
cancelAnimationFrame(resizeUpdateId)
resizeUpdateId = null
}
})
</script>
Loading

0 comments on commit 6414c81

Please sign in to comment.