Cascader.js 8.5 KB

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