{"version":3,"file":"index.mjs","sources":["../../../../src/components/Select/index.vue"],"sourcesContent":["<script lang=\"ts\">\nexport default {\n  name: 'RobustSelect',\n  inheritAttrs: false,\n};\n</script>\n<script lang=\"ts\" setup>\nimport RobustInputWrapper from '../InputWrapper/index.vue';\nimport FLoating from '../Floating/index.vue';\nimport { ref, computed, nextTick, toRefs, onMounted, PropType } from 'vue';\nimport { debouncedWatch } from '@vueuse/core';\nimport { onClickOutside } from '@vueuse/core';\nimport { PhCheck, PhCaretDown } from '@phosphor-icons/vue';\nimport isEqual from 'fast-deep-equal/es6';\n\nexport type Value = string | number | boolean | null | undefined;\n\nexport interface Option {\n  title: string;\n  value: Value;\n  class?: string;\n}\n\nconst props = defineProps({\n  title: {\n    type: String,\n    default: undefined,\n  },\n  class: {\n    type: String as PropType<string | string[] | object>,\n    default: undefined,\n  },\n  boxClass: {\n    type: String as PropType<string | string[] | object>,\n    default: undefined,\n  },\n  hint: {\n    type: String,\n    default: undefined,\n  },\n  error: {\n    type: String,\n    default: undefined,\n  },\n  fixedHeight: {\n    type: Boolean,\n    default: true,\n  },\n  readonly: {\n    type: Boolean,\n    default: false,\n  },\n  condensed: {\n    type: Boolean,\n    default: false,\n  },\n  disabled: {\n    type: Boolean,\n    default: false,\n  },\n  searchable: {\n    type: Boolean,\n    default: true,\n  },\n  searchFunction: {\n    type: Function as PropType<(query: string) => Promise<Array<Option>>>,\n    default: undefined,\n  },\n  placeholder: {\n    type: String,\n    default: 'Select',\n  },\n  options: {\n    type: Array as PropType<Array<Option>>,\n    default: () => [],\n  },\n  modelValue: {\n    type: [String, Number, Boolean, Array] as PropType<Value | Array<Value>>,\n    default: undefined,\n  },\n  border: {\n    type: Boolean,\n    default: () => true,\n  },\n  lazy: {\n    type: Boolean,\n    default: false,\n  },\n});\n\nconst emit = defineEmits([\n  'update:modelValue',\n  'input',\n  'change',\n  'focus',\n  'blur',\n  'search',\n]);\n// const refSelectContainer = ref()\nconst refSelectInput = ref();\n\nconst refSelectWrapper = ref();\nconst { options, modelValue, border } = toRefs(props);\n\nconst anchorRef = ref();\n\nconst open = ref(false);\nconst elementRef = ref();\n// const inputWrapper = ref()\n\nconst search = ref('');\n\nconst middleware = [\n  // size({\n  //   apply({ rects }) {\n  //     Object.assign(elementRef.value.style, {\n  //       width: `${rects.reference.width}px`,\n  //     });\n  //   },\n  // }),\n];\n\nconst computedOptions = ref<Option[]>([]);\n\nasync function filterBySearchTerm(value) {\n  if (props.searchFunction !== undefined) {\n    computedOptions.value = await props.searchFunction(value);\n  }\n\n  if (value === '') {\n    computedOptions.value = options.value;\n  } else {\n    computedOptions.value = options.value.filter((option) =>\n      option.title.toLowerCase().includes(value.toLowerCase())\n    );\n  }\n}\n\ndebouncedWatch(\n  options,\n  async () => {\n    if (!props.lazy) {\n      await filterBySearchTerm(search.value);\n    }\n  },\n  { debounce: 150 }\n);\n\ndebouncedWatch(\n  search,\n  async (value) => {\n    if (!props.lazy) {\n      await filterBySearchTerm(search.value);\n    }\n    emit('search', value);\n  },\n  { debounce: 150 }\n);\n\nonClickOutside(elementRef, (event) => {\n  if (!open.value) {\n    return;\n  }\n\n  if (refSelectWrapper.value?.wrapperRef.contains(event.target)) {\n    event.stopPropagation();\n  }\n\n  resetFields();\n  closeDropdown();\n});\n\nfunction openDropdown(event) {\n  if (props.readonly || props.disabled) {\n    return;\n  }\n  open.value = true;\n  sortOptionsBySelected();\n  nextTick(() => {\n    if (refSelectInput.value) {\n      refSelectInput.value.focus();\n    }\n  });\n  emit('focus');\n}\n\nfunction closeDropdown() {\n  open.value = false;\n  emit('blur');\n}\n\nonMounted(async () => {\n  await filterBySearchTerm('');\n});\n\nconst activeItem = computed(() => {\n  return props.options.find((item) => isEqual(item.value, props.modelValue));\n});\n\nfunction selectItem(item) {\n  if (Array.isArray(modelValue.value)) {\n    let updatedValue = modelValue.value;\n    if (modelValue.value.includes(item.value)) {\n      updatedValue = modelValue.value.filter((v) => v !== item.value);\n    } else {\n      updatedValue = [...updatedValue, item.value];\n    }\n    emit('input', updatedValue);\n    emit('change', updatedValue);\n    emit('update:modelValue', updatedValue);\n  } else {\n    if (item.value !== props.modelValue) {\n      emit('change', item.value);\n      emit('update:modelValue', item.value);\n    }\n    nextTick(() => {\n      closeDropdown();\n      emit('blur');\n    });\n  }\n}\n\nfunction resetFields() {\n  search.value = '';\n}\n\n// returns if options is selected\n// in form of boolean\nfunction optionSelected(option: any) {\n  if (Array.isArray(modelValue.value)) {\n    return modelValue.value.some((value: any) => isEqual(value, option.value));\n  }\n  return isEqual(option.value, modelValue.value);\n}\n\nfunction getInputTitle() {\n  if (Array.isArray(modelValue.value)) {\n    if (modelValue.value.length < 1) {\n      return props.placeholder;\n    }\n    const titles = [];\n    for (const value of modelValue.value) {\n      const found = options.value.find((o) => o.value === value);\n      if (found) {\n        titles.push(found.title);\n      }\n    }\n    return titles.join(', ');\n  } else {\n    return activeItem.value ? activeItem.value.title : props.placeholder;\n  }\n}\n\nfunction sortOptionsBySelected() {\n  if (Array.isArray(modelValue.value)) {\n    const selectedOptions = [];\n    const notSelectedOptions = [];\n\n    for (const option of computedOptions.value) {\n      if (modelValue.value.includes(option.value)) {\n        selectedOptions.push(option);\n      } else {\n        notSelectedOptions.push(option);\n      }\n    }\n\n    computedOptions.value = [...selectedOptions, ...notSelectedOptions];\n    console.log(computedOptions.value);\n  }\n}\n\nfunction controlAll() {\n  if (!Array.isArray(modelValue.value)) {\n    return;\n  }\n  if (modelValue.value.length < 1) {\n    selectAll();\n  } else {\n    deselectAll();\n  }\n}\n\nfunction selectAll() {\n  const newValue = computedOptions.value.map((option) => option.value);\n  emit('input', newValue);\n  emit('change', newValue);\n  emit('update:modelValue', newValue);\n}\n\nfunction deselectAll() {\n  const newValue = [];\n  emit('input', newValue);\n  emit('change', newValue);\n  emit('update:modelValue', newValue);\n}\n</script>\n<template>\n  <RobustInputWrapper\n    ref=\"refSelectWrapper\"\n    type=\"button\"\n    :title=\"title\"\n    :hint=\"hint\"\n    :error=\"error\"\n    :class=\"$props.class\"\n    :readonly=\"readonly\"\n    :condensed=\"condensed\"\n    :box-class=\"!border && 'border-0 focus-within:ring-0'\"\n    v-bind=\"$attrs\"\n    @click.stop=\"openDropdown\"\n    @focus.stop=\"openDropdown\"\n    @blur.stop=\"closeDropdown\"\n  >\n    <div\n      v-if=\"$slots.prefix\"\n      class=\"pointer-events-none flex h-full select-none items-center pr-2 text-gray-400\"\n      :class=\"[condensed ? 'pl-2' : 'pl-3']\"\n    >\n      <slot tag=\"div\" name=\"prefix\" />\n    </div>\n    <div\n      ref=\"refSelect\"\n      class=\"relative flex h-full min-w-0 flex-1 flex-shrink items-center bg-transparent text-current outline-none\"\n      :class=\"[$slots.prefix || condensed ? 'pl-2' : 'pl-3']\"\n      v-bind=\"$attrs\"\n    >\n      <div\n        :class=\"[!open || !searchable ? 'opacity-100' : 'opacity-0']\"\n        class=\"min-w-0 select-none truncate\"\n      >\n        {{ getInputTitle() }}\n      </div>\n      <input\n        v-if=\"open && searchable\"\n        ref=\"refSelectInput\"\n        v-model=\"search\"\n        class=\"pointer-events-none absolute block h-full w-full min-w-0 select-none bg-transparent text-current outline-none\"\n        :class=\"[$slots.prefix || condensed ? 'pl-2' : 'pl-3']\"\n      />\n    </div>\n    <div\n      class=\"pointer-events-none flex h-full flex-shrink-0 select-none items-center pr-3 text-gray-400 dark:text-gray-500\"\n      :class=\"[condensed ? 'pl-2' : 'pl-3']\"\n    >\n      <PhCaretDown\n        v-if=\"!readonly && !disabled\"\n        :size=\"14\"\n        weight=\"bold\"\n        class=\"transition-transform duration-200\"\n        :class=\"{ 'rotate-180 transform': open }\"\n      />\n    </div>\n  </RobustInputWrapper>\n  <FLoating\n    v-if=\"refSelectWrapper?.wrapperRef\"\n    ref=\"elementRef\"\n    v-model:open=\"open\"\n    same-size\n    class=\"z-[100] origin-top select-none overflow-hidden\"\n    :reference=\"refSelectWrapper?.wrapperRef\"\n    :middleware=\"middleware\"\n  >\n    <div v-if=\"Array.isArray(modelValue)\" class=\"flex justify-end px-4 py-2\">\n      <button type=\"button\" class=\"select-none font-light\" @click=\"controlAll\">\n        <div v-if=\"Array.isArray(modelValue) && modelValue.length > 0\">\n          Clear all\n        </div>\n        <div v-else>Select all</div>\n      </button>\n    </div>\n    <div v-if=\"computedOptions.length > 0\" class=\"max-h-72 overflow-auto\">\n      <button\n        v-for=\"option in computedOptions\"\n        :key=\"String(option.value)\"\n        type=\"button\"\n        class=\"flex w-full min-w-0 max-w-full items-center gap-4 whitespace-pre-wrap px-4 py-2 text-left transition-colors duration-200 hover:bg-gray-100 dark:hover:bg-gray-700\"\n        :class=\"option.class\"\n        @click=\"selectItem(option)\"\n      >\n        <span>{{ option.title }}</span>\n        <PhCheck\n          v-if=\"optionSelected(option)\"\n          class=\"ml-auto flex-shrink-0 text-gray-400\"\n          weight=\"bold\"\n          size=\"14\"\n        />\n      </button>\n    </div>\n    <div v-else class=\"py-2 text-center text-gray-400\">No options</div>\n  </FLoating>\n</template>\n"],"names":["__default__","props","__props","emit","__emit","refSelectInput","ref","refSelectWrapper","options","modelValue","border","toRefs","open","elementRef","search","middleware","computedOptions","filterBySearchTerm","value","option","debouncedWatch","onClickOutside","event","_a","resetFields","closeDropdown","openDropdown","sortOptionsBySelected","nextTick","onMounted","activeItem","computed","item","isEqual","selectItem","updatedValue","v","optionSelected","getInputTitle","titles","found","o","selectedOptions","notSelectedOptions","controlAll","selectAll","deselectAll","newValue"],"mappings":";;;;;;;;;;;;;;;;GACAA,KAAe;AAAA,EACb,MAAM;AAAA,EACN,cAAc;AAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA,UAAMC,IAAQC,GAmERC,IAAOC,GASPC,IAAiBC,KAEjBC,IAAmBD,KACnB,EAAE,SAAAE,GAAS,YAAAC,GAAY,QAAAC,EAAO,IAAIC,EAAOV,CAAK;AAElC,IAAAK,EAAI;AAEhB,UAAAM,IAAON,EAAI,EAAK,GAChBO,IAAaP,KAGbQ,IAASR,EAAI,EAAE,GAEfS,IAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,GAUbC,IAAkBV,EAAc,CAAA,CAAE;AAExC,mBAAeW,EAAmBC,GAAO;AACnC,MAAAjB,EAAM,mBAAmB,WAC3Be,EAAgB,QAAQ,MAAMf,EAAM,eAAeiB,CAAK,IAGtDA,MAAU,KACZF,EAAgB,QAAQR,EAAQ,QAEhBQ,EAAA,QAAQR,EAAQ,MAAM;AAAA,QAAO,CAACW,MAC5CA,EAAO,MAAM,YAAc,EAAA,SAASD,EAAM,aAAa;AAAA,MAAA;AAAA,IAG7D;AAEA,IAAAE;AAAA,MACEZ;AAAA,MACA,YAAY;AACN,QAACP,EAAM,QACH,MAAAgB,EAAmBH,EAAO,KAAK;AAAA,MAEzC;AAAA,MACA,EAAE,UAAU,IAAI;AAAA,IAAA,GAGlBM;AAAA,MACEN;AAAA,MACA,OAAOI,MAAU;AACX,QAACjB,EAAM,QACH,MAAAgB,EAAmBH,EAAO,KAAK,GAEvCX,EAAK,UAAUe,CAAK;AAAA,MACtB;AAAA,MACA,EAAE,UAAU,IAAI;AAAA,IAAA,GAGHG,GAAAR,GAAY,CAACS,MAAU;;AAChC,MAACV,EAAK,WAINW,IAAAhB,EAAiB,UAAjB,QAAAgB,EAAwB,WAAW,SAASD,EAAM,WACpDA,EAAM,gBAAgB,GAGZE,KACEC;IAAA,CACf;AAED,aAASC,EAAaJ,GAAO;AACvB,MAAArB,EAAM,YAAYA,EAAM,aAG5BW,EAAK,QAAQ,IACSe,KACtBC,EAAS,MAAM;AACb,QAAIvB,EAAe,SACjBA,EAAe,MAAM;MACvB,CACD,GACDF,EAAK,OAAO;AAAA,IACd;AAEA,aAASsB,IAAgB;AACvB,MAAAb,EAAK,QAAQ,IACbT,EAAK,MAAM;AAAA,IACb;AAEA,IAAA0B,EAAU,YAAY;AACpB,YAAMZ,EAAmB,EAAE;AAAA,IAAA,CAC5B;AAEK,UAAAa,IAAaC,EAAS,MACnB9B,EAAM,QAAQ,KAAK,CAAC+B,MAASC,EAAQD,EAAK,OAAO/B,EAAM,UAAU,CAAC,CAC1E;AAED,aAASiC,EAAWF,GAAM;AACxB,UAAI,MAAM,QAAQvB,EAAW,KAAK,GAAG;AACnC,YAAI0B,IAAe1B,EAAW;AAC9B,QAAIA,EAAW,MAAM,SAASuB,EAAK,KAAK,IACtCG,IAAe1B,EAAW,MAAM,OAAO,CAAC2B,MAAMA,MAAMJ,EAAK,KAAK,IAE9DG,IAAe,CAAC,GAAGA,GAAcH,EAAK,KAAK,GAE7C7B,EAAK,SAASgC,CAAY,GAC1BhC,EAAK,UAAUgC,CAAY,GAC3BhC,EAAK,qBAAqBgC,CAAY;AAAA,MAAA;AAElC,QAAAH,EAAK,UAAU/B,EAAM,eAClBE,EAAA,UAAU6B,EAAK,KAAK,GACpB7B,EAAA,qBAAqB6B,EAAK,KAAK,IAEtCJ,EAAS,MAAM;AACC,UAAAH,KACdtB,EAAK,MAAM;AAAA,QAAA,CACZ;AAAA,IAEL;AAEA,aAASqB,IAAc;AACrB,MAAAV,EAAO,QAAQ;AAAA,IACjB;AAIA,aAASuB,EAAelB,GAAa;AACnC,aAAI,MAAM,QAAQV,EAAW,KAAK,IACzBA,EAAW,MAAM,KAAK,CAACS,MAAee,EAAQf,GAAOC,EAAO,KAAK,CAAC,IAEpEc,EAAQd,EAAO,OAAOV,EAAW,KAAK;AAAA,IAC/C;AAEA,aAAS6B,IAAgB;AACvB,UAAI,MAAM,QAAQ7B,EAAW,KAAK,GAAG;AAC/B,YAAAA,EAAW,MAAM,SAAS;AAC5B,iBAAOR,EAAM;AAEf,cAAMsC,IAAS,CAAA;AACJ,mBAAArB,KAAST,EAAW,OAAO;AAC9B,gBAAA+B,IAAQhC,EAAQ,MAAM,KAAK,CAACiC,MAAMA,EAAE,UAAUvB,CAAK;AACzD,UAAIsB,KACKD,EAAA,KAAKC,EAAM,KAAK;AAAA,QAE3B;AACO,eAAAD,EAAO,KAAK,IAAI;AAAA,MAAA;AAEvB,eAAOT,EAAW,QAAQA,EAAW,MAAM,QAAQ7B,EAAM;AAAA,IAE7D;AAEA,aAAS0B,IAAwB;AAC/B,UAAI,MAAM,QAAQlB,EAAW,KAAK,GAAG;AACnC,cAAMiC,IAAkB,CAAA,GAClBC,IAAqB,CAAA;AAEhB,mBAAAxB,KAAUH,EAAgB;AACnC,UAAIP,EAAW,MAAM,SAASU,EAAO,KAAK,IACxCuB,EAAgB,KAAKvB,CAAM,IAE3BwB,EAAmB,KAAKxB,CAAM;AAIlC,QAAAH,EAAgB,QAAQ,CAAC,GAAG0B,GAAiB,GAAGC,CAAkB,GACtD3B,EAAgB;AAAA,MAC9B;AAAA,IACF;AAEA,aAAS4B,IAAa;AACpB,MAAK,MAAM,QAAQnC,EAAW,KAAK,MAG/BA,EAAW,MAAM,SAAS,IAClBoC,MAEEC;IAEhB;AAEA,aAASD,IAAY;AACnB,YAAME,IAAW/B,EAAgB,MAAM,IAAI,CAACG,MAAWA,EAAO,KAAK;AACnE,MAAAhB,EAAK,SAAS4C,CAAQ,GACtB5C,EAAK,UAAU4C,CAAQ,GACvB5C,EAAK,qBAAqB4C,CAAQ;AAAA,IACpC;AAEA,aAASD,IAAc;AACrB,YAAMC,IAAW,CAAA;AACjB,MAAA5C,EAAK,SAAS4C,CAAQ,GACtB5C,EAAK,UAAU4C,CAAQ,GACvB5C,EAAK,qBAAqB4C,CAAQ;AAAA,IACpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}