Cascader.mjs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import { createVNode as _createVNode } from "vue";
  2. import { ref, watch, nextTick, defineComponent } from "vue";
  3. import { extend, truthProp, numericProp, makeArrayProp, makeStringProp, createNamespace, HAPTICS_FEEDBACK } from "../utils/index.mjs";
  4. import { Tab } from "../tab/index.mjs";
  5. import { Tabs } from "../tabs/index.mjs";
  6. import { Icon } from "../icon/index.mjs";
  7. const [name, bem, t] = createNamespace("cascader");
  8. const cascaderProps = {
  9. title: String,
  10. options: makeArrayProp(),
  11. closeable: truthProp,
  12. swipeable: truthProp,
  13. closeIcon: makeStringProp("cross"),
  14. showHeader: truthProp,
  15. modelValue: numericProp,
  16. fieldNames: Object,
  17. placeholder: String,
  18. activeColor: String
  19. };
  20. var stdin_default = defineComponent({
  21. name,
  22. props: cascaderProps,
  23. emits: ["close", "change", "finish", "click-tab", "update:modelValue"],
  24. setup(props, {
  25. slots,
  26. emit
  27. }) {
  28. const tabs = ref([]);
  29. const activeTab = ref(0);
  30. const {
  31. text: textKey,
  32. value: valueKey,
  33. children: childrenKey
  34. } = extend({
  35. text: "text",
  36. value: "value",
  37. children: "children"
  38. }, props.fieldNames);
  39. const getSelectedOptionsByValue = (options, value) => {
  40. for (const option of options) {
  41. if (option[valueKey] === value) {
  42. return [option];
  43. }
  44. if (option[childrenKey]) {
  45. const selectedOptions = getSelectedOptionsByValue(option[childrenKey], value);
  46. if (selectedOptions) {
  47. return [option, ...selectedOptions];
  48. }
  49. }
  50. }
  51. };
  52. const updateTabs = () => {
  53. const {
  54. options,
  55. modelValue
  56. } = props;
  57. if (modelValue !== void 0) {
  58. const selectedOptions = getSelectedOptionsByValue(options, modelValue);
  59. if (selectedOptions) {
  60. let optionsCursor = options;
  61. tabs.value = selectedOptions.map((option) => {
  62. const tab = {
  63. options: optionsCursor,
  64. selected: option
  65. };
  66. const next = optionsCursor.find((item) => item[valueKey] === option[valueKey]);
  67. if (next) {
  68. optionsCursor = next[childrenKey];
  69. }
  70. return tab;
  71. });
  72. if (optionsCursor) {
  73. tabs.value.push({
  74. options: optionsCursor,
  75. selected: null
  76. });
  77. }
  78. nextTick(() => {
  79. activeTab.value = tabs.value.length - 1;
  80. });
  81. return;
  82. }
  83. }
  84. tabs.value = [{
  85. options,
  86. selected: null
  87. }];
  88. };
  89. const onSelect = (option, tabIndex) => {
  90. if (option.disabled) {
  91. return;
  92. }
  93. tabs.value[tabIndex].selected = option;
  94. if (tabs.value.length > tabIndex + 1) {
  95. tabs.value = tabs.value.slice(0, tabIndex + 1);
  96. }
  97. if (option[childrenKey]) {
  98. const nextTab = {
  99. options: option[childrenKey],
  100. selected: null
  101. };
  102. if (tabs.value[tabIndex + 1]) {
  103. tabs.value[tabIndex + 1] = nextTab;
  104. } else {
  105. tabs.value.push(nextTab);
  106. }
  107. nextTick(() => {
  108. activeTab.value++;
  109. });
  110. }
  111. const selectedOptions = tabs.value.map((tab) => tab.selected).filter(Boolean);
  112. emit("update:modelValue", option[valueKey]);
  113. const params = {
  114. value: option[valueKey],
  115. tabIndex,
  116. selectedOptions
  117. };
  118. emit("change", params);
  119. if (!option[childrenKey]) {
  120. emit("finish", params);
  121. }
  122. };
  123. const onClose = () => emit("close");
  124. const onClickTab = ({
  125. name: name2,
  126. title
  127. }) => emit("click-tab", name2, title);
  128. const renderHeader = () => props.showHeader ? _createVNode("div", {
  129. "class": bem("header")
  130. }, [_createVNode("h2", {
  131. "class": bem("title")
  132. }, [slots.title ? slots.title() : props.title]), props.closeable ? _createVNode(Icon, {
  133. "name": props.closeIcon,
  134. "class": [bem("close-icon"), HAPTICS_FEEDBACK],
  135. "onClick": onClose
  136. }, null) : null]) : null;
  137. const renderOption = (option, selectedOption, tabIndex) => {
  138. const {
  139. disabled
  140. } = option;
  141. const selected = !!(selectedOption && option[valueKey] === selectedOption[valueKey]);
  142. const color = option.color || (selected ? props.activeColor : void 0);
  143. const Text = slots.option ? slots.option({
  144. option,
  145. selected
  146. }) : _createVNode("span", null, [option[textKey]]);
  147. return _createVNode("li", {
  148. "role": "menuitemradio",
  149. "class": [bem("option", {
  150. selected,
  151. disabled
  152. }), option.className],
  153. "style": {
  154. color
  155. },
  156. "tabindex": disabled ? void 0 : selected ? 0 : -1,
  157. "aria-checked": selected,
  158. "aria-disabled": disabled || void 0,
  159. "onClick": () => onSelect(option, tabIndex)
  160. }, [Text, selected ? _createVNode(Icon, {
  161. "name": "success",
  162. "class": bem("selected-icon")
  163. }, null) : null]);
  164. };
  165. const renderOptions = (options, selectedOption, tabIndex) => _createVNode("ul", {
  166. "role": "menu",
  167. "class": bem("options")
  168. }, [options.map((option) => renderOption(option, selectedOption, tabIndex))]);
  169. const renderTab = (tab, tabIndex) => {
  170. const {
  171. options,
  172. selected
  173. } = tab;
  174. const placeholder = props.placeholder || t("select");
  175. const title = selected ? selected[textKey] : placeholder;
  176. return _createVNode(Tab, {
  177. "title": title,
  178. "titleClass": bem("tab", {
  179. unselected: !selected
  180. })
  181. }, {
  182. default: () => {
  183. var _a, _b;
  184. return [(_a = slots["options-top"]) == null ? void 0 : _a.call(slots, {
  185. tabIndex
  186. }), renderOptions(options, selected, tabIndex), (_b = slots["options-bottom"]) == null ? void 0 : _b.call(slots, {
  187. tabIndex
  188. })];
  189. }
  190. });
  191. };
  192. const renderTabs = () => _createVNode(Tabs, {
  193. "active": activeTab.value,
  194. "onUpdate:active": ($event) => activeTab.value = $event,
  195. "shrink": true,
  196. "animated": true,
  197. "class": bem("tabs"),
  198. "color": props.activeColor,
  199. "swipeable": props.swipeable,
  200. "onClick-tab": onClickTab
  201. }, {
  202. default: () => [tabs.value.map(renderTab)]
  203. });
  204. updateTabs();
  205. watch(() => props.options, updateTabs, {
  206. deep: true
  207. });
  208. watch(() => props.modelValue, (value) => {
  209. if (value !== void 0) {
  210. const values = tabs.value.map((tab) => {
  211. var _a;
  212. return (_a = tab.selected) == null ? void 0 : _a[valueKey];
  213. });
  214. if (values.includes(value)) {
  215. return;
  216. }
  217. }
  218. updateTabs();
  219. });
  220. return () => _createVNode("div", {
  221. "class": bem()
  222. }, [renderHeader(), renderTabs()]);
  223. }
  224. });
  225. export {
  226. stdin_default as default
  227. };