VSkeletonLoader.mjs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import { mergeProps as _mergeProps, createVNode as _createVNode } from "vue";
  2. // Styles
  3. import "./VSkeletonLoader.css";
  4. // Composables
  5. import { useBackgroundColor } from "../../composables/color.mjs";
  6. import { makeDimensionProps, useDimension } from "../../composables/dimensions.mjs";
  7. import { makeElevationProps, useElevation } from "../../composables/elevation.mjs";
  8. import { useLocale } from "../../composables/locale.mjs";
  9. import { makeThemeProps, provideTheme } from "../../composables/theme.mjs"; // Utilities
  10. import { computed, toRef } from 'vue';
  11. import { genericComponent, propsFactory, useRender, wrapInArray } from "../../util/index.mjs"; // Types
  12. export const rootTypes = {
  13. actions: 'button@2',
  14. article: 'heading, paragraph',
  15. avatar: 'avatar',
  16. button: 'button',
  17. card: 'image, heading',
  18. 'card-avatar': 'image, list-item-avatar',
  19. chip: 'chip',
  20. 'date-picker': 'list-item, heading, divider, date-picker-options, date-picker-days, actions',
  21. 'date-picker-options': 'text, avatar@2',
  22. 'date-picker-days': 'avatar@28',
  23. divider: 'divider',
  24. heading: 'heading',
  25. image: 'image',
  26. 'list-item': 'text',
  27. 'list-item-avatar': 'avatar, text',
  28. 'list-item-two-line': 'sentences',
  29. 'list-item-avatar-two-line': 'avatar, sentences',
  30. 'list-item-three-line': 'paragraph',
  31. 'list-item-avatar-three-line': 'avatar, paragraph',
  32. ossein: 'ossein',
  33. paragraph: 'text@3',
  34. sentences: 'text@2',
  35. subtitle: 'text',
  36. table: 'table-heading, table-thead, table-tbody, table-tfoot',
  37. 'table-heading': 'chip, text',
  38. 'table-thead': 'heading@6',
  39. 'table-tbody': 'table-row-divider@6',
  40. 'table-row-divider': 'table-row, divider',
  41. 'table-row': 'text@6',
  42. 'table-tfoot': 'text@2, avatar@2',
  43. text: 'text'
  44. };
  45. function genBone(type) {
  46. let children = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  47. return _createVNode("div", {
  48. "class": ['v-skeleton-loader__bone', `v-skeleton-loader__${type}`]
  49. }, [children]);
  50. }
  51. function genBones(bone) {
  52. // e.g. 'text@3'
  53. const [type, length] = bone.split('@');
  54. // Generate a length array based upon
  55. // value after @ in the bone string
  56. return Array.from({
  57. length
  58. }).map(() => genStructure(type));
  59. }
  60. function genStructure(type) {
  61. let children = [];
  62. if (!type) return children;
  63. // TODO: figure out a better way to type this
  64. const bone = rootTypes[type];
  65. // End of recursion, do nothing
  66. /* eslint-disable-next-line no-empty, brace-style */
  67. if (type === bone) {}
  68. // Array of values - e.g. 'heading, paragraph, text@2'
  69. else if (type.includes(',')) return mapBones(type);
  70. // Array of values - e.g. 'paragraph@4'
  71. else if (type.includes('@')) return genBones(type);
  72. // Array of values - e.g. 'card@2'
  73. else if (bone.includes(',')) children = mapBones(bone);
  74. // Array of values - e.g. 'list-item@2'
  75. else if (bone.includes('@')) children = genBones(bone);
  76. // Single value - e.g. 'card-heading'
  77. else if (bone) children.push(genStructure(bone));
  78. return [genBone(type, children)];
  79. }
  80. function mapBones(bones) {
  81. // Remove spaces and return array of structures
  82. return bones.replace(/\s/g, '').split(',').map(genStructure);
  83. }
  84. export const makeVSkeletonLoaderProps = propsFactory({
  85. boilerplate: Boolean,
  86. color: String,
  87. loading: Boolean,
  88. loadingText: {
  89. type: String,
  90. default: '$vuetify.loading'
  91. },
  92. type: {
  93. type: [String, Array],
  94. default: 'ossein'
  95. },
  96. ...makeDimensionProps(),
  97. ...makeElevationProps(),
  98. ...makeThemeProps()
  99. }, 'VSkeletonLoader');
  100. export const VSkeletonLoader = genericComponent()({
  101. name: 'VSkeletonLoader',
  102. props: makeVSkeletonLoaderProps(),
  103. setup(props, _ref) {
  104. let {
  105. slots
  106. } = _ref;
  107. const {
  108. backgroundColorClasses,
  109. backgroundColorStyles
  110. } = useBackgroundColor(toRef(props, 'color'));
  111. const {
  112. dimensionStyles
  113. } = useDimension(props);
  114. const {
  115. elevationClasses
  116. } = useElevation(props);
  117. const {
  118. themeClasses
  119. } = provideTheme(props);
  120. const {
  121. t
  122. } = useLocale();
  123. const items = computed(() => genStructure(wrapInArray(props.type).join(',')));
  124. useRender(() => {
  125. const isLoading = !slots.default || props.loading;
  126. const loadingProps = props.boilerplate || !isLoading ? {} : {
  127. ariaLive: 'polite',
  128. ariaLabel: t(props.loadingText),
  129. role: 'alert'
  130. };
  131. return _createVNode("div", _mergeProps({
  132. "class": ['v-skeleton-loader', {
  133. 'v-skeleton-loader--boilerplate': props.boilerplate
  134. }, themeClasses.value, backgroundColorClasses.value, elevationClasses.value],
  135. "style": [backgroundColorStyles.value, isLoading ? dimensionStyles.value : {}]
  136. }, loadingProps), [isLoading ? items.value : slots.default?.()]);
  137. });
  138. return {};
  139. }
  140. });
  141. //# sourceMappingURL=VSkeletonLoader.mjs.map