vue-material-adapter
Version:
Vue 3 wrapper arround Material Components for the Web
177 lines (153 loc) • 4.28 kB
JavaScript
import { MDCCircularProgressFoundation } from '@material/circular-progress/foundation.js';
import {
computed,
onBeforeUnmount,
onMounted,
reactive,
toRefs,
watch,
} from 'vue';
const ProgressPropertyType = {
type: [Number, String],
validator(value) {
return Number(value) >= 0 && Number(value) <= 1;
},
};
export default {
name: 'mcw-circular-progress',
props: {
open: { type: Boolean, default: true },
indeterminate: Boolean,
medium: Boolean,
progress: ProgressPropertyType,
tag: { type: String, default: 'div' },
label: { type: String },
fourColor: Boolean,
},
setup(props) {
const uiState = reactive({
classes: {
'mdc-circular-progress': 1,
'mdc-circular-progress--medium': props.medium,
'mdc-circular-progress--large': !props.medium,
},
rootAttrs: props.medium
? { style: { width: '36px', height: '36px' } }
: { style: { width: '48px', height: '48px' } },
circleAttrs: getCircleAttributes(props.medium, false),
trackAttrs: getTrackAttributes(props.medium, false),
indeterminateAttrs: getCircleAttributes(props.medium, true),
viewbox: props.medium ? '0 0 36 36' : '0 0 48 48',
root: undefined,
});
let foundation;
const adapter = {
addClass: className => {
uiState.classes = { ...uiState.classes, [className]: true };
},
getDeterminateCircleAttribute: attributeName => {
return uiState.circleAttrs[attributeName];
},
hasClass: className => uiState.root.classList.contains(className),
removeClass: className => {
const { [className]: removed, ...rest } = uiState.classes;
uiState.classes = rest;
},
removeAttribute: attributeName => {
const { [attributeName]: removed, ...rest } = uiState.rootAttrs;
uiState.rootAttrs = rest;
},
setAttribute: (attributeName, value) => {
uiState.rootAttrs = { ...uiState.rootAttrs, [attributeName]: value };
},
setDeterminateCircleAttribute: (attributeName, value) =>
(uiState.circleAttrs = {
...uiState.circleAttrs,
[attributeName]: value,
}),
};
const rootAttributes = computed(() => {
return {
role: 'progressbar',
'aria-valuemin': '0',
'aria-valuemax': '1',
'aria-label': props.label,
...uiState.rootAttrs,
};
});
watch(
() => props.open,
nv => {
if (nv) {
foundation.open();
} else {
foundation.close();
}
},
);
watch(
() => props.progress,
nv => {
foundation.setProgress(Number(nv));
},
);
watch(
() => props.indeterminate,
nv => {
foundation.setDeterminate(!nv);
},
);
const colorClass = index => {
return props.fourColor
? `mdc-circular-progress__color-${index}`
: undefined;
};
const colors = Array.from(
{ length: props.fourColor ? 4 : 1 },
(_, index) => index + 1,
);
onMounted(() => {
foundation = new MDCCircularProgressFoundation(adapter);
foundation.init();
foundation.setProgress(Number(props.progress));
foundation.setDeterminate(!props.indeterminate);
if (props.open) {
foundation.open();
} else {
foundation.close();
}
});
onBeforeUnmount(() => foundation.destroy());
return { ...toRefs(uiState), rootAttributes, colors, colorClass };
},
};
// ===
// Private functions
// ===
function getCircleAttributes(medium = false, indeterminate = true) {
return medium
? {
cx: '16',
cy: '16',
r: '12.5',
'stroke-dasharray': '78.54',
'stroke-dashoffset': indeterminate ? '39.27' : '78.54',
'stroke-width': '3',
}
: {
cx: '24',
cy: '24',
r: '18',
'stroke-dasharray': '113.097',
'stroke-dashoffset': indeterminate ? '56.549' : '113.097',
'stroke-width': '4',
};
}
function getTrackAttributes(medium = false) {
const {
['stroke-dasharray']: sda,
['stroke-dashoffset']: sdo,
...rest
} = getCircleAttributes(medium);
return rest;
}