@j-solution/components 1.9.4 → 1.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +5 -4
  2. package/assets/jwms-portal-frontend-CJtAlYis.css +1 -0
  3. package/assets/styles/j-components.css +1 -1
  4. package/assets/styles/main.css +12 -3
  5. package/components/atoms/JCombo.vue.cjs +1 -1
  6. package/components/atoms/JCombo.vue.cjs.map +1 -1
  7. package/components/atoms/JCombo.vue.js +1 -1
  8. package/components/atoms/JCombo.vue.js.map +1 -1
  9. package/components/atoms/JDatepicker.vue.cjs +1 -1
  10. package/components/atoms/JDatepicker.vue.cjs.map +1 -1
  11. package/components/atoms/JDatepicker.vue.js +1 -1
  12. package/components/atoms/JDatepicker.vue.js.map +1 -1
  13. package/components/atoms/JGrid.vue.cjs +1 -1
  14. package/components/atoms/JGrid.vue.js +2 -2
  15. package/components/atoms/JGrid.vue2.cjs.map +1 -1
  16. package/components/atoms/JGrid.vue2.js.map +1 -1
  17. package/components/atoms/JInput.vue.cjs +1 -1
  18. package/components/atoms/JInput.vue.cjs.map +1 -1
  19. package/components/atoms/JInput.vue.js +1 -1
  20. package/components/atoms/JInput.vue.js.map +1 -1
  21. package/components/atoms/JSearchCombo.vue.cjs +1 -1
  22. package/components/atoms/JSearchCombo.vue.cjs.map +1 -1
  23. package/components/atoms/JSearchCombo.vue.js +5 -5
  24. package/components/atoms/JSearchCombo.vue.js.map +1 -1
  25. package/components/atoms/JSplitter.vue.cjs +1 -1
  26. package/components/atoms/JSplitter.vue.js +2 -2
  27. package/components/atoms/JSplitter.vue2.cjs +1 -1
  28. package/components/atoms/JSplitter.vue2.cjs.map +1 -1
  29. package/components/atoms/JSplitter.vue2.js +14 -10
  30. package/components/atoms/JSplitter.vue2.js.map +1 -1
  31. package/components/molecules/JTabs.vue.cjs +1 -1
  32. package/components/molecules/JTabs.vue.js +2 -2
  33. package/components/molecules/JTabs.vue2.cjs +1 -1
  34. package/components/molecules/JTabs.vue2.cjs.map +1 -1
  35. package/components/molecules/JTabs.vue2.js +4 -4
  36. package/components/molecules/JTabs.vue2.js.map +1 -1
  37. package/components/organisms/JDynamicForm.vue.cjs +1 -1
  38. package/components/organisms/JDynamicForm.vue.js +1 -1
  39. package/components/organisms/JDynamicForm.vue2.cjs +1 -1
  40. package/components/organisms/JDynamicForm.vue2.cjs.map +1 -1
  41. package/components/organisms/JDynamicForm.vue2.js +35 -35
  42. package/components/organisms/JDynamicForm.vue2.js.map +1 -1
  43. package/components/organisms/JFilterBar.vue.cjs +1 -1
  44. package/components/organisms/JFilterBar.vue.js +2 -2
  45. package/components/organisms/JFilterBar.vue2.cjs +1 -1
  46. package/components/organisms/JFilterBar.vue2.cjs.map +1 -1
  47. package/components/organisms/JFilterBar.vue2.js +2 -2
  48. package/components/organisms/JFilterBar.vue2.js.map +1 -1
  49. package/components/organisms/JPageContainer.vue.cjs +1 -1
  50. package/components/organisms/JPageContainer.vue.cjs.map +1 -1
  51. package/components/organisms/JPageContainer.vue.js +4 -4
  52. package/components/organisms/JPageContainer.vue.js.map +1 -1
  53. package/components/organisms/JSearchPanel.vue.cjs +1 -1
  54. package/components/organisms/JSearchPanel.vue.js +2 -2
  55. package/components/organisms/JSearchPanel.vue2.cjs +1 -1
  56. package/components/organisms/JSearchPanel.vue2.cjs.map +1 -1
  57. package/components/organisms/JSearchPanel.vue2.js +3 -3
  58. package/components/organisms/JSearchPanel.vue2.js.map +1 -1
  59. package/components/shadcn/Input.vue.cjs +1 -1
  60. package/components/shadcn/Input.vue.cjs.map +1 -1
  61. package/components/shadcn/Input.vue.js +1 -1
  62. package/components/shadcn/Input.vue.js.map +1 -1
  63. package/components/shadcn/SelectTrigger.vue.cjs +1 -1
  64. package/components/shadcn/SelectTrigger.vue.cjs.map +1 -1
  65. package/components/shadcn/SelectTrigger.vue.js +1 -1
  66. package/components/shadcn/SelectTrigger.vue.js.map +1 -1
  67. package/components/shadcn/resizable/ResizableHandle.vue.cjs +1 -1
  68. package/components/shadcn/resizable/ResizableHandle.vue.cjs.map +1 -1
  69. package/components/shadcn/resizable/ResizableHandle.vue.js +3 -3
  70. package/components/shadcn/resizable/ResizableHandle.vue.js.map +1 -1
  71. package/package.json +1 -1
  72. package/assets/jwms-portal-frontend-Bd12BmyZ.css +0 -1
@@ -29,7 +29,7 @@ const V = /* @__PURE__ */ c({
29
29
  class: "border-amber-500 focus-visible:ring-amber-500"
30
30
  },
31
31
  sm: {
32
- class: "h-8 text-xs px-2"
32
+ class: "h-7 text-xs px-2"
33
33
  },
34
34
  lg: {
35
35
  class: "h-12 text-base px-4"
@@ -1 +1 @@
1
- {"version":3,"file":"JInput.vue.js","sources":["../../../../src/components/atoms/JInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { Input } from '@/components/shadcn'\r\n\r\ntype StyleType =\r\n | 'default' // 기본 스타일\r\n | 'error' // 에러 상태 (빨강 테두리)\r\n | 'success' // 성공 상태 (초록 테두리)\r\n | 'warning' // 경고 상태 (주황 테두리)\r\n | 'sm' // 작은 크기\r\n | 'lg' // 큰 크기\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n modelValue?: string | number\r\n type?: string\r\n placeholder?: string\r\n disabled?: boolean\r\n readonly?: boolean\r\n required?: boolean\r\n name?: string\r\n id?: string\r\n class?: string\r\n /** 스타일 프리셋 */\r\n styletype?: StyleType\r\n }>(),\r\n {\r\n type: 'text',\r\n placeholder: '',\r\n disabled: false,\r\n readonly: false,\r\n required: false,\r\n styletype: 'default',\r\n },\r\n)\r\n\r\nconst emit = defineEmits<{\r\n 'update:modelValue': [value: string | number]\r\n 'change': [value: string | number]\r\n 'focus': [event: FocusEvent]\r\n 'blur': [event: FocusEvent]\r\n}>()\r\n\r\n/**\r\n * styletype -> class 매핑\r\n */\r\nconst STYLE_PRESETS: Record<StyleType, { class: string }> = {\r\n default: { class: '' },\r\n error: { \r\n class: 'border-destructive focus-visible:ring-destructive',\r\n },\r\n success: { \r\n class: 'border-green-500 focus-visible:ring-green-500',\r\n },\r\n warning: { \r\n class: 'border-amber-500 focus-visible:ring-amber-500',\r\n },\r\n sm: { \r\n class: 'h-8 text-xs px-2',\r\n },\r\n lg: { \r\n class: 'h-12 text-base px-4',\r\n },\r\n}\r\n\r\n/** 최종 바인딩: 직접 넘긴 class가 있으면 styletype 기본값과 병합 */\r\nconst mapped = computed(() => {\r\n const styleKey = props.styletype || 'default'\r\n const preset = STYLE_PRESETS[styleKey] ?? STYLE_PRESETS.default\r\n const finalClass = [preset?.class, props.class].filter(Boolean).join(' ')\r\n \r\n return {\r\n type: props.type,\r\n placeholder: props.placeholder,\r\n disabled: props.disabled,\r\n readonly: props.readonly,\r\n required: props.required,\r\n name: props.name,\r\n id: props.id,\r\n class: finalClass,\r\n }\r\n})\r\n\r\nconst handleInput = (value: string | number) => {\r\n emit('update:modelValue', value)\r\n emit('change', value)\r\n}\r\n</script>\r\n\r\n<template>\r\n <Input \r\n v-bind=\"mapped\" \r\n :model-value=\"modelValue\" \r\n @update:model-value=\"handleInput\"\r\n @focus=\"emit('focus', $event)\"\r\n @blur=\"emit('blur', $event)\"\r\n >\r\n <slot />\r\n </Input>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","STYLE_PRESETS","mapped","computed","styleKey","finalClass","handleInput","value","_createBlock","_unref","Input","_mergeProps","_cache","$event","_renderSlot","_ctx"],"mappings":";;;;;;;;;;;;;;;;;;;AAYA,UAAMA,IAAQC,GAwBRC,IAAOC,GAUPC,IAAsD;AAAA,MAC1D,SAAS,EAAE,OAAO,GAAA;AAAA,MAClB,OAAO;AAAA,QACL,OAAO;AAAA,MAAA;AAAA,MAET,SAAS;AAAA,QACP,OAAO;AAAA,MAAA;AAAA,MAET,SAAS;AAAA,QACP,OAAO;AAAA,MAAA;AAAA,MAET,IAAI;AAAA,QACF,OAAO;AAAA,MAAA;AAAA,MAET,IAAI;AAAA,QACF,OAAO;AAAA,MAAA;AAAA,IACT,GAIIC,IAASC,EAAS,MAAM;AAC5B,YAAMC,IAAWP,EAAM,aAAa,WAE9BQ,IAAa,EADJJ,EAAcG,CAAQ,KAAKH,EAAc,UAC5B,OAAOJ,EAAM,KAAK,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAExE,aAAO;AAAA,QACL,MAAMA,EAAM;AAAA,QACZ,aAAaA,EAAM;AAAA,QACnB,UAAUA,EAAM;AAAA,QAChB,UAAUA,EAAM;AAAA,QAChB,UAAUA,EAAM;AAAA,QAChB,MAAMA,EAAM;AAAA,QACZ,IAAIA,EAAM;AAAA,QACV,OAAOQ;AAAA,MAAA;AAAA,IAEX,CAAC,GAEKC,IAAc,CAACC,MAA2B;AAC9C,MAAAR,EAAK,qBAAqBQ,CAAK,GAC/BR,EAAK,UAAUQ,CAAK;AAAA,IACtB;2BAIEC,EAQQC,EAAAC,CAAA,GARRC,EAQQT,EAAA,OAPQ;AAAA,MACb,eAAaJ,EAAA;AAAA,MACb,uBAAoBQ;AAAA,MACpB,SAAKM,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAC,MAAEd,EAAI,SAAUc,CAAM;AAAA,MAC3B,QAAID,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAC,MAAEd,EAAI,QAASc,CAAM;AAAA,IAAA;iBAE1B,MAAQ;AAAA,QAARC,EAAQC,EAAA,QAAA,SAAA;AAAA,MAAA;;;;;"}
1
+ {"version":3,"file":"JInput.vue.js","sources":["../../../../src/components/atoms/JInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { Input } from '@/components/shadcn'\r\n\r\ntype StyleType =\r\n | 'default' // 기본 스타일\r\n | 'error' // 에러 상태 (빨강 테두리)\r\n | 'success' // 성공 상태 (초록 테두리)\r\n | 'warning' // 경고 상태 (주황 테두리)\r\n | 'sm' // 작은 크기\r\n | 'lg' // 큰 크기\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n modelValue?: string | number\r\n type?: string\r\n placeholder?: string\r\n disabled?: boolean\r\n readonly?: boolean\r\n required?: boolean\r\n name?: string\r\n id?: string\r\n class?: string\r\n /** 스타일 프리셋 */\r\n styletype?: StyleType\r\n }>(),\r\n {\r\n type: 'text',\r\n placeholder: '',\r\n disabled: false,\r\n readonly: false,\r\n required: false,\r\n styletype: 'default',\r\n },\r\n)\r\n\r\nconst emit = defineEmits<{\r\n 'update:modelValue': [value: string | number]\r\n 'change': [value: string | number]\r\n 'focus': [event: FocusEvent]\r\n 'blur': [event: FocusEvent]\r\n}>()\r\n\r\n/**\r\n * styletype -> class 매핑\r\n */\r\nconst STYLE_PRESETS: Record<StyleType, { class: string }> = {\r\n default: { class: '' },\r\n error: { \r\n class: 'border-destructive focus-visible:ring-destructive',\r\n },\r\n success: { \r\n class: 'border-green-500 focus-visible:ring-green-500',\r\n },\r\n warning: { \r\n class: 'border-amber-500 focus-visible:ring-amber-500',\r\n },\r\n sm: { \n class: 'h-7 text-xs px-2',\n },\n lg: { \r\n class: 'h-12 text-base px-4',\r\n },\r\n}\r\n\r\n/** 최종 바인딩: 직접 넘긴 class가 있으면 styletype 기본값과 병합 */\r\nconst mapped = computed(() => {\r\n const styleKey = props.styletype || 'default'\r\n const preset = STYLE_PRESETS[styleKey] ?? STYLE_PRESETS.default\r\n const finalClass = [preset?.class, props.class].filter(Boolean).join(' ')\r\n \r\n return {\r\n type: props.type,\r\n placeholder: props.placeholder,\r\n disabled: props.disabled,\r\n readonly: props.readonly,\r\n required: props.required,\r\n name: props.name,\r\n id: props.id,\r\n class: finalClass,\r\n }\r\n})\r\n\r\nconst handleInput = (value: string | number) => {\r\n emit('update:modelValue', value)\r\n emit('change', value)\r\n}\r\n</script>\r\n\r\n<template>\r\n <Input \r\n v-bind=\"mapped\" \r\n :model-value=\"modelValue\" \r\n @update:model-value=\"handleInput\"\r\n @focus=\"emit('focus', $event)\"\r\n @blur=\"emit('blur', $event)\"\r\n >\r\n <slot />\r\n </Input>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","STYLE_PRESETS","mapped","computed","styleKey","finalClass","handleInput","value","_createBlock","_unref","Input","_mergeProps","_cache","$event","_renderSlot","_ctx"],"mappings":";;;;;;;;;;;;;;;;;;;AAYA,UAAMA,IAAQC,GAwBRC,IAAOC,GAUPC,IAAsD;AAAA,MAC1D,SAAS,EAAE,OAAO,GAAA;AAAA,MAClB,OAAO;AAAA,QACL,OAAO;AAAA,MAAA;AAAA,MAET,SAAS;AAAA,QACP,OAAO;AAAA,MAAA;AAAA,MAET,SAAS;AAAA,QACP,OAAO;AAAA,MAAA;AAAA,MAET,IAAI;AAAA,QACF,OAAO;AAAA,MAAA;AAAA,MAET,IAAI;AAAA,QACF,OAAO;AAAA,MAAA;AAAA,IACT,GAIIC,IAASC,EAAS,MAAM;AAC5B,YAAMC,IAAWP,EAAM,aAAa,WAE9BQ,IAAa,EADJJ,EAAcG,CAAQ,KAAKH,EAAc,UAC5B,OAAOJ,EAAM,KAAK,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAExE,aAAO;AAAA,QACL,MAAMA,EAAM;AAAA,QACZ,aAAaA,EAAM;AAAA,QACnB,UAAUA,EAAM;AAAA,QAChB,UAAUA,EAAM;AAAA,QAChB,UAAUA,EAAM;AAAA,QAChB,MAAMA,EAAM;AAAA,QACZ,IAAIA,EAAM;AAAA,QACV,OAAOQ;AAAA,MAAA;AAAA,IAEX,CAAC,GAEKC,IAAc,CAACC,MAA2B;AAC9C,MAAAR,EAAK,qBAAqBQ,CAAK,GAC/BR,EAAK,UAAUQ,CAAK;AAAA,IACtB;2BAIEC,EAQQC,EAAAC,CAAA,GARRC,EAQQT,EAAA,OAPQ;AAAA,MACb,eAAaJ,EAAA;AAAA,MACb,uBAAoBQ;AAAA,MACpB,SAAKM,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAC,MAAEd,EAAI,SAAUc,CAAM;AAAA,MAC3B,QAAID,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAC,MAAEd,EAAI,QAASc,CAAM;AAAA,IAAA;iBAE1B,MAAQ;AAAA,QAARC,EAAQC,EAAA,QAAA,SAAA;AAAA,MAAA;;;;;"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),n=require("lucide-vue-next"),o=require("../../lib/utils.cjs");require("../shadcn/index.cjs");const b=require("../shadcn/Combobox.vue.cjs"),x=require("../shadcn/ComboboxAnchor.vue.cjs"),_=require("../shadcn/ComboboxTrigger.vue.cjs"),v=require("../shadcn/Button.vue.cjs"),C=require("../shadcn/ComboboxList.vue.cjs"),h=require("../shadcn/ComboboxInput.vue.cjs"),y=require("../shadcn/ComboboxEmpty.vue.cjs"),g=require("../shadcn/ComboboxGroup.vue.cjs"),V=require("../shadcn/ComboboxItem.vue.cjs"),q={class:"relative w-full max-w-sm items-center"},N={class:"absolute start-0 inset-y-0 flex items-center justify-center px-2.5"},w=e.defineComponent({__name:"JSearchCombo",props:{modelValue:{},options:{default:()=>[]},placeholder:{default:"선택해주세요."},searchPlaceholder:{default:""},emptyText:{default:"검색 결과가 없습니다."},disabled:{type:Boolean,default:!1},required:{type:Boolean,default:!1},name:{},id:{},multiple:{type:Boolean,default:!1},class:{},styletype:{default:"default"}},emits:["update:modelValue","change","focus","blur"],setup(t,{emit:c}){const d=t,r=c,i={default:{variant:"outline",buttonClass:"h-8 text-xs px-2.5"},error:{variant:"outline",buttonClass:"h-8 text-xs px-2.5 border-destructive text-destructive",inputClass:"border-destructive focus:ring-destructive"},success:{variant:"outline",buttonClass:"h-8 text-xs px-2.5 border-green-500 text-green-700",inputClass:"border-green-500 focus:ring-green-500"},warning:{variant:"outline",buttonClass:"h-8 text-xs px-2.5 border-amber-500 text-amber-700",inputClass:"border-amber-500 focus:ring-amber-500"},sm:{variant:"outline",buttonClass:"h-7 text-[11px] px-2",inputClass:"h-7 text-[11px] px-2 py-1"},lg:{variant:"outline",buttonClass:"h-10 text-sm px-3",inputClass:"h-10 text-sm px-3 py-2"}},s=e.computed(()=>{const a=d.styletype||"default";return i[a]??i.default}),p=e.computed(()=>o.cn("justify-between",s.value?.buttonClass,d.class)),f=e.computed(()=>o.cn("pl-8 pr-2 py-1.5 focus-visible:ring-0 border-0 border-b rounded-none h-8 text-xs",s.value?.inputClass)),m=a=>{r("update:modelValue",a),r("change",a)};return(a,l)=>(e.openBlock(),e.createBlock(e.unref(b.default),{"model-value":t.modelValue,by:"label",disabled:t.disabled,required:t.required,name:t.name,"onUpdate:modelValue":l[2]||(l[2]=u=>m(u))},{default:e.withCtx(()=>[e.createVNode(e.unref(x.default),{"as-child":""},{default:e.withCtx(()=>[e.createVNode(e.unref(_.default),{"as-child":""},{default:e.withCtx(()=>[e.createVNode(e.unref(v.default),{id:t.id,variant:s.value.variant,class:e.normalizeClass(p.value),disabled:t.disabled,onFocus:l[0]||(l[0]=u=>r("focus",u)),onBlur:l[1]||(l[1]=u=>r("blur",u))},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(t.modelValue?.label??t.placeholder)+" ",1),e.createVNode(e.unref(n.ChevronsUpDown),{class:"ml-2 h-3.5 w-3.5 shrink-0 opacity-50"})]),_:1},8,["id","variant","class","disabled"])]),_:1})]),_:1}),e.createVNode(e.unref(C.default),null,{default:e.withCtx(()=>[e.createElementVNode("div",q,[e.createVNode(e.unref(h.default),{class:e.normalizeClass(f.value),placeholder:t.searchPlaceholder},null,8,["class","placeholder"]),e.createElementVNode("span",N,[e.createVNode(e.unref(n.Search),{class:"size-3.5 text-muted-foreground"})])]),e.createVNode(e.unref(y.default),null,{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(t.emptyText),1)]),_:1}),e.createVNode(e.unref(g.default),null,{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.options,u=>(e.openBlock(),e.createBlock(e.unref(V.default),{key:u.value,value:u},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(u.label)+" ",1),t.modelValue?.value===u.value?(e.openBlock(),e.createBlock(e.unref(n.Check),{key:0,class:e.normalizeClass(e.unref(o.cn)("ml-auto h-3.5 w-3.5"))},null,8,["class"])):e.createCommentVNode("",!0)]),_:2},1032,["value"]))),128))]),_:1})]),_:1})]),_:1},8,["model-value","disabled","required","name"]))}});exports.default=w;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),n=require("lucide-vue-next"),o=require("../../lib/utils.cjs");require("../shadcn/index.cjs");const b=require("../shadcn/Combobox.vue.cjs"),x=require("../shadcn/ComboboxAnchor.vue.cjs"),_=require("../shadcn/ComboboxTrigger.vue.cjs"),v=require("../shadcn/Button.vue.cjs"),C=require("../shadcn/ComboboxList.vue.cjs"),h=require("../shadcn/ComboboxInput.vue.cjs"),y=require("../shadcn/ComboboxEmpty.vue.cjs"),g=require("../shadcn/ComboboxGroup.vue.cjs"),V=require("../shadcn/ComboboxItem.vue.cjs"),q={class:"relative w-full max-w-sm items-center"},N={class:"absolute start-0 inset-y-0 flex items-center justify-center px-2.5"},w=e.defineComponent({__name:"JSearchCombo",props:{modelValue:{},options:{default:()=>[]},placeholder:{default:"선택해주세요."},searchPlaceholder:{default:""},emptyText:{default:"검색 결과가 없습니다."},disabled:{type:Boolean,default:!1},required:{type:Boolean,default:!1},name:{},id:{},multiple:{type:Boolean,default:!1},class:{},styletype:{default:"default"}},emits:["update:modelValue","change","focus","blur"],setup(t,{emit:c}){const d=t,r=c,i={default:{variant:"outline",buttonClass:"h-7 text-xs px-2.5"},error:{variant:"outline",buttonClass:"h-7 text-xs px-2.5 border-destructive text-destructive",inputClass:"border-destructive focus:ring-destructive"},success:{variant:"outline",buttonClass:"h-7 text-xs px-2.5 border-green-500 text-green-700",inputClass:"border-green-500 focus:ring-green-500"},warning:{variant:"outline",buttonClass:"h-7 text-xs px-2.5 border-amber-500 text-amber-700",inputClass:"border-amber-500 focus:ring-amber-500"},sm:{variant:"outline",buttonClass:"h-7 text-[11px] px-2",inputClass:"h-7 text-[11px] px-2 py-1"},lg:{variant:"outline",buttonClass:"h-10 text-sm px-3",inputClass:"h-10 text-sm px-3 py-2"}},s=e.computed(()=>{const a=d.styletype||"default";return i[a]??i.default}),p=e.computed(()=>o.cn("justify-between",s.value?.buttonClass,d.class)),f=e.computed(()=>o.cn("pl-8 pr-2 py-1 focus-visible:ring-0 border-0 border-b rounded-none h-7 text-xs",s.value?.inputClass)),m=a=>{r("update:modelValue",a),r("change",a)};return(a,l)=>(e.openBlock(),e.createBlock(e.unref(b.default),{"model-value":t.modelValue,by:"label",disabled:t.disabled,required:t.required,name:t.name,"onUpdate:modelValue":l[2]||(l[2]=u=>m(u))},{default:e.withCtx(()=>[e.createVNode(e.unref(x.default),{"as-child":""},{default:e.withCtx(()=>[e.createVNode(e.unref(_.default),{"as-child":""},{default:e.withCtx(()=>[e.createVNode(e.unref(v.default),{id:t.id,variant:s.value.variant,class:e.normalizeClass(p.value),disabled:t.disabled,onFocus:l[0]||(l[0]=u=>r("focus",u)),onBlur:l[1]||(l[1]=u=>r("blur",u))},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(t.modelValue?.label??t.placeholder)+" ",1),e.createVNode(e.unref(n.ChevronsUpDown),{class:"ml-2 h-3.5 w-3.5 shrink-0 opacity-50"})]),_:1},8,["id","variant","class","disabled"])]),_:1})]),_:1}),e.createVNode(e.unref(C.default),null,{default:e.withCtx(()=>[e.createElementVNode("div",q,[e.createVNode(e.unref(h.default),{class:e.normalizeClass(f.value),placeholder:t.searchPlaceholder},null,8,["class","placeholder"]),e.createElementVNode("span",N,[e.createVNode(e.unref(n.Search),{class:"size-3.5 text-muted-foreground"})])]),e.createVNode(e.unref(y.default),null,{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(t.emptyText),1)]),_:1}),e.createVNode(e.unref(g.default),null,{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.options,u=>(e.openBlock(),e.createBlock(e.unref(V.default),{key:u.value,value:u},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(u.label)+" ",1),t.modelValue?.value===u.value?(e.openBlock(),e.createBlock(e.unref(n.Check),{key:0,class:e.normalizeClass(e.unref(o.cn)("ml-auto h-3.5 w-3.5"))},null,8,["class"])):e.createCommentVNode("",!0)]),_:2},1032,["value"]))),128))]),_:1})]),_:1})]),_:1},8,["model-value","disabled","required","name"]))}});exports.default=w;
2
2
  //# sourceMappingURL=JSearchCombo.vue.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"JSearchCombo.vue.cjs","sources":["../../../../src/components/atoms/JSearchCombo.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { Check, ChevronsUpDown, Search } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport {\n Button,\n Combobox,\n ComboboxAnchor,\n ComboboxEmpty,\n ComboboxGroup,\n ComboboxInput,\n ComboboxItem,\n ComboboxList,\n ComboboxTrigger,\n} from '@/components/shadcn'\n\nexport interface ComboboxOption {\n value: string | number\n label: string\n}\n\ntype StyleType =\n | 'default' // 기본 스타일\n | 'error' // 에러 상태\n | 'success' // 성공 상태\n | 'warning' // 경고 상태\n | 'sm' // 작은 크기\n | 'lg' // 큰 크기\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: ComboboxOption\n options?: ComboboxOption[]\n placeholder?: string\n searchPlaceholder?: string\n emptyText?: string\n disabled?: boolean\n required?: boolean\n name?: string\n id?: string\n multiple?: boolean\n class?: string\n /** 스타일 프리셋 */\n styletype?: StyleType\n }>(),\n {\n options: () => [],\n placeholder: '선택해주세요.',\n searchPlaceholder: '',\n emptyText: '검색 결과가 없습니다.',\n disabled: false,\n required: false,\n multiple: false,\n styletype: 'default',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: ComboboxOption | undefined]\n 'change': [value: ComboboxOption | undefined]\n 'focus': [event: FocusEvent]\n 'blur': [event: FocusEvent]\n}>()\n\n/**\n * styletype -> variant/class 매핑\n */\nconst STYLE_PRESETS: Record<StyleType, { variant: 'default' | 'outline' | 'destructive' | 'secondary' | 'ghost' | 'link', buttonClass?: string, inputClass?: string }> = {\n default: { \n variant: 'outline',\n buttonClass: 'h-8 text-xs px-2.5',\n },\n error: { \n variant: 'outline',\n buttonClass: 'h-8 text-xs px-2.5 border-destructive text-destructive',\n inputClass: 'border-destructive focus:ring-destructive',\n },\n success: { \n variant: 'outline',\n buttonClass: 'h-8 text-xs px-2.5 border-green-500 text-green-700',\n inputClass: 'border-green-500 focus:ring-green-500',\n },\n warning: { \n variant: 'outline',\n buttonClass: 'h-8 text-xs px-2.5 border-amber-500 text-amber-700',\n inputClass: 'border-amber-500 focus:ring-amber-500',\n },\n sm: { \n variant: 'outline',\n buttonClass: 'h-7 text-[11px] px-2',\n inputClass: 'h-7 text-[11px] px-2 py-1',\n },\n lg: { \n variant: 'outline',\n buttonClass: 'h-10 text-sm px-3',\n inputClass: 'h-10 text-sm px-3 py-2',\n },\n}\n\nconst preset = computed(() => {\n const styleKey = props.styletype || 'default'\n return STYLE_PRESETS[styleKey] ?? STYLE_PRESETS.default\n})\n\nconst buttonClass = computed(() => {\n return cn('justify-between', preset.value?.buttonClass, props.class)\n})\n\nconst inputClass = computed(() => {\n return cn('pl-8 pr-2 py-1.5 focus-visible:ring-0 border-0 border-b rounded-none h-8 text-xs', preset.value?.inputClass)\n})\n\nconst handleChange = (value: ComboboxOption | undefined) => {\n emit('update:modelValue', value)\n emit('change', value)\n}\n</script>\n\n<template>\n <Combobox \n :model-value=\"modelValue\" \n by=\"label\" \n :disabled=\"disabled\"\n :required=\"required\"\n :name=\"name\"\n @update:model-value=\"(value) => handleChange(value as ComboboxOption | undefined)\"\n >\n <ComboboxAnchor as-child>\n <ComboboxTrigger as-child>\n <Button \n :id=\"id\"\n :variant=\"preset.variant\" \n :class=\"buttonClass\"\n :disabled=\"disabled\"\n @focus=\"emit('focus', $event as FocusEvent)\"\n @blur=\"emit('blur', $event as FocusEvent)\"\n >\n {{ modelValue?.label ?? placeholder }}\n <ChevronsUpDown class=\"ml-2 h-3.5 w-3.5 shrink-0 opacity-50\" />\n </Button>\n </ComboboxTrigger>\n </ComboboxAnchor>\n\n <ComboboxList>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput :class=\"inputClass\" :placeholder=\"searchPlaceholder\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-2.5\">\n <Search class=\"size-3.5 text-muted-foreground\" />\n </span>\n </div>\n\n <ComboboxEmpty>\n {{ emptyText }}\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"option in options\"\n :key=\"option.value\"\n :value=\"option\"\n >\n {{ option.label }}\n\n <Check v-if=\"modelValue?.value === option.value\" :class=\"cn('ml-auto h-3.5 w-3.5')\" />\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n"],"names":["props","__props","emit","__emit","STYLE_PRESETS","preset","computed","styleKey","buttonClass","cn","inputClass","handleChange","value","_createBlock","_unref","Combobox","_cache","_createVNode","ComboboxAnchor","ComboboxTrigger","Button","$event","_createTextVNode","_toDisplayString","ChevronsUpDown","ComboboxList","_createElementVNode","_hoisted_1","ComboboxInput","_hoisted_2","Search","ComboboxEmpty","ComboboxGroup","_createElementBlock","_Fragment","_renderList","option","ComboboxItem","Check"],"mappings":"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"}
1
+ {"version":3,"file":"JSearchCombo.vue.cjs","sources":["../../../../src/components/atoms/JSearchCombo.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { Check, ChevronsUpDown, Search } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport {\n Button,\n Combobox,\n ComboboxAnchor,\n ComboboxEmpty,\n ComboboxGroup,\n ComboboxInput,\n ComboboxItem,\n ComboboxList,\n ComboboxTrigger,\n} from '@/components/shadcn'\n\nexport interface ComboboxOption {\n value: string | number\n label: string\n}\n\ntype StyleType =\n | 'default' // 기본 스타일\n | 'error' // 에러 상태\n | 'success' // 성공 상태\n | 'warning' // 경고 상태\n | 'sm' // 작은 크기\n | 'lg' // 큰 크기\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: ComboboxOption\n options?: ComboboxOption[]\n placeholder?: string\n searchPlaceholder?: string\n emptyText?: string\n disabled?: boolean\n required?: boolean\n name?: string\n id?: string\n multiple?: boolean\n class?: string\n /** 스타일 프리셋 */\n styletype?: StyleType\n }>(),\n {\n options: () => [],\n placeholder: '선택해주세요.',\n searchPlaceholder: '',\n emptyText: '검색 결과가 없습니다.',\n disabled: false,\n required: false,\n multiple: false,\n styletype: 'default',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: ComboboxOption | undefined]\n 'change': [value: ComboboxOption | undefined]\n 'focus': [event: FocusEvent]\n 'blur': [event: FocusEvent]\n}>()\n\n/**\n * styletype -> variant/class 매핑\n */\nconst STYLE_PRESETS: Record<StyleType, { variant: 'default' | 'outline' | 'destructive' | 'secondary' | 'ghost' | 'link', buttonClass?: string, inputClass?: string }> = {\n default: { \n variant: 'outline',\n buttonClass: 'h-7 text-xs px-2.5',\n },\n error: { \n variant: 'outline',\n buttonClass: 'h-7 text-xs px-2.5 border-destructive text-destructive',\n inputClass: 'border-destructive focus:ring-destructive',\n },\n success: { \n variant: 'outline',\n buttonClass: 'h-7 text-xs px-2.5 border-green-500 text-green-700',\n inputClass: 'border-green-500 focus:ring-green-500',\n },\n warning: { \n variant: 'outline',\n buttonClass: 'h-7 text-xs px-2.5 border-amber-500 text-amber-700',\n inputClass: 'border-amber-500 focus:ring-amber-500',\n },\n sm: { \n variant: 'outline',\n buttonClass: 'h-7 text-[11px] px-2',\n inputClass: 'h-7 text-[11px] px-2 py-1',\n },\n lg: { \n variant: 'outline',\n buttonClass: 'h-10 text-sm px-3',\n inputClass: 'h-10 text-sm px-3 py-2',\n },\n}\n\nconst preset = computed(() => {\n const styleKey = props.styletype || 'default'\n return STYLE_PRESETS[styleKey] ?? STYLE_PRESETS.default\n})\n\nconst buttonClass = computed(() => {\n return cn('justify-between', preset.value?.buttonClass, props.class)\n})\n\nconst inputClass = computed(() => {\n return cn('pl-8 pr-2 py-1 focus-visible:ring-0 border-0 border-b rounded-none h-7 text-xs', preset.value?.inputClass)\n})\n\nconst handleChange = (value: ComboboxOption | undefined) => {\n emit('update:modelValue', value)\n emit('change', value)\n}\n</script>\n\n<template>\n <Combobox \n :model-value=\"modelValue\" \n by=\"label\" \n :disabled=\"disabled\"\n :required=\"required\"\n :name=\"name\"\n @update:model-value=\"(value) => handleChange(value as ComboboxOption | undefined)\"\n >\n <ComboboxAnchor as-child>\n <ComboboxTrigger as-child>\n <Button \n :id=\"id\"\n :variant=\"preset.variant\" \n :class=\"buttonClass\"\n :disabled=\"disabled\"\n @focus=\"emit('focus', $event as FocusEvent)\"\n @blur=\"emit('blur', $event as FocusEvent)\"\n >\n {{ modelValue?.label ?? placeholder }}\n <ChevronsUpDown class=\"ml-2 h-3.5 w-3.5 shrink-0 opacity-50\" />\n </Button>\n </ComboboxTrigger>\n </ComboboxAnchor>\n\n <ComboboxList>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput :class=\"inputClass\" :placeholder=\"searchPlaceholder\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-2.5\">\n <Search class=\"size-3.5 text-muted-foreground\" />\n </span>\n </div>\n\n <ComboboxEmpty>\n {{ emptyText }}\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"option in options\"\n :key=\"option.value\"\n :value=\"option\"\n >\n {{ option.label }}\n\n <Check v-if=\"modelValue?.value === option.value\" :class=\"cn('ml-auto h-3.5 w-3.5')\" />\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n"],"names":["props","__props","emit","__emit","STYLE_PRESETS","preset","computed","styleKey","buttonClass","cn","inputClass","handleChange","value","_createBlock","_unref","Combobox","_cache","_createVNode","ComboboxAnchor","ComboboxTrigger","Button","$event","_createTextVNode","_toDisplayString","ChevronsUpDown","ComboboxList","_createElementVNode","_hoisted_1","ComboboxInput","_hoisted_2","Search","ComboboxEmpty","ComboboxGroup","_createElementBlock","_Fragment","_renderList","option","ComboboxItem","Check"],"mappings":"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"}
@@ -32,21 +32,21 @@ const J = { class: "relative w-full max-w-sm items-center" }, K = { class: "abso
32
32
  const x = e, u = C, h = {
33
33
  default: {
34
34
  variant: "outline",
35
- buttonClass: "h-8 text-xs px-2.5"
35
+ buttonClass: "h-7 text-xs px-2.5"
36
36
  },
37
37
  error: {
38
38
  variant: "outline",
39
- buttonClass: "h-8 text-xs px-2.5 border-destructive text-destructive",
39
+ buttonClass: "h-7 text-xs px-2.5 border-destructive text-destructive",
40
40
  inputClass: "border-destructive focus:ring-destructive"
41
41
  },
42
42
  success: {
43
43
  variant: "outline",
44
- buttonClass: "h-8 text-xs px-2.5 border-green-500 text-green-700",
44
+ buttonClass: "h-7 text-xs px-2.5 border-green-500 text-green-700",
45
45
  inputClass: "border-green-500 focus:ring-green-500"
46
46
  },
47
47
  warning: {
48
48
  variant: "outline",
49
- buttonClass: "h-8 text-xs px-2.5 border-amber-500 text-amber-700",
49
+ buttonClass: "h-7 text-xs px-2.5 border-amber-500 text-amber-700",
50
50
  inputClass: "border-amber-500 focus:ring-amber-500"
51
51
  },
52
52
  sm: {
@@ -62,7 +62,7 @@ const J = { class: "relative w-full max-w-sm items-center" }, K = { class: "abso
62
62
  }, i = d(() => {
63
63
  const n = x.styletype || "default";
64
64
  return h[n] ?? h.default;
65
- }), y = d(() => b("justify-between", i.value?.buttonClass, x.class)), g = d(() => b("pl-8 pr-2 py-1.5 focus-visible:ring-0 border-0 border-b rounded-none h-8 text-xs", i.value?.inputClass)), V = (n) => {
65
+ }), y = d(() => b("justify-between", i.value?.buttonClass, x.class)), g = d(() => b("pl-8 pr-2 py-1 focus-visible:ring-0 border-0 border-b rounded-none h-7 text-xs", i.value?.inputClass)), V = (n) => {
66
66
  u("update:modelValue", n), u("change", n);
67
67
  };
68
68
  return (n, r) => (o(), m(t(q), {
@@ -1 +1 @@
1
- {"version":3,"file":"JSearchCombo.vue.js","sources":["../../../../src/components/atoms/JSearchCombo.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { Check, ChevronsUpDown, Search } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport {\n Button,\n Combobox,\n ComboboxAnchor,\n ComboboxEmpty,\n ComboboxGroup,\n ComboboxInput,\n ComboboxItem,\n ComboboxList,\n ComboboxTrigger,\n} from '@/components/shadcn'\n\nexport interface ComboboxOption {\n value: string | number\n label: string\n}\n\ntype StyleType =\n | 'default' // 기본 스타일\n | 'error' // 에러 상태\n | 'success' // 성공 상태\n | 'warning' // 경고 상태\n | 'sm' // 작은 크기\n | 'lg' // 큰 크기\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: ComboboxOption\n options?: ComboboxOption[]\n placeholder?: string\n searchPlaceholder?: string\n emptyText?: string\n disabled?: boolean\n required?: boolean\n name?: string\n id?: string\n multiple?: boolean\n class?: string\n /** 스타일 프리셋 */\n styletype?: StyleType\n }>(),\n {\n options: () => [],\n placeholder: '선택해주세요.',\n searchPlaceholder: '',\n emptyText: '검색 결과가 없습니다.',\n disabled: false,\n required: false,\n multiple: false,\n styletype: 'default',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: ComboboxOption | undefined]\n 'change': [value: ComboboxOption | undefined]\n 'focus': [event: FocusEvent]\n 'blur': [event: FocusEvent]\n}>()\n\n/**\n * styletype -> variant/class 매핑\n */\nconst STYLE_PRESETS: Record<StyleType, { variant: 'default' | 'outline' | 'destructive' | 'secondary' | 'ghost' | 'link', buttonClass?: string, inputClass?: string }> = {\n default: { \n variant: 'outline',\n buttonClass: 'h-8 text-xs px-2.5',\n },\n error: { \n variant: 'outline',\n buttonClass: 'h-8 text-xs px-2.5 border-destructive text-destructive',\n inputClass: 'border-destructive focus:ring-destructive',\n },\n success: { \n variant: 'outline',\n buttonClass: 'h-8 text-xs px-2.5 border-green-500 text-green-700',\n inputClass: 'border-green-500 focus:ring-green-500',\n },\n warning: { \n variant: 'outline',\n buttonClass: 'h-8 text-xs px-2.5 border-amber-500 text-amber-700',\n inputClass: 'border-amber-500 focus:ring-amber-500',\n },\n sm: { \n variant: 'outline',\n buttonClass: 'h-7 text-[11px] px-2',\n inputClass: 'h-7 text-[11px] px-2 py-1',\n },\n lg: { \n variant: 'outline',\n buttonClass: 'h-10 text-sm px-3',\n inputClass: 'h-10 text-sm px-3 py-2',\n },\n}\n\nconst preset = computed(() => {\n const styleKey = props.styletype || 'default'\n return STYLE_PRESETS[styleKey] ?? STYLE_PRESETS.default\n})\n\nconst buttonClass = computed(() => {\n return cn('justify-between', preset.value?.buttonClass, props.class)\n})\n\nconst inputClass = computed(() => {\n return cn('pl-8 pr-2 py-1.5 focus-visible:ring-0 border-0 border-b rounded-none h-8 text-xs', preset.value?.inputClass)\n})\n\nconst handleChange = (value: ComboboxOption | undefined) => {\n emit('update:modelValue', value)\n emit('change', value)\n}\n</script>\n\n<template>\n <Combobox \n :model-value=\"modelValue\" \n by=\"label\" \n :disabled=\"disabled\"\n :required=\"required\"\n :name=\"name\"\n @update:model-value=\"(value) => handleChange(value as ComboboxOption | undefined)\"\n >\n <ComboboxAnchor as-child>\n <ComboboxTrigger as-child>\n <Button \n :id=\"id\"\n :variant=\"preset.variant\" \n :class=\"buttonClass\"\n :disabled=\"disabled\"\n @focus=\"emit('focus', $event as FocusEvent)\"\n @blur=\"emit('blur', $event as FocusEvent)\"\n >\n {{ modelValue?.label ?? placeholder }}\n <ChevronsUpDown class=\"ml-2 h-3.5 w-3.5 shrink-0 opacity-50\" />\n </Button>\n </ComboboxTrigger>\n </ComboboxAnchor>\n\n <ComboboxList>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput :class=\"inputClass\" :placeholder=\"searchPlaceholder\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-2.5\">\n <Search class=\"size-3.5 text-muted-foreground\" />\n </span>\n </div>\n\n <ComboboxEmpty>\n {{ emptyText }}\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"option in options\"\n :key=\"option.value\"\n :value=\"option\"\n >\n {{ option.label }}\n\n <Check v-if=\"modelValue?.value === option.value\" :class=\"cn('ml-auto h-3.5 w-3.5')\" />\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n"],"names":["props","__props","emit","__emit","STYLE_PRESETS","preset","computed","styleKey","buttonClass","cn","inputClass","handleChange","value","_createBlock","_unref","Combobox","_cache","_createVNode","ComboboxAnchor","ComboboxTrigger","Button","$event","_createTextVNode","_toDisplayString","ChevronsUpDown","ComboboxList","_createElementVNode","_hoisted_1","ComboboxInput","_hoisted_2","Search","ComboboxEmpty","ComboboxGroup","_createElementBlock","_Fragment","_renderList","option","ComboboxItem","Check"],"mappings":"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"}
1
+ {"version":3,"file":"JSearchCombo.vue.js","sources":["../../../../src/components/atoms/JSearchCombo.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { Check, ChevronsUpDown, Search } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport {\n Button,\n Combobox,\n ComboboxAnchor,\n ComboboxEmpty,\n ComboboxGroup,\n ComboboxInput,\n ComboboxItem,\n ComboboxList,\n ComboboxTrigger,\n} from '@/components/shadcn'\n\nexport interface ComboboxOption {\n value: string | number\n label: string\n}\n\ntype StyleType =\n | 'default' // 기본 스타일\n | 'error' // 에러 상태\n | 'success' // 성공 상태\n | 'warning' // 경고 상태\n | 'sm' // 작은 크기\n | 'lg' // 큰 크기\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: ComboboxOption\n options?: ComboboxOption[]\n placeholder?: string\n searchPlaceholder?: string\n emptyText?: string\n disabled?: boolean\n required?: boolean\n name?: string\n id?: string\n multiple?: boolean\n class?: string\n /** 스타일 프리셋 */\n styletype?: StyleType\n }>(),\n {\n options: () => [],\n placeholder: '선택해주세요.',\n searchPlaceholder: '',\n emptyText: '검색 결과가 없습니다.',\n disabled: false,\n required: false,\n multiple: false,\n styletype: 'default',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: ComboboxOption | undefined]\n 'change': [value: ComboboxOption | undefined]\n 'focus': [event: FocusEvent]\n 'blur': [event: FocusEvent]\n}>()\n\n/**\n * styletype -> variant/class 매핑\n */\nconst STYLE_PRESETS: Record<StyleType, { variant: 'default' | 'outline' | 'destructive' | 'secondary' | 'ghost' | 'link', buttonClass?: string, inputClass?: string }> = {\n default: { \n variant: 'outline',\n buttonClass: 'h-7 text-xs px-2.5',\n },\n error: { \n variant: 'outline',\n buttonClass: 'h-7 text-xs px-2.5 border-destructive text-destructive',\n inputClass: 'border-destructive focus:ring-destructive',\n },\n success: { \n variant: 'outline',\n buttonClass: 'h-7 text-xs px-2.5 border-green-500 text-green-700',\n inputClass: 'border-green-500 focus:ring-green-500',\n },\n warning: { \n variant: 'outline',\n buttonClass: 'h-7 text-xs px-2.5 border-amber-500 text-amber-700',\n inputClass: 'border-amber-500 focus:ring-amber-500',\n },\n sm: { \n variant: 'outline',\n buttonClass: 'h-7 text-[11px] px-2',\n inputClass: 'h-7 text-[11px] px-2 py-1',\n },\n lg: { \n variant: 'outline',\n buttonClass: 'h-10 text-sm px-3',\n inputClass: 'h-10 text-sm px-3 py-2',\n },\n}\n\nconst preset = computed(() => {\n const styleKey = props.styletype || 'default'\n return STYLE_PRESETS[styleKey] ?? STYLE_PRESETS.default\n})\n\nconst buttonClass = computed(() => {\n return cn('justify-between', preset.value?.buttonClass, props.class)\n})\n\nconst inputClass = computed(() => {\n return cn('pl-8 pr-2 py-1 focus-visible:ring-0 border-0 border-b rounded-none h-7 text-xs', preset.value?.inputClass)\n})\n\nconst handleChange = (value: ComboboxOption | undefined) => {\n emit('update:modelValue', value)\n emit('change', value)\n}\n</script>\n\n<template>\n <Combobox \n :model-value=\"modelValue\" \n by=\"label\" \n :disabled=\"disabled\"\n :required=\"required\"\n :name=\"name\"\n @update:model-value=\"(value) => handleChange(value as ComboboxOption | undefined)\"\n >\n <ComboboxAnchor as-child>\n <ComboboxTrigger as-child>\n <Button \n :id=\"id\"\n :variant=\"preset.variant\" \n :class=\"buttonClass\"\n :disabled=\"disabled\"\n @focus=\"emit('focus', $event as FocusEvent)\"\n @blur=\"emit('blur', $event as FocusEvent)\"\n >\n {{ modelValue?.label ?? placeholder }}\n <ChevronsUpDown class=\"ml-2 h-3.5 w-3.5 shrink-0 opacity-50\" />\n </Button>\n </ComboboxTrigger>\n </ComboboxAnchor>\n\n <ComboboxList>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput :class=\"inputClass\" :placeholder=\"searchPlaceholder\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-2.5\">\n <Search class=\"size-3.5 text-muted-foreground\" />\n </span>\n </div>\n\n <ComboboxEmpty>\n {{ emptyText }}\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"option in options\"\n :key=\"option.value\"\n :value=\"option\"\n >\n {{ option.label }}\n\n <Check v-if=\"modelValue?.value === option.value\" :class=\"cn('ml-auto h-3.5 w-3.5')\" />\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n"],"names":["props","__props","emit","__emit","STYLE_PRESETS","preset","computed","styleKey","buttonClass","cn","inputClass","handleChange","value","_createBlock","_unref","Combobox","_cache","_createVNode","ComboboxAnchor","ComboboxTrigger","Button","$event","_createTextVNode","_toDisplayString","ChevronsUpDown","ComboboxList","_createElementVNode","_hoisted_1","ComboboxInput","_hoisted_2","Search","ComboboxEmpty","ComboboxGroup","_createElementBlock","_Fragment","_renderList","option","ComboboxItem","Check"],"mappings":"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"}
@@ -3,5 +3,5 @@
3
3
  for (const [t_key, t_val] of t_opts)
4
4
  t_merged[t_key] = t_val;
5
5
  return t_merged;
6
- };,u=t(e.default,[["__scopeId","data-v-50b8cb31"]]);exports.default=u;
6
+ };,u=t(e.default,[["__scopeId","data-v-9c1f0de8"]]);exports.default=u;
7
7
  //# sourceMappingURL=JSplitter.vue.cjs.map
@@ -6,8 +6,8 @@ const o = (o_comp, o_opts) => {
6
6
  o_merged[o_key] = o_val;
7
7
  return o_merged;
8
8
  };
9
- const _ = /* @__PURE__ */ o(t, [["__scopeId", "data-v-50b8cb31"]]);
9
+ const f = /* @__PURE__ */ o(t, [["__scopeId", "data-v-9c1f0de8"]]);
10
10
  export {
11
- _ as default
11
+ f as default
12
12
  };
13
13
  //# sourceMappingURL=JSplitter.vue.js.map
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),s=require("../shadcn/resizable/ResizableHandle.vue.cjs"),r=require("../shadcn/resizable/ResizablePanelGroup.vue.cjs"),l=require("reka-ui"),d=e.defineComponent({__name:"JSplitter",props:{direction:{default:"horizontal"},defaultSize:{default:40},minSize:{default:20},maxSize:{},secondMinSize:{},secondMaxSize:{},withHandle:{type:Boolean,default:!1},gap:{default:4},class:{}},setup(t){const i=t,n=e.computed(()=>100-i.defaultSize);return(a,u)=>(e.openBlock(),e.createBlock(e.unref(r.default),{direction:t.direction,class:e.normalizeClass(t.class)},{default:e.withCtx(()=>[e.createVNode(e.unref(l.SplitterPanel),{"default-size":t.defaultSize,"min-size":t.minSize,"max-size":t.maxSize,class:e.normalizeClass(t.gap>0?t.direction==="horizontal"?"pr-[calc(var(--gap)/2)]":"pb-[calc(var(--gap)/2)]":""),style:e.normalizeStyle(t.gap>0?{"--gap":`${t.gap}px`}:{})},{default:e.withCtx(()=>[e.renderSlot(a.$slots,"first",{},void 0,!0),e.renderSlot(a.$slots,"left",{},void 0,!0),e.renderSlot(a.$slots,"top",{},void 0,!0)]),_:3},8,["default-size","min-size","max-size","class","style"]),e.createVNode(e.unref(s.default),{"with-handle":t.withHandle},null,8,["with-handle"]),e.createVNode(e.unref(l.SplitterPanel),{"default-size":n.value,"min-size":t.secondMinSize,"max-size":t.secondMaxSize,class:e.normalizeClass(t.gap>0?t.direction==="horizontal"?"pl-[calc(var(--gap)/2)]":"pt-[calc(var(--gap)/2)]":""),style:e.normalizeStyle(t.gap>0?{"--gap":`${t.gap}px`}:{})},{default:e.withCtx(()=>[e.renderSlot(a.$slots,"second",{},void 0,!0),e.renderSlot(a.$slots,"right",{},void 0,!0),e.renderSlot(a.$slots,"bottom",{},void 0,!0)]),_:3},8,["default-size","min-size","max-size","class","style"])]),_:3},8,["direction","class"]))}});exports.default=d;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),s=require("../../lib/utils.cjs"),r=require("../shadcn/resizable/ResizableHandle.vue.cjs"),u=require("../shadcn/resizable/ResizablePanelGroup.vue.cjs"),l=require("reka-ui"),o=e.defineComponent({__name:"JSplitter",props:{direction:{default:"horizontal"},defaultSize:{default:40},minSize:{default:20},maxSize:{},secondMinSize:{},secondMaxSize:{},withHandle:{type:Boolean,default:!1},gap:{default:2},class:{}},setup(t){const i=t,n=e.computed(()=>100-i.defaultSize);return(a,d)=>(e.openBlock(),e.createBlock(e.unref(u.default),{direction:t.direction,class:e.normalizeClass(e.unref(s.cn)("jsplitter-group min-h-0 min-w-0",a.$props.class))},{default:e.withCtx(()=>[e.createVNode(e.unref(l.SplitterPanel),{"default-size":t.defaultSize,"min-size":t.minSize,"max-size":t.maxSize,class:e.normalizeClass(t.gap>0?t.direction==="horizontal"?"pr-[calc(var(--gap)/2)]":"pb-[calc(var(--gap)/2)]":""),style:e.normalizeStyle(t.gap>0?{"--gap":`${t.gap}px`}:{})},{default:e.withCtx(()=>[e.renderSlot(a.$slots,"first",{},void 0,!0),e.renderSlot(a.$slots,"left",{},void 0,!0),e.renderSlot(a.$slots,"top",{},void 0,!0)]),_:3},8,["default-size","min-size","max-size","class","style"]),e.createVNode(e.unref(r.default),{"with-handle":t.withHandle,class:"jsplitter-handle"},null,8,["with-handle"]),e.createVNode(e.unref(l.SplitterPanel),{"default-size":n.value,"min-size":t.secondMinSize,"max-size":t.secondMaxSize,class:e.normalizeClass(t.gap>0?t.direction==="horizontal"?"pl-[calc(var(--gap)/2)]":"pt-[calc(var(--gap)/2)]":""),style:e.normalizeStyle(t.gap>0?{"--gap":`${t.gap}px`}:{})},{default:e.withCtx(()=>[e.renderSlot(a.$slots,"second",{},void 0,!0),e.renderSlot(a.$slots,"right",{},void 0,!0),e.renderSlot(a.$slots,"bottom",{},void 0,!0)]),_:3},8,["default-size","min-size","max-size","class","style"])]),_:3},8,["direction","class"]))}});exports.default=o;
2
2
  //# sourceMappingURL=JSplitter.vue2.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"JSplitter.vue2.cjs","sources":["../../../../src/components/atoms/JSplitter.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport {\n ResizablePanelGroup,\n ResizablePanel,\n ResizableHandle,\n} from '@/components/shadcn/resizable'\n\ntype Orientation = 'horizontal' | 'vertical'\n\nconst props = withDefaults(\n defineProps<{\n /** 분할 방향 (horizontal: 좌우, vertical: 상하) */\n direction?: Orientation\n /** 첫 번째 패널의 기본 크기 (%) */\n defaultSize?: number\n /** 첫 번째 패널의 최소 크기 (%) */\n minSize?: number\n /** 첫 번째 패널의 최대 크기 (%) */\n maxSize?: number\n /** 두 번째 패널의 최소 크기 (%) */\n secondMinSize?: number\n /** 두 번째 패널의 최대 크기 (%) */\n secondMaxSize?: number\n /** ResizableHandle에 grip 아이콘 표시 여부 */\n withHandle?: boolean\n /** 패널 간 여백 (px) */\n gap?: number\n /** 추가 CSS 클래스 */\n class?: string\n }>(),\n {\n direction: 'horizontal',\n defaultSize: 40,\n minSize: 20,\n withHandle: false,\n gap: 4,\n },\n)\n\n// 두 번째 패널의 기본 크기 계산\nconst secondDefaultSize = computed(() => 100 - props.defaultSize)\n</script>\n\n<template>\n <ResizablePanelGroup :direction=\"direction\" :class=\"class\">\n <!-- 첫 번째 패널 (좌측/상단) -->\n <ResizablePanel \n :default-size=\"defaultSize\" \n :min-size=\"minSize\" \n :max-size=\"maxSize\"\n :class=\"gap > 0 ? (direction === 'horizontal' ? 'pr-[calc(var(--gap)/2)]' : 'pb-[calc(var(--gap)/2)]') : ''\"\n :style=\"gap > 0 ? { '--gap': `${gap}px` } : {}\"\n >\n <slot name=\"first\" />\n <!-- direction=\"horizontal\"일 때 left, vertical일 때 top으로도 사용 가능 -->\n <slot name=\"left\" />\n <slot name=\"top\" />\n </ResizablePanel>\n\n <!-- 크기 조정 핸들 -->\n <ResizableHandle :with-handle=\"withHandle\" />\n\n <!-- 두 번째 패널 (우측/하단) -->\n <ResizablePanel\n :default-size=\"secondDefaultSize\"\n :min-size=\"secondMinSize\"\n :max-size=\"secondMaxSize\"\n :class=\"gap > 0 ? (direction === 'horizontal' ? 'pl-[calc(var(--gap)/2)]' : 'pt-[calc(var(--gap)/2)]') : ''\"\n :style=\"gap > 0 ? { '--gap': `${gap}px` } : {}\"\n >\n <slot name=\"second\" />\n <!-- direction=\"horizontal\"일 때 right, vertical일 때 bottom으로도 사용 가능 -->\n <slot name=\"right\" />\n <slot name=\"bottom\" />\n </ResizablePanel>\n </ResizablePanelGroup>\n</template>\n\n<style scoped>\n/* ========================================\n 패턴 5: Splitter Handle 스타일\n ======================================== */\n\n:deep([data-panel-resize-handle-id]) {\n background: transparent;\n transition: background-color 0.15s ease;\n position: relative;\n user-select: none;\n touch-action: none;\n}\n\n:deep([data-panel-resize-handle-id]::before) {\n content: '';\n position: absolute;\n background: hsl(var(--border));\n border-radius: 9999px;\n}\n\n:deep([data-panel-resize-handle-id]:hover) {\n background: hsl(var(--primary) / 0.06);\n}\n\n:deep([data-panel-resize-handle-id]:hover::before) {\n background: hsl(var(--primary) / 0.55);\n}\n\n:deep([data-panel-resize-handle-id]:active) {\n background: hsl(var(--primary) / 0.1);\n}\n\n:deep([data-panel-resize-handle-id]:active::before) {\n background: hsl(var(--primary));\n}\n\n:deep([data-panel-resize-handle-id][data-resize-handle-orientation=\"horizontal\"]) {\n cursor: col-resize;\n width: 6px;\n}\n\n:deep([data-panel-resize-handle-id][data-resize-handle-orientation=\"horizontal\"]::before) {\n top: 6px;\n bottom: 6px;\n left: 50%;\n width: 1px;\n transform: translateX(-50%);\n}\n\n:deep([data-panel-resize-handle-id][data-resize-handle-orientation=\"vertical\"]) {\n cursor: row-resize;\n height: 6px;\n}\n\n:deep([data-panel-resize-handle-id][data-resize-handle-orientation=\"vertical\"]::before) {\n left: 6px;\n right: 6px;\n top: 50%;\n height: 1px;\n transform: translateY(-50%);\n}\n</style>\n"],"names":["props","__props","secondDefaultSize","computed","_createBlock","_unref","ResizablePanelGroup","_createVNode","ResizablePanel","_normalizeClass","_normalizeStyle","_renderSlot","_ctx","ResizableHandle"],"mappings":"igBAUA,MAAMA,EAAQC,EA+BRC,EAAoBC,EAAAA,SAAS,IAAM,IAAMH,EAAM,WAAW,8BAI9DI,EAAAA,YA+BsBC,EAAAA,MAAAC,EAAAA,OAAA,EAAA,CA/BA,UAAWL,EAAA,UAAY,uBAAOA,EAAA,KAAK,CAAA,qBAEvD,IAWiB,CAXjBM,cAWiBF,EAAAA,MAAAG,EAAAA,aAAA,EAAA,CAVd,eAAcP,EAAA,YACd,WAAUA,EAAA,QACV,WAAUA,EAAA,QACV,MAAKQ,EAAAA,eAAER,EAAA,IAAG,EAAQA,EAAA,YAAS,aAAA,0BAAA,0BAAA,EAAA,EAC3B,MAAKS,EAAAA,eAAET,EAAA,IAAG,EAAA,CAAA,QAAA,GAAqBA,EAAA,GAAG,IAAA,EAAA,CAAA,CAAA,CAAA,qBAEnC,IAAqB,CAArBU,EAAAA,WAAqBC,EAAA,OAAA,QAAA,CAAA,EAAA,OAAA,EAAA,EAErBD,EAAAA,WAAoBC,EAAA,OAAA,OAAA,CAAA,EAAA,OAAA,EAAA,EACpBD,EAAAA,WAAmBC,EAAA,OAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAA,kEAIrBL,EAAAA,YAA6CF,EAAAA,MAAAQ,SAAA,EAAA,CAA3B,cAAaZ,EAAA,UAAA,EAAU,KAAA,EAAA,CAAA,aAAA,CAAA,EAGzCM,cAWiBF,EAAAA,MAAAG,EAAAA,aAAA,EAAA,CAVd,eAAcN,EAAA,MACd,WAAUD,EAAA,cACV,WAAUA,EAAA,cACV,MAAKQ,EAAAA,eAAER,EAAA,IAAG,EAAQA,EAAA,YAAS,aAAA,0BAAA,0BAAA,EAAA,EAC3B,MAAKS,EAAAA,eAAET,EAAA,IAAG,EAAA,CAAA,QAAA,GAAqBA,EAAA,GAAG,IAAA,EAAA,CAAA,CAAA,CAAA,qBAEnC,IAAsB,CAAtBU,EAAAA,WAAsBC,EAAA,OAAA,SAAA,CAAA,EAAA,OAAA,EAAA,EAEtBD,EAAAA,WAAqBC,EAAA,OAAA,QAAA,CAAA,EAAA,OAAA,EAAA,EACrBD,EAAAA,WAAsBC,EAAA,OAAA,SAAA,CAAA,EAAA,OAAA,EAAA,CAAA"}
1
+ {"version":3,"file":"JSplitter.vue2.cjs","sources":["../../../../src/components/atoms/JSplitter.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { cn } from '@/lib/utils'\nimport {\n ResizablePanelGroup,\n ResizablePanel,\n ResizableHandle,\n} from '@/components/shadcn/resizable'\n\ntype Orientation = 'horizontal' | 'vertical'\n\nconst props = withDefaults(\n defineProps<{\n /** 분할 방향 (horizontal: 좌우, vertical: 상하) */\n direction?: Orientation\n /** 첫 번째 패널의 기본 크기 (%) */\n defaultSize?: number\n /** 첫 번째 패널의 최소 크기 (%) */\n minSize?: number\n /** 첫 번째 패널의 최대 크기 (%) */\n maxSize?: number\n /** 두 번째 패널의 최소 크기 (%) */\n secondMinSize?: number\n /** 두 번째 패널의 최대 크기 (%) */\n secondMaxSize?: number\n /** ResizableHandle에 grip 아이콘 표시 여부 */\n withHandle?: boolean\n /** 패널 간 여백 (px) */\n gap?: number\n /** 추가 CSS 클래스 */\n class?: string\n }>(),\n {\n direction: 'horizontal',\n defaultSize: 40,\n minSize: 20,\n withHandle: false,\n gap: 2,\n },\n)\n\n// 두 번째 패널의 기본 크기 계산\nconst secondDefaultSize = computed(() => 100 - props.defaultSize)\n</script>\n\n<template>\n <ResizablePanelGroup :direction=\"direction\" :class=\"cn('jsplitter-group min-h-0 min-w-0', $props.class)\">\n <!-- 첫 번째 패널 (좌측/상단) -->\n <ResizablePanel \n :default-size=\"defaultSize\" \n :min-size=\"minSize\" \n :max-size=\"maxSize\"\n :class=\"gap > 0 ? (direction === 'horizontal' ? 'pr-[calc(var(--gap)/2)]' : 'pb-[calc(var(--gap)/2)]') : ''\"\n :style=\"gap > 0 ? { '--gap': `${gap}px` } : {}\"\n >\n <slot name=\"first\" />\n <!-- direction=\"horizontal\"일 때 left, vertical일 때 top으로도 사용 가능 -->\n <slot name=\"left\" />\n <slot name=\"top\" />\n </ResizablePanel>\n\n <!-- 크기 조정 핸들 -->\n <ResizableHandle :with-handle=\"withHandle\" class=\"jsplitter-handle\" />\n\n <!-- 두 번째 패널 (우측/하단) -->\n <ResizablePanel\n :default-size=\"secondDefaultSize\"\n :min-size=\"secondMinSize\"\n :max-size=\"secondMaxSize\"\n :class=\"gap > 0 ? (direction === 'horizontal' ? 'pl-[calc(var(--gap)/2)]' : 'pt-[calc(var(--gap)/2)]') : ''\"\n :style=\"gap > 0 ? { '--gap': `${gap}px` } : {}\"\n >\n <slot name=\"second\" />\n <!-- direction=\"horizontal\"일 때 right, vertical일 때 bottom으로도 사용 가능 -->\n <slot name=\"right\" />\n <slot name=\"bottom\" />\n </ResizablePanel>\n </ResizablePanelGroup>\n</template>\n\n<style scoped>\n/* 패널 콘텐츠가 최소 크기 이하로 줄 때 overflow 깨짐 방지 */\n:deep(.jsplitter-group [data-panel]) {\n min-width: 0;\n min-height: 0;\n}\n\n/* splitter: 시각선 1px + 드래그 히트영역 8px */\n:deep(.jsplitter-handle[data-orientation=\"horizontal\"]) {\n width: 8px !important;\n background: transparent !important;\n z-index: 3;\n}\n\n:deep(.jsplitter-handle[data-orientation=\"vertical\"]) {\n height: 8px !important;\n background: transparent !important;\n z-index: 3;\n}\n\n:deep(.jsplitter-handle::after) {\n background: hsl(var(--border) / 0.75) !important;\n}\n\n:deep(.jsplitter-handle:hover) {\n background: hsl(var(--primary) / 0.05) !important;\n}\n\n:deep(.jsplitter-handle:hover::after),\n:deep(.jsplitter-handle:active::after),\n:deep(.jsplitter-handle[data-state=\"drag\"]::after) {\n background: hsl(var(--primary) / 0.85) !important;\n}\n</style>\n"],"names":["props","__props","secondDefaultSize","computed","_createBlock","_unref","ResizablePanelGroup","_normalizeClass","cn","$props","_createVNode","ResizablePanel","_normalizeStyle","_renderSlot","_ctx","ResizableHandle"],"mappings":"kiBAWA,MAAMA,EAAQC,EA+BRC,EAAoBC,EAAAA,SAAS,IAAM,IAAMH,EAAM,WAAW,8BAI9DI,EAAAA,YA+BsBC,EAAAA,MAAAC,EAAAA,OAAA,EAAA,CA/BA,UAAWL,EAAA,UAAY,MAAKM,EAAAA,eAAEF,EAAAA,MAAAG,EAAAA,EAAA,EAAE,kCAAoCC,EAAAA,OAAO,KAAK,CAAA,CAAA,qBAEpG,IAWiB,CAXjBC,cAWiBL,EAAAA,MAAAM,EAAAA,aAAA,EAAA,CAVd,eAAcV,EAAA,YACd,WAAUA,EAAA,QACV,WAAUA,EAAA,QACV,MAAKM,EAAAA,eAAEN,EAAA,IAAG,EAAQA,EAAA,YAAS,aAAA,0BAAA,0BAAA,EAAA,EAC3B,MAAKW,EAAAA,eAAEX,EAAA,IAAG,EAAA,CAAA,QAAA,GAAqBA,EAAA,GAAG,IAAA,EAAA,CAAA,CAAA,CAAA,qBAEnC,IAAqB,CAArBY,EAAAA,WAAqBC,EAAA,OAAA,QAAA,CAAA,EAAA,OAAA,EAAA,EAErBD,EAAAA,WAAoBC,EAAA,OAAA,OAAA,CAAA,EAAA,OAAA,EAAA,EACpBD,EAAAA,WAAmBC,EAAA,OAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAA,kEAIrBJ,cAAsEL,EAAAA,MAAAU,EAAAA,OAAA,EAAA,CAApD,cAAad,EAAA,WAAY,MAAM,kBAAA,0BAGjDS,cAWiBL,EAAAA,MAAAM,EAAAA,aAAA,EAAA,CAVd,eAAcT,EAAA,MACd,WAAUD,EAAA,cACV,WAAUA,EAAA,cACV,MAAKM,EAAAA,eAAEN,EAAA,IAAG,EAAQA,EAAA,YAAS,aAAA,0BAAA,0BAAA,EAAA,EAC3B,MAAKW,EAAAA,eAAEX,EAAA,IAAG,EAAA,CAAA,QAAA,GAAqBA,EAAA,GAAG,IAAA,EAAA,CAAA,CAAA,CAAA,qBAEnC,IAAsB,CAAtBY,EAAAA,WAAsBC,EAAA,OAAA,SAAA,CAAA,EAAA,OAAA,EAAA,EAEtBD,EAAAA,WAAqBC,EAAA,OAAA,QAAA,CAAA,EAAA,OAAA,EAAA,EACrBD,EAAAA,WAAsBC,EAAA,OAAA,SAAA,CAAA,EAAA,OAAA,EAAA,CAAA"}
@@ -1,8 +1,9 @@
1
- import { defineComponent as u, computed as f, createBlock as m, openBlock as z, unref as t, normalizeClass as l, withCtx as s, createVNode as n, normalizeStyle as o, renderSlot as i } from "vue";
2
- import S from "../shadcn/resizable/ResizableHandle.vue.js";
3
- import g from "../shadcn/resizable/ResizablePanelGroup.vue.js";
1
+ import { defineComponent as m, computed as u, createBlock as f, openBlock as z, unref as t, normalizeClass as l, withCtx as s, createVNode as n, normalizeStyle as o, renderSlot as i } from "vue";
2
+ import { cn as S } from "../../lib/utils.js";
3
+ import g from "../shadcn/resizable/ResizableHandle.vue.js";
4
+ import h from "../shadcn/resizable/ResizablePanelGroup.vue.js";
4
5
  import { SplitterPanel as d } from "reka-ui";
5
- const w = /* @__PURE__ */ u({
6
+ const M = /* @__PURE__ */ m({
6
7
  __name: "JSplitter",
7
8
  props: {
8
9
  direction: { default: "horizontal" },
@@ -12,14 +13,14 @@ const w = /* @__PURE__ */ u({
12
13
  secondMinSize: {},
13
14
  secondMaxSize: {},
14
15
  withHandle: { type: Boolean, default: !1 },
15
- gap: { default: 4 },
16
+ gap: { default: 2 },
16
17
  class: {}
17
18
  },
18
19
  setup(e) {
19
- const c = e, r = f(() => 100 - c.defaultSize);
20
- return (a, h) => (z(), m(t(g), {
20
+ const c = e, r = u(() => 100 - c.defaultSize);
21
+ return (a, p) => (z(), f(t(h), {
21
22
  direction: e.direction,
22
- class: l(e.class)
23
+ class: l(t(S)("jsplitter-group min-h-0 min-w-0", a.$props.class))
23
24
  }, {
24
25
  default: s(() => [
25
26
  n(t(d), {
@@ -36,7 +37,10 @@ const w = /* @__PURE__ */ u({
36
37
  ]),
37
38
  _: 3
38
39
  }, 8, ["default-size", "min-size", "max-size", "class", "style"]),
39
- n(t(S), { "with-handle": e.withHandle }, null, 8, ["with-handle"]),
40
+ n(t(g), {
41
+ "with-handle": e.withHandle,
42
+ class: "jsplitter-handle"
43
+ }, null, 8, ["with-handle"]),
40
44
  n(t(d), {
41
45
  "default-size": r.value,
42
46
  "min-size": e.secondMinSize,
@@ -57,6 +61,6 @@ const w = /* @__PURE__ */ u({
57
61
  }
58
62
  });
59
63
  export {
60
- w as default
64
+ M as default
61
65
  };
62
66
  //# sourceMappingURL=JSplitter.vue2.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"JSplitter.vue2.js","sources":["../../../../src/components/atoms/JSplitter.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport {\n ResizablePanelGroup,\n ResizablePanel,\n ResizableHandle,\n} from '@/components/shadcn/resizable'\n\ntype Orientation = 'horizontal' | 'vertical'\n\nconst props = withDefaults(\n defineProps<{\n /** 분할 방향 (horizontal: 좌우, vertical: 상하) */\n direction?: Orientation\n /** 첫 번째 패널의 기본 크기 (%) */\n defaultSize?: number\n /** 첫 번째 패널의 최소 크기 (%) */\n minSize?: number\n /** 첫 번째 패널의 최대 크기 (%) */\n maxSize?: number\n /** 두 번째 패널의 최소 크기 (%) */\n secondMinSize?: number\n /** 두 번째 패널의 최대 크기 (%) */\n secondMaxSize?: number\n /** ResizableHandle에 grip 아이콘 표시 여부 */\n withHandle?: boolean\n /** 패널 간 여백 (px) */\n gap?: number\n /** 추가 CSS 클래스 */\n class?: string\n }>(),\n {\n direction: 'horizontal',\n defaultSize: 40,\n minSize: 20,\n withHandle: false,\n gap: 4,\n },\n)\n\n// 두 번째 패널의 기본 크기 계산\nconst secondDefaultSize = computed(() => 100 - props.defaultSize)\n</script>\n\n<template>\n <ResizablePanelGroup :direction=\"direction\" :class=\"class\">\n <!-- 첫 번째 패널 (좌측/상단) -->\n <ResizablePanel \n :default-size=\"defaultSize\" \n :min-size=\"minSize\" \n :max-size=\"maxSize\"\n :class=\"gap > 0 ? (direction === 'horizontal' ? 'pr-[calc(var(--gap)/2)]' : 'pb-[calc(var(--gap)/2)]') : ''\"\n :style=\"gap > 0 ? { '--gap': `${gap}px` } : {}\"\n >\n <slot name=\"first\" />\n <!-- direction=\"horizontal\"일 때 left, vertical일 때 top으로도 사용 가능 -->\n <slot name=\"left\" />\n <slot name=\"top\" />\n </ResizablePanel>\n\n <!-- 크기 조정 핸들 -->\n <ResizableHandle :with-handle=\"withHandle\" />\n\n <!-- 두 번째 패널 (우측/하단) -->\n <ResizablePanel\n :default-size=\"secondDefaultSize\"\n :min-size=\"secondMinSize\"\n :max-size=\"secondMaxSize\"\n :class=\"gap > 0 ? (direction === 'horizontal' ? 'pl-[calc(var(--gap)/2)]' : 'pt-[calc(var(--gap)/2)]') : ''\"\n :style=\"gap > 0 ? { '--gap': `${gap}px` } : {}\"\n >\n <slot name=\"second\" />\n <!-- direction=\"horizontal\"일 때 right, vertical일 때 bottom으로도 사용 가능 -->\n <slot name=\"right\" />\n <slot name=\"bottom\" />\n </ResizablePanel>\n </ResizablePanelGroup>\n</template>\n\n<style scoped>\n/* ========================================\n 패턴 5: Splitter Handle 스타일\n ======================================== */\n\n:deep([data-panel-resize-handle-id]) {\n background: transparent;\n transition: background-color 0.15s ease;\n position: relative;\n user-select: none;\n touch-action: none;\n}\n\n:deep([data-panel-resize-handle-id]::before) {\n content: '';\n position: absolute;\n background: hsl(var(--border));\n border-radius: 9999px;\n}\n\n:deep([data-panel-resize-handle-id]:hover) {\n background: hsl(var(--primary) / 0.06);\n}\n\n:deep([data-panel-resize-handle-id]:hover::before) {\n background: hsl(var(--primary) / 0.55);\n}\n\n:deep([data-panel-resize-handle-id]:active) {\n background: hsl(var(--primary) / 0.1);\n}\n\n:deep([data-panel-resize-handle-id]:active::before) {\n background: hsl(var(--primary));\n}\n\n:deep([data-panel-resize-handle-id][data-resize-handle-orientation=\"horizontal\"]) {\n cursor: col-resize;\n width: 6px;\n}\n\n:deep([data-panel-resize-handle-id][data-resize-handle-orientation=\"horizontal\"]::before) {\n top: 6px;\n bottom: 6px;\n left: 50%;\n width: 1px;\n transform: translateX(-50%);\n}\n\n:deep([data-panel-resize-handle-id][data-resize-handle-orientation=\"vertical\"]) {\n cursor: row-resize;\n height: 6px;\n}\n\n:deep([data-panel-resize-handle-id][data-resize-handle-orientation=\"vertical\"]::before) {\n left: 6px;\n right: 6px;\n top: 50%;\n height: 1px;\n transform: translateY(-50%);\n}\n</style>\n"],"names":["props","__props","secondDefaultSize","computed","_createBlock","_unref","ResizablePanelGroup","_createVNode","ResizablePanel","_normalizeClass","_normalizeStyle","_renderSlot","_ctx","ResizableHandle"],"mappings":";;;;;;;;;;;;;;;;;;AAUA,UAAMA,IAAQC,GA+BRC,IAAoBC,EAAS,MAAM,MAAMH,EAAM,WAAW;2BAI9DI,EA+BsBC,EAAAC,CAAA,GAAA;AAAA,MA/BA,WAAWL,EAAA;AAAA,MAAY,SAAOA,EAAA,KAAK;AAAA,IAAA;iBAEvD,MAWiB;AAAA,QAXjBM,EAWiBF,EAAAG,CAAA,GAAA;AAAA,UAVd,gBAAcP,EAAA;AAAA,UACd,YAAUA,EAAA;AAAA,UACV,YAAUA,EAAA;AAAA,UACV,OAAKQ,EAAER,EAAA,MAAG,IAAQA,EAAA,cAAS,eAAA,4BAAA,4BAAA,EAAA;AAAA,UAC3B,OAAKS,EAAET,EAAA,MAAG,IAAA,EAAA,SAAA,GAAqBA,EAAA,GAAG,KAAA,IAAA,CAAA,CAAA;AAAA,QAAA;qBAEnC,MAAqB;AAAA,YAArBU,EAAqBC,EAAA,QAAA,SAAA,CAAA,GAAA,QAAA,EAAA;AAAA,YAErBD,EAAoBC,EAAA,QAAA,QAAA,CAAA,GAAA,QAAA,EAAA;AAAA,YACpBD,EAAmBC,EAAA,QAAA,OAAA,CAAA,GAAA,QAAA,EAAA;AAAA,UAAA;;;QAIrBL,EAA6CF,EAAAQ,CAAA,GAAA,EAA3B,eAAaZ,EAAA,WAAA,GAAU,MAAA,GAAA,CAAA,aAAA,CAAA;AAAA,QAGzCM,EAWiBF,EAAAG,CAAA,GAAA;AAAA,UAVd,gBAAcN,EAAA;AAAA,UACd,YAAUD,EAAA;AAAA,UACV,YAAUA,EAAA;AAAA,UACV,OAAKQ,EAAER,EAAA,MAAG,IAAQA,EAAA,cAAS,eAAA,4BAAA,4BAAA,EAAA;AAAA,UAC3B,OAAKS,EAAET,EAAA,MAAG,IAAA,EAAA,SAAA,GAAqBA,EAAA,GAAG,KAAA,IAAA,CAAA,CAAA;AAAA,QAAA;qBAEnC,MAAsB;AAAA,YAAtBU,EAAsBC,EAAA,QAAA,UAAA,CAAA,GAAA,QAAA,EAAA;AAAA,YAEtBD,EAAqBC,EAAA,QAAA,SAAA,CAAA,GAAA,QAAA,EAAA;AAAA,YACrBD,EAAsBC,EAAA,QAAA,UAAA,CAAA,GAAA,QAAA,EAAA;AAAA,UAAA;;;;;;;;"}
1
+ {"version":3,"file":"JSplitter.vue2.js","sources":["../../../../src/components/atoms/JSplitter.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { cn } from '@/lib/utils'\nimport {\n ResizablePanelGroup,\n ResizablePanel,\n ResizableHandle,\n} from '@/components/shadcn/resizable'\n\ntype Orientation = 'horizontal' | 'vertical'\n\nconst props = withDefaults(\n defineProps<{\n /** 분할 방향 (horizontal: 좌우, vertical: 상하) */\n direction?: Orientation\n /** 첫 번째 패널의 기본 크기 (%) */\n defaultSize?: number\n /** 첫 번째 패널의 최소 크기 (%) */\n minSize?: number\n /** 첫 번째 패널의 최대 크기 (%) */\n maxSize?: number\n /** 두 번째 패널의 최소 크기 (%) */\n secondMinSize?: number\n /** 두 번째 패널의 최대 크기 (%) */\n secondMaxSize?: number\n /** ResizableHandle에 grip 아이콘 표시 여부 */\n withHandle?: boolean\n /** 패널 간 여백 (px) */\n gap?: number\n /** 추가 CSS 클래스 */\n class?: string\n }>(),\n {\n direction: 'horizontal',\n defaultSize: 40,\n minSize: 20,\n withHandle: false,\n gap: 2,\n },\n)\n\n// 두 번째 패널의 기본 크기 계산\nconst secondDefaultSize = computed(() => 100 - props.defaultSize)\n</script>\n\n<template>\n <ResizablePanelGroup :direction=\"direction\" :class=\"cn('jsplitter-group min-h-0 min-w-0', $props.class)\">\n <!-- 첫 번째 패널 (좌측/상단) -->\n <ResizablePanel \n :default-size=\"defaultSize\" \n :min-size=\"minSize\" \n :max-size=\"maxSize\"\n :class=\"gap > 0 ? (direction === 'horizontal' ? 'pr-[calc(var(--gap)/2)]' : 'pb-[calc(var(--gap)/2)]') : ''\"\n :style=\"gap > 0 ? { '--gap': `${gap}px` } : {}\"\n >\n <slot name=\"first\" />\n <!-- direction=\"horizontal\"일 때 left, vertical일 때 top으로도 사용 가능 -->\n <slot name=\"left\" />\n <slot name=\"top\" />\n </ResizablePanel>\n\n <!-- 크기 조정 핸들 -->\n <ResizableHandle :with-handle=\"withHandle\" class=\"jsplitter-handle\" />\n\n <!-- 두 번째 패널 (우측/하단) -->\n <ResizablePanel\n :default-size=\"secondDefaultSize\"\n :min-size=\"secondMinSize\"\n :max-size=\"secondMaxSize\"\n :class=\"gap > 0 ? (direction === 'horizontal' ? 'pl-[calc(var(--gap)/2)]' : 'pt-[calc(var(--gap)/2)]') : ''\"\n :style=\"gap > 0 ? { '--gap': `${gap}px` } : {}\"\n >\n <slot name=\"second\" />\n <!-- direction=\"horizontal\"일 때 right, vertical일 때 bottom으로도 사용 가능 -->\n <slot name=\"right\" />\n <slot name=\"bottom\" />\n </ResizablePanel>\n </ResizablePanelGroup>\n</template>\n\n<style scoped>\n/* 패널 콘텐츠가 최소 크기 이하로 줄 때 overflow 깨짐 방지 */\n:deep(.jsplitter-group [data-panel]) {\n min-width: 0;\n min-height: 0;\n}\n\n/* splitter: 시각선 1px + 드래그 히트영역 8px */\n:deep(.jsplitter-handle[data-orientation=\"horizontal\"]) {\n width: 8px !important;\n background: transparent !important;\n z-index: 3;\n}\n\n:deep(.jsplitter-handle[data-orientation=\"vertical\"]) {\n height: 8px !important;\n background: transparent !important;\n z-index: 3;\n}\n\n:deep(.jsplitter-handle::after) {\n background: hsl(var(--border) / 0.75) !important;\n}\n\n:deep(.jsplitter-handle:hover) {\n background: hsl(var(--primary) / 0.05) !important;\n}\n\n:deep(.jsplitter-handle:hover::after),\n:deep(.jsplitter-handle:active::after),\n:deep(.jsplitter-handle[data-state=\"drag\"]::after) {\n background: hsl(var(--primary) / 0.85) !important;\n}\n</style>\n"],"names":["props","__props","secondDefaultSize","computed","_createBlock","_unref","ResizablePanelGroup","_normalizeClass","cn","$props","_createVNode","ResizablePanel","_normalizeStyle","_renderSlot","_ctx","ResizableHandle"],"mappings":";;;;;;;;;;;;;;;;;;;AAWA,UAAMA,IAAQC,GA+BRC,IAAoBC,EAAS,MAAM,MAAMH,EAAM,WAAW;2BAI9DI,EA+BsBC,EAAAC,CAAA,GAAA;AAAA,MA/BA,WAAWL,EAAA;AAAA,MAAY,OAAKM,EAAEF,EAAAG,CAAA,EAAE,mCAAoCC,EAAAA,OAAO,KAAK,CAAA;AAAA,IAAA;iBAEpG,MAWiB;AAAA,QAXjBC,EAWiBL,EAAAM,CAAA,GAAA;AAAA,UAVd,gBAAcV,EAAA;AAAA,UACd,YAAUA,EAAA;AAAA,UACV,YAAUA,EAAA;AAAA,UACV,OAAKM,EAAEN,EAAA,MAAG,IAAQA,EAAA,cAAS,eAAA,4BAAA,4BAAA,EAAA;AAAA,UAC3B,OAAKW,EAAEX,EAAA,MAAG,IAAA,EAAA,SAAA,GAAqBA,EAAA,GAAG,KAAA,IAAA,CAAA,CAAA;AAAA,QAAA;qBAEnC,MAAqB;AAAA,YAArBY,EAAqBC,EAAA,QAAA,SAAA,CAAA,GAAA,QAAA,EAAA;AAAA,YAErBD,EAAoBC,EAAA,QAAA,QAAA,CAAA,GAAA,QAAA,EAAA;AAAA,YACpBD,EAAmBC,EAAA,QAAA,OAAA,CAAA,GAAA,QAAA,EAAA;AAAA,UAAA;;;QAIrBJ,EAAsEL,EAAAU,CAAA,GAAA;AAAA,UAApD,eAAad,EAAA;AAAA,UAAY,OAAM;AAAA,QAAA;QAGjDS,EAWiBL,EAAAM,CAAA,GAAA;AAAA,UAVd,gBAAcT,EAAA;AAAA,UACd,YAAUD,EAAA;AAAA,UACV,YAAUA,EAAA;AAAA,UACV,OAAKM,EAAEN,EAAA,MAAG,IAAQA,EAAA,cAAS,eAAA,4BAAA,4BAAA,EAAA;AAAA,UAC3B,OAAKW,EAAEX,EAAA,MAAG,IAAA,EAAA,SAAA,GAAqBA,EAAA,GAAG,KAAA,IAAA,CAAA,CAAA;AAAA,QAAA;qBAEnC,MAAsB;AAAA,YAAtBY,EAAsBC,EAAA,QAAA,UAAA,CAAA,GAAA,QAAA,EAAA;AAAA,YAEtBD,EAAqBC,EAAA,QAAA,SAAA,CAAA,GAAA,QAAA,EAAA;AAAA,YACrBD,EAAsBC,EAAA,QAAA,UAAA,CAAA,GAAA,QAAA,EAAA;AAAA,UAAA;;;;;;;;"}
@@ -3,5 +3,5 @@
3
3
  for (const [t_key, t_val] of t_opts)
4
4
  t_merged[t_key] = t_val;
5
5
  return t_merged;
6
- };,u=t(e.default,[["__scopeId","data-v-1e163e81"]]);exports.default=u;
6
+ };,u=t(e.default,[["__scopeId","data-v-0e5b57c9"]]);exports.default=u;
7
7
  //# sourceMappingURL=JTabs.vue.cjs.map
@@ -6,8 +6,8 @@ const t = (t_comp, t_opts) => {
6
6
  t_merged[t_key] = t_val;
7
7
  return t_merged;
8
8
  };
9
- const m = /* @__PURE__ */ t(o, [["__scopeId", "data-v-1e163e81"]]);
9
+ const p = /* @__PURE__ */ t(o, [["__scopeId", "data-v-0e5b57c9"]]);
10
10
  export {
11
- m as default
11
+ p as default
12
12
  };
13
13
  //# sourceMappingURL=JTabs.vue.js.map
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue");require("../shadcn/index.cjs");const h=require("lucide-vue-next"),i=require("../../lib/utils.cjs"),x=require("../atoms/JIcon.vue.cjs"),y=require("../shadcn/Tabs.vue.cjs"),T=require("../shadcn/TabsList.vue.cjs"),b=require("../shadcn/TabsTrigger.vue.cjs"),B=require("../shadcn/TabsContent.vue.cjs"),S={class:"flex-1 truncate"},E=["aria-label","onClick"],P={class:"flex-1 w-full overflow-auto"},q={key:1,class:"p-4"},V={class:"text-muted-foreground"},z=e.defineComponent({__name:"JTabs",props:{tabs:{},activeTabId:{},class:{},listClass:{},styletype:{default:"default"}},emits:["tabChange","tabClose","update:activeTabId"],setup(f,{emit:v}){const n=f,r=v,u=e.computed(()=>Array.isArray(n.tabs)?n.tabs:[]),a=e.ref(n.activeTabId||(u.value.length>0?u.value[0]?.id:"")||"");let o=!1;e.watch(()=>n.activeTabId,t=>{t!==void 0&&t!==a.value&&(a.value=t)},{immediate:!0}),e.watch(u,t=>{!n.activeTabId&&t.length>0&&!t.find(s=>s.id===a.value)&&t[0]&&(a.value=t[0].id)});const _=t=>{if(o)return;const s=String(t);s!==a.value&&(o=!0,a.value=s,r("update:activeTabId",s),r("tabChange",s),e.nextTick(()=>{o=!1}))},m=t=>{o||t===a.value||(o=!0,a.value=t,r("update:activeTabId",t),r("tabChange",t),e.nextTick(()=>{o=!1}))},C=(t,s)=>{t.stopPropagation(),r("tabClose",s)},g=e.computed(()=>i.cn("flex flex-col w-full h-full",n.class)),d={default:{tabPaddingClass:"px-2 py-0.5",tabTextSizeClass:"text-xs",listPaddingClass:"px-1.5 py-0.5"},minimal:{tabPaddingClass:"px-1.5 py-0.5",tabTextSizeClass:"text-xs",listPaddingClass:"px-1.5 py-0.5"}},c=e.computed(()=>d[n.styletype]??d.default),k=e.computed(()=>i.cn("w-full justify-start",c.value.listPaddingClass,n.listClass));return(t,s)=>(e.openBlock(),e.createBlock(e.unref(y.default),{"model-value":a.value,"onUpdate:modelValue":_,orientation:"horizontal",class:e.normalizeClass(g.value)},{default:e.withCtx(()=>[e.createVNode(e.unref(T.default),{class:e.normalizeClass(k.value)},{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(u.value,l=>(e.openBlock(),e.createBlock(e.unref(b.default),{key:l.id,value:l.id,onClick:p=>m(l.id),class:e.normalizeClass(e.unref(i.cn)("!flex !items-center !gap-2",c.value.tabPaddingClass,c.value.tabTextSizeClass))},{default:e.withCtx(()=>[l.icon?(e.openBlock(),e.createBlock(x.default,{key:0,name:l.icon,size:"sm",class:"flex-shrink-0"},null,8,["name"])):e.createCommentVNode("",!0),e.createElementVNode("span",S,e.toDisplayString(l.label),1),l.closable?(e.openBlock(),e.createElementBlock("button",{key:1,type:"button",class:"flex-shrink-0 h-3.5 w-3.5 rounded-sm hover:bg-destructive/10 hover:text-destructive transition-colors focus:outline-none focus:ring-2 focus:ring-ring flex items-center justify-center","aria-label":`${l.label} 탭 닫기`,onClick:p=>C(p,l.id)},[e.createVNode(e.unref(h.X),{class:"h-2.5 w-2.5"})],8,E)):e.createCommentVNode("",!0)]),_:2},1032,["value","onClick","class"]))),128))]),_:1},8,["class"]),e.createElementVNode("div",P,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(u.value,l=>(e.openBlock(),e.createBlock(e.unref(B.default),{key:`content-${l.id}`,value:l.id,class:"h-full mt-0 data-[state=active]:flex data-[state=active]:flex-col"},{default:e.withCtx(()=>[e.renderSlot(t.$slots,`content-${l.id}`,{tab:l},()=>[l.component?(e.openBlock(),e.createBlock(e.resolveDynamicComponent(l.component),e.mergeProps({key:0,ref_for:!0},l.props||{}),null,16)):(e.openBlock(),e.createElementBlock("div",q,[e.createElementVNode("p",V,e.toDisplayString(l.label)+" 콘텐츠",1)]))],!0)]),_:2},1032,["value"]))),128))])]),_:3},8,["model-value","class"]))}});exports.default=z;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue");require("../shadcn/index.cjs");const h=require("lucide-vue-next"),i=require("../../lib/utils.cjs"),x=require("../atoms/JIcon.vue.cjs"),y=require("../shadcn/Tabs.vue.cjs"),T=require("../shadcn/TabsList.vue.cjs"),b=require("../shadcn/TabsTrigger.vue.cjs"),B=require("../shadcn/TabsContent.vue.cjs"),S={class:"flex-1 truncate"},E=["aria-label","onClick"],P={class:"flex-1 w-full overflow-auto"},q={key:1,class:"p-4"},V={class:"text-muted-foreground"},z=e.defineComponent({__name:"JTabs",props:{tabs:{},activeTabId:{},class:{},listClass:{},styletype:{default:"default"}},emits:["tabChange","tabClose","update:activeTabId"],setup(f,{emit:v}){const n=f,r=v,u=e.computed(()=>Array.isArray(n.tabs)?n.tabs:[]),a=e.ref(n.activeTabId||(u.value.length>0?u.value[0]?.id:"")||"");let o=!1;e.watch(()=>n.activeTabId,t=>{t!==void 0&&t!==a.value&&(a.value=t)},{immediate:!0}),e.watch(u,t=>{!n.activeTabId&&t.length>0&&!t.find(s=>s.id===a.value)&&t[0]&&(a.value=t[0].id)});const _=t=>{if(o)return;const s=String(t);s!==a.value&&(o=!0,a.value=s,r("update:activeTabId",s),r("tabChange",s),e.nextTick(()=>{o=!1}))},m=t=>{o||t===a.value||(o=!0,a.value=t,r("update:activeTabId",t),r("tabChange",t),e.nextTick(()=>{o=!1}))},C=(t,s)=>{t.stopPropagation(),r("tabClose",s)},g=e.computed(()=>i.cn("flex flex-col w-full h-full",n.class)),d={default:{tabPaddingClass:"px-2 py-1",tabTextSizeClass:"text-xs",listPaddingClass:"px-1 py-1"},minimal:{tabPaddingClass:"px-1.5 py-1",tabTextSizeClass:"text-xs",listPaddingClass:"px-1 py-1"}},c=e.computed(()=>d[n.styletype]??d.default),k=e.computed(()=>i.cn("w-full justify-start",c.value.listPaddingClass,n.listClass));return(t,s)=>(e.openBlock(),e.createBlock(e.unref(y.default),{"model-value":a.value,"onUpdate:modelValue":_,orientation:"horizontal",class:e.normalizeClass(g.value)},{default:e.withCtx(()=>[e.createVNode(e.unref(T.default),{class:e.normalizeClass(k.value)},{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(u.value,l=>(e.openBlock(),e.createBlock(e.unref(b.default),{key:l.id,value:l.id,onClick:p=>m(l.id),class:e.normalizeClass(e.unref(i.cn)("!flex !items-center !gap-2",c.value.tabPaddingClass,c.value.tabTextSizeClass))},{default:e.withCtx(()=>[l.icon?(e.openBlock(),e.createBlock(x.default,{key:0,name:l.icon,size:"sm",class:"flex-shrink-0"},null,8,["name"])):e.createCommentVNode("",!0),e.createElementVNode("span",S,e.toDisplayString(l.label),1),l.closable?(e.openBlock(),e.createElementBlock("button",{key:1,type:"button",class:"flex-shrink-0 h-3.5 w-3.5 rounded-sm hover:bg-destructive/10 hover:text-destructive transition-colors focus:outline-none focus:ring-2 focus:ring-ring flex items-center justify-center","aria-label":`${l.label} 탭 닫기`,onClick:p=>C(p,l.id)},[e.createVNode(e.unref(h.X),{class:"h-2.5 w-2.5"})],8,E)):e.createCommentVNode("",!0)]),_:2},1032,["value","onClick","class"]))),128))]),_:1},8,["class"]),e.createElementVNode("div",P,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(u.value,l=>(e.openBlock(),e.createBlock(e.unref(B.default),{key:`content-${l.id}`,value:l.id,class:"h-full mt-0 data-[state=active]:flex data-[state=active]:flex-col"},{default:e.withCtx(()=>[e.renderSlot(t.$slots,`content-${l.id}`,{tab:l},()=>[l.component?(e.openBlock(),e.createBlock(e.resolveDynamicComponent(l.component),e.mergeProps({key:0,ref_for:!0},l.props||{}),null,16)):(e.openBlock(),e.createElementBlock("div",q,[e.createElementVNode("p",V,e.toDisplayString(l.label)+" 콘텐츠",1)]))],!0)]),_:2},1032,["value"]))),128))])]),_:3},8,["model-value","class"]))}});exports.default=z;
2
2
  //# sourceMappingURL=JTabs.vue2.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"JTabs.vue2.cjs","sources":["../../../../src/components/molecules/JTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch, nextTick } from 'vue'\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/shadcn'\nimport type { JTabsProps, JTabsEmits } from '@/types/dynamic-tabs.types'\nimport { X } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport JIcon from '@/components/atoms/JIcon.vue'\n\n/**\n * JTabs - 기본 탭 UI 컴포넌트 (molecules)\n * Basic Tabs UI Component\n * \n * @description\n * 정적인 탭 목록을 렌더링하는 기본 탭 컴포넌트입니다.\n * 닫기 버튼, 아이콘 등을 지원합니다.\n * \n * @example\n * ```vue\n * <JTabs \n * :tabs=\"tabs\"\n * :active-tab-id=\"activeId\"\n * @tab-change=\"handleChange\"\n * @tab-close=\"handleClose\"\n * />\n * ```\n */\n\ntype StyleType = 'default' | 'minimal'\n\nconst props = withDefaults(defineProps<JTabsProps>(), {\n styletype: 'default',\n})\n\nconst emit = defineEmits<JTabsEmits>()\n\n/**\n * 안전한 tabs 배열 (undefined/null 체크)\n * Safe tabs array (undefined/null check)\n */\nconst safeTabs = computed(() => {\n return Array.isArray(props.tabs) ? props.tabs : []\n})\n\n/**\n * 현재 활성화된 탭 ID (내부 상태)\n * Current active tab ID (internal state)\n */\nconst internalActiveId = ref<string>(\n props.activeTabId || (safeTabs.value.length > 0 ? safeTabs.value[0]?.id : '') || ''\n)\n\n/**\n * 이벤트 처리 중 플래그 (중복 이벤트 방지)\n * Flag to prevent duplicate events\n */\nlet isHandlingEvent = false\n\n/**\n * props.activeTabId가 변경되면 내부 상태 동기화\n * Sync internal state when props.activeTabId changes\n */\nwatch(() => props.activeTabId, (newValue) => {\n if (newValue !== undefined && newValue !== internalActiveId.value) {\n internalActiveId.value = newValue\n }\n}, { immediate: true })\n\n/**\n * props.tabs가 변경되고 activeTabId가 없으면 첫 번째 탭 활성화\n * Activate first tab when tabs change and no activeTabId\n */\nwatch(safeTabs, (newTabs) => {\n if (!props.activeTabId && newTabs.length > 0 && !newTabs.find(t => t.id === internalActiveId.value) && newTabs[0]) {\n internalActiveId.value = newTabs[0].id\n }\n})\n\n/**\n * 탭 값 변경 핸들러 (reka-ui TabsRoot에서 직접 호출됨)\n * Tab value change handler (called directly from reka-ui TabsRoot)\n */\nconst handleTabValueChange = (value: string | number) => {\n if (isHandlingEvent) return\n \n const stringValue = String(value)\n if (stringValue !== internalActiveId.value) {\n isHandlingEvent = true\n internalActiveId.value = stringValue\n emit('update:activeTabId', stringValue)\n emit('tabChange', stringValue)\n // 다음 tick에서 플래그 리셋\n nextTick(() => {\n isHandlingEvent = false\n })\n }\n}\n\n/**\n * 탭 클릭 핸들러 (백업 방안 - reka-ui 이벤트가 작동하지 않을 경우)\n * Tab click handler (backup - in case reka-ui events don't work)\n */\nconst handleTabClick = (tabId: string) => {\n // reka-ui 이벤트가 작동하지 않을 경우 직접 처리\n // handleTabValueChange가 이미 처리했으면 중복 방지\n if (isHandlingEvent || tabId === internalActiveId.value) return\n \n isHandlingEvent = true\n internalActiveId.value = tabId\n emit('update:activeTabId', tabId)\n emit('tabChange', tabId)\n // 다음 tick에서 플래그 리셋\n nextTick(() => {\n isHandlingEvent = false\n })\n}\n\n/**\n * 탭 닫기 핸들러\n * Tab close handler\n */\nconst handleCloseTab = (e: Event, tabId: string) => {\n e.stopPropagation() // 탭 클릭 이벤트 전파 방지\n emit('tabClose', tabId)\n}\n\n/**\n * 루트 클래스\n * Root classes\n */\nconst rootClasses = computed(() => {\n return cn('flex flex-col w-full h-full', props.class)\n})\n\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n tabPaddingClass: string\n tabTextSizeClass: string\n listPaddingClass: string\n}> = {\n default: {\n tabPaddingClass: 'px-2 py-0.5',\n tabTextSizeClass: 'text-xs',\n listPaddingClass: 'px-1.5 py-0.5',\n },\n minimal: {\n tabPaddingClass: 'px-1.5 py-0.5',\n tabTextSizeClass: 'text-xs',\n listPaddingClass: 'px-1.5 py-0.5',\n },\n}\n\nconst preset = computed(() => {\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\n/**\n * 탭 리스트 클래스\n * Tabs list classes\n */\nconst listClasses = computed(() => {\n return cn('w-full justify-start', preset.value.listPaddingClass, props.listClass)\n})\n</script>\n\n<template>\n <Tabs\n :model-value=\"internalActiveId\"\n @update:model-value=\"handleTabValueChange\"\n orientation=\"horizontal\"\n :class=\"rootClasses\"\n >\n <!-- 탭 헤더 영역 / Tab Headers -->\n <TabsList :class=\"listClasses\">\n <TabsTrigger\n v-for=\"tab in safeTabs\"\n :key=\"tab.id\"\n :value=\"tab.id\"\n @click=\"handleTabClick(tab.id)\"\n :class=\"cn('!flex !items-center !gap-2', preset.tabPaddingClass, preset.tabTextSizeClass)\"\n >\n <!-- 탭 아이콘 (있을 경우) / Tab Icon -->\n <JIcon \n v-if=\"tab.icon\" \n :name=\"tab.icon\" \n size=\"sm\"\n class=\"flex-shrink-0\"\n />\n \n <!-- 탭 레이블 / Tab Label -->\n <span class=\"flex-1 truncate\">{{ tab.label }}</span>\n \n <!-- 닫기 버튼 / Close Button (항상 표시) -->\n <button\n v-if=\"tab.closable\"\n type=\"button\"\n class=\"flex-shrink-0 h-3.5 w-3.5 rounded-sm hover:bg-destructive/10 hover:text-destructive transition-colors focus:outline-none focus:ring-2 focus:ring-ring flex items-center justify-center\"\n :aria-label=\"`${tab.label} 탭 닫기`\"\n @click=\"(e) => handleCloseTab(e, tab.id)\"\n >\n <X class=\"h-2.5 w-2.5\" />\n </button>\n </TabsTrigger>\n </TabsList>\n\n <!-- 탭 콘텐츠 영역 / Tab Contents -->\n <div class=\"flex-1 w-full overflow-auto\">\n <TabsContent\n v-for=\"tab in safeTabs\"\n :key=\"`content-${tab.id}`\"\n :value=\"tab.id\"\n class=\"h-full mt-0 data-[state=active]:flex data-[state=active]:flex-col\"\n >\n <!-- 슬롯 우선 / Slot First -->\n <slot :name=\"`content-${tab.id}`\" :tab=\"tab\">\n <!-- 동적 컴포넌트 렌더링 / Dynamic Component Rendering -->\n <component\n v-if=\"tab.component\"\n :is=\"tab.component\"\n v-bind=\"tab.props || {}\"\n />\n \n <!-- 기본 콘텐츠 / Default Content -->\n <div v-else class=\"p-4\">\n <p class=\"text-muted-foreground\">{{ tab.label }} 콘텐츠</p>\n </div>\n </slot>\n </TabsContent>\n </div>\n </Tabs>\n</template>\n\n<style scoped>\n/**\n * 탭 리스트 스타일 - 하단 보더 제거\n * Tab list styles without bottom border\n */\n:deep([role=\"tablist\"]:not(.ag-side-buttons)) {\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: thin;\n scrollbar-color: rgba(0, 0, 0, 0.2) transparent;\n background: hsl(var(--background));\n padding: 0.125rem 0.375rem 0;\n gap: 0.125rem;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar) {\n height: 6px;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-track) {\n background: transparent;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb) {\n background-color: rgba(0, 0, 0, 0.2);\n border-radius: 3px;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb:hover) {\n background-color: rgba(0, 0, 0, 0.3);\n}\n\n/**\n * 다크모드에서 스크롤바 스타일\n * Scrollbar styles in dark mode\n */\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb) {\n background-color: rgba(255, 255, 255, 0.2);\n}\n\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb:hover) {\n background-color: rgba(255, 255, 255, 0.3);\n}\n\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)) {\n scrollbar-color: rgba(255, 255, 255, 0.2) transparent;\n}\n\n/**\n * 탭 버튼 스타일 - 명확한 구분\n * Tab button styles - clear distinction\n */\n:deep([role=\"tab\"]) {\n position: relative;\n padding: 0.25rem 0.625rem;\n min-height: 1.75rem;\n border-radius: 0.375rem 0.375rem 0 0;\n transition: all 0.2s ease;\n border: 1px solid transparent;\n border-bottom: none;\n}\n\n/**\n * Minimal 스타일 탭 버튼 - JSidebarAdvanced와 높이 맞춤\n */\n:deep([role=\"tablist\"][class*=\"p-0.5\"] [role=\"tab\"]) {\n padding: 0.25rem 0.5rem;\n}\n\n/**\n * 비활성 탭 - 명확하게 구분\n * Inactive tabs - clear distinction\n */\n:deep([role=\"tab\"][data-state=\"inactive\"]) {\n background: hsl(var(--muted) / 0.2);\n color: hsl(var(--muted-foreground));\n border-top: 1px solid hsl(var(--border) / 0.4);\n border-left: 1px solid hsl(var(--border) / 0.4);\n border-right: 1px solid hsl(var(--border) / 0.4);\n}\n\n:deep([role=\"tab\"][data-state=\"inactive\"]:hover) {\n background: hsl(var(--muted) / 0.4);\n color: hsl(var(--foreground));\n}\n\n/**\n * 다크모드에서 비활성 탭 - 다크 배경색 사용\n * Inactive tabs in dark mode - use dark background\n */\n.dark :deep([role=\"tab\"][data-state=\"inactive\"]) {\n background: hsl(var(--secondary));\n color: hsl(var(--secondary-foreground));\n border-top: 1px solid hsl(var(--border) / 0.5);\n border-left: 1px solid hsl(var(--border) / 0.5);\n border-right: 1px solid hsl(var(--border) / 0.5);\n}\n\n.dark :deep([role=\"tab\"][data-state=\"inactive\"]:hover) {\n background: hsl(var(--muted));\n color: hsl(var(--muted-foreground));\n}\n\n/**\n * 활성 탭 - 강조된 스타일\n * Active tab - emphasized style\n */\n:deep([role=\"tab\"][data-state=\"active\"]) {\n background: hsl(var(--background));\n color: hsl(var(--foreground));\n font-weight: 500;\n border-top: 2px solid hsl(var(--primary));\n border-left: 1px solid hsl(var(--border));\n border-right: 1px solid hsl(var(--border));\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n z-index: 1;\n}\n\n/**\n * 다크모드에서 활성 탭 - 배경색 명확히 구분\n * Active tab in dark mode - clear background distinction\n */\n.dark :deep([role=\"tab\"][data-state=\"active\"]) {\n background: hsl(var(--card));\n color: hsl(var(--card-foreground));\n border-top: 2px solid hsl(var(--primary));\n border-left: 1px solid hsl(var(--border));\n border-right: 1px solid hsl(var(--border));\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n}\n\n</style>\n"],"names":["props","__props","emit","__emit","safeTabs","computed","internalActiveId","ref","isHandlingEvent","watch","newValue","newTabs","t","handleTabValueChange","value","stringValue","nextTick","handleTabClick","tabId","handleCloseTab","e","rootClasses","cn","STYLE_PRESETS","preset","listClasses","_createBlock","_unref","Tabs","_createVNode","TabsList","_createElementBlock","_Fragment","_renderList","tab","TabsTrigger","$event","_normalizeClass","JIcon","_createElementVNode","_hoisted_1","_toDisplayString","X","_hoisted_3","TabsContent","_renderSlot","_ctx","_openBlock","_resolveDynamicComponent","_mergeProps","_hoisted_4","_hoisted_5"],"mappings":"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"}
1
+ {"version":3,"file":"JTabs.vue2.cjs","sources":["../../../../src/components/molecules/JTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch, nextTick } from 'vue'\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/shadcn'\nimport type { JTabsProps, JTabsEmits } from '@/types/dynamic-tabs.types'\nimport { X } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport JIcon from '@/components/atoms/JIcon.vue'\n\n/**\n * JTabs - 기본 탭 UI 컴포넌트 (molecules)\n * Basic Tabs UI Component\n * \n * @description\n * 정적인 탭 목록을 렌더링하는 기본 탭 컴포넌트입니다.\n * 닫기 버튼, 아이콘 등을 지원합니다.\n * \n * @example\n * ```vue\n * <JTabs \n * :tabs=\"tabs\"\n * :active-tab-id=\"activeId\"\n * @tab-change=\"handleChange\"\n * @tab-close=\"handleClose\"\n * />\n * ```\n */\n\ntype StyleType = 'default' | 'minimal'\n\nconst props = withDefaults(defineProps<JTabsProps>(), {\n styletype: 'default',\n})\n\nconst emit = defineEmits<JTabsEmits>()\n\n/**\n * 안전한 tabs 배열 (undefined/null 체크)\n * Safe tabs array (undefined/null check)\n */\nconst safeTabs = computed(() => {\n return Array.isArray(props.tabs) ? props.tabs : []\n})\n\n/**\n * 현재 활성화된 탭 ID (내부 상태)\n * Current active tab ID (internal state)\n */\nconst internalActiveId = ref<string>(\n props.activeTabId || (safeTabs.value.length > 0 ? safeTabs.value[0]?.id : '') || ''\n)\n\n/**\n * 이벤트 처리 중 플래그 (중복 이벤트 방지)\n * Flag to prevent duplicate events\n */\nlet isHandlingEvent = false\n\n/**\n * props.activeTabId가 변경되면 내부 상태 동기화\n * Sync internal state when props.activeTabId changes\n */\nwatch(() => props.activeTabId, (newValue) => {\n if (newValue !== undefined && newValue !== internalActiveId.value) {\n internalActiveId.value = newValue\n }\n}, { immediate: true })\n\n/**\n * props.tabs가 변경되고 activeTabId가 없으면 첫 번째 탭 활성화\n * Activate first tab when tabs change and no activeTabId\n */\nwatch(safeTabs, (newTabs) => {\n if (!props.activeTabId && newTabs.length > 0 && !newTabs.find(t => t.id === internalActiveId.value) && newTabs[0]) {\n internalActiveId.value = newTabs[0].id\n }\n})\n\n/**\n * 탭 값 변경 핸들러 (reka-ui TabsRoot에서 직접 호출됨)\n * Tab value change handler (called directly from reka-ui TabsRoot)\n */\nconst handleTabValueChange = (value: string | number) => {\n if (isHandlingEvent) return\n \n const stringValue = String(value)\n if (stringValue !== internalActiveId.value) {\n isHandlingEvent = true\n internalActiveId.value = stringValue\n emit('update:activeTabId', stringValue)\n emit('tabChange', stringValue)\n // 다음 tick에서 플래그 리셋\n nextTick(() => {\n isHandlingEvent = false\n })\n }\n}\n\n/**\n * 탭 클릭 핸들러 (백업 방안 - reka-ui 이벤트가 작동하지 않을 경우)\n * Tab click handler (backup - in case reka-ui events don't work)\n */\nconst handleTabClick = (tabId: string) => {\n // reka-ui 이벤트가 작동하지 않을 경우 직접 처리\n // handleTabValueChange가 이미 처리했으면 중복 방지\n if (isHandlingEvent || tabId === internalActiveId.value) return\n \n isHandlingEvent = true\n internalActiveId.value = tabId\n emit('update:activeTabId', tabId)\n emit('tabChange', tabId)\n // 다음 tick에서 플래그 리셋\n nextTick(() => {\n isHandlingEvent = false\n })\n}\n\n/**\n * 탭 닫기 핸들러\n * Tab close handler\n */\nconst handleCloseTab = (e: Event, tabId: string) => {\n e.stopPropagation() // 탭 클릭 이벤트 전파 방지\n emit('tabClose', tabId)\n}\n\n/**\n * 루트 클래스\n * Root classes\n */\nconst rootClasses = computed(() => {\n return cn('flex flex-col w-full h-full', props.class)\n})\n\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n tabPaddingClass: string\n tabTextSizeClass: string\n listPaddingClass: string\n}> = {\n default: {\n tabPaddingClass: 'px-2 py-1',\n tabTextSizeClass: 'text-xs',\n listPaddingClass: 'px-1 py-1',\n },\n minimal: {\n tabPaddingClass: 'px-1.5 py-1',\n tabTextSizeClass: 'text-xs',\n listPaddingClass: 'px-1 py-1',\n },\n}\n\nconst preset = computed(() => {\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\n/**\n * 탭 리스트 클래스\n * Tabs list classes\n */\nconst listClasses = computed(() => {\n return cn('w-full justify-start', preset.value.listPaddingClass, props.listClass)\n})\n</script>\n\n<template>\n <Tabs\n :model-value=\"internalActiveId\"\n @update:model-value=\"handleTabValueChange\"\n orientation=\"horizontal\"\n :class=\"rootClasses\"\n >\n <!-- 탭 헤더 영역 / Tab Headers -->\n <TabsList :class=\"listClasses\">\n <TabsTrigger\n v-for=\"tab in safeTabs\"\n :key=\"tab.id\"\n :value=\"tab.id\"\n @click=\"handleTabClick(tab.id)\"\n :class=\"cn('!flex !items-center !gap-2', preset.tabPaddingClass, preset.tabTextSizeClass)\"\n >\n <!-- 탭 아이콘 (있을 경우) / Tab Icon -->\n <JIcon \n v-if=\"tab.icon\" \n :name=\"tab.icon\" \n size=\"sm\"\n class=\"flex-shrink-0\"\n />\n \n <!-- 탭 레이블 / Tab Label -->\n <span class=\"flex-1 truncate\">{{ tab.label }}</span>\n \n <!-- 닫기 버튼 / Close Button (항상 표시) -->\n <button\n v-if=\"tab.closable\"\n type=\"button\"\n class=\"flex-shrink-0 h-3.5 w-3.5 rounded-sm hover:bg-destructive/10 hover:text-destructive transition-colors focus:outline-none focus:ring-2 focus:ring-ring flex items-center justify-center\"\n :aria-label=\"`${tab.label} 탭 닫기`\"\n @click=\"(e) => handleCloseTab(e, tab.id)\"\n >\n <X class=\"h-2.5 w-2.5\" />\n </button>\n </TabsTrigger>\n </TabsList>\n\n <!-- 탭 콘텐츠 영역 / Tab Contents -->\n <div class=\"flex-1 w-full overflow-auto\">\n <TabsContent\n v-for=\"tab in safeTabs\"\n :key=\"`content-${tab.id}`\"\n :value=\"tab.id\"\n class=\"h-full mt-0 data-[state=active]:flex data-[state=active]:flex-col\"\n >\n <!-- 슬롯 우선 / Slot First -->\n <slot :name=\"`content-${tab.id}`\" :tab=\"tab\">\n <!-- 동적 컴포넌트 렌더링 / Dynamic Component Rendering -->\n <component\n v-if=\"tab.component\"\n :is=\"tab.component\"\n v-bind=\"tab.props || {}\"\n />\n \n <!-- 기본 콘텐츠 / Default Content -->\n <div v-else class=\"p-4\">\n <p class=\"text-muted-foreground\">{{ tab.label }} 콘텐츠</p>\n </div>\n </slot>\n </TabsContent>\n </div>\n </Tabs>\n</template>\n\n<style scoped>\n/**\n * 탭 리스트 스타일 - 하단 보더 제거\n * Tab list styles without bottom border\n */\n:deep([role=\"tablist\"]:not(.ag-side-buttons)) {\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: thin;\n scrollbar-color: rgba(0, 0, 0, 0.2) transparent;\n background: hsl(var(--background));\n padding: 0 0.25rem;\n gap: 0.25rem;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar) {\n height: 6px;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-track) {\n background: transparent;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb) {\n background-color: rgba(0, 0, 0, 0.2);\n border-radius: 3px;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb:hover) {\n background-color: rgba(0, 0, 0, 0.3);\n}\n\n/**\n * 다크모드에서 스크롤바 스타일\n * Scrollbar styles in dark mode\n */\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb) {\n background-color: rgba(255, 255, 255, 0.2);\n}\n\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb:hover) {\n background-color: rgba(255, 255, 255, 0.3);\n}\n\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)) {\n scrollbar-color: rgba(255, 255, 255, 0.2) transparent;\n}\n\n/**\n * 탭 버튼 스타일 - 명확한 구분\n * Tab button styles - clear distinction\n */\n:deep([role=\"tab\"]) {\n position: relative;\n padding: 0.25rem 0.5rem;\n min-height: 1.75rem;\n border-radius: 0.375rem 0.375rem 0 0;\n transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;\n border: 1px solid transparent;\n border-bottom: none;\n}\n\n/**\n * Minimal 스타일 탭 버튼 - JSidebarAdvanced와 높이 맞춤\n */\n:deep([role=\"tablist\"][class*=\"p-0.5\"] [role=\"tab\"]) {\n padding: 0.25rem 0.5rem;\n}\n\n/**\n * 비활성 탭 - 명확하게 구분\n * Inactive tabs - clear distinction\n */\n:deep([role=\"tab\"][data-state=\"inactive\"]) {\n background: hsl(var(--muted) / 0.2);\n color: hsl(var(--muted-foreground));\n border-top: 1px solid hsl(var(--border) / 0.4);\n border-left: 1px solid hsl(var(--border) / 0.4);\n border-right: 1px solid hsl(var(--border) / 0.4);\n}\n\n:deep([role=\"tab\"][data-state=\"inactive\"]:hover) {\n background: hsl(var(--muted) / 0.4);\n color: hsl(var(--foreground));\n}\n\n/**\n * 다크모드에서 비활성 탭 - 다크 배경색 사용\n * Inactive tabs in dark mode - use dark background\n */\n.dark :deep([role=\"tab\"][data-state=\"inactive\"]) {\n background: hsl(var(--secondary));\n color: hsl(var(--secondary-foreground));\n border-top: 1px solid hsl(var(--border) / 0.5);\n border-left: 1px solid hsl(var(--border) / 0.5);\n border-right: 1px solid hsl(var(--border) / 0.5);\n}\n\n.dark :deep([role=\"tab\"][data-state=\"inactive\"]:hover) {\n background: hsl(var(--muted));\n color: hsl(var(--muted-foreground));\n}\n\n/**\n * 활성 탭 - 강조된 스타일\n * Active tab - emphasized style\n */\n:deep([role=\"tab\"][data-state=\"active\"]) {\n background: hsl(var(--background));\n color: hsl(var(--foreground));\n font-weight: 500;\n border-top: 2px solid hsl(var(--primary));\n border-left: 1px solid hsl(var(--border));\n border-right: 1px solid hsl(var(--border));\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n z-index: 1;\n}\n\n/**\n * 다크모드에서 활성 탭 - 배경색 명확히 구분\n * Active tab in dark mode - clear background distinction\n */\n.dark :deep([role=\"tab\"][data-state=\"active\"]) {\n background: hsl(var(--card));\n color: hsl(var(--card-foreground));\n border-top: 2px solid hsl(var(--primary));\n border-left: 1px solid hsl(var(--border));\n border-right: 1px solid hsl(var(--border));\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n}\n\n</style>\n"],"names":["props","__props","emit","__emit","safeTabs","computed","internalActiveId","ref","isHandlingEvent","watch","newValue","newTabs","t","handleTabValueChange","value","stringValue","nextTick","handleTabClick","tabId","handleCloseTab","e","rootClasses","cn","STYLE_PRESETS","preset","listClasses","_createBlock","_unref","Tabs","_createVNode","TabsList","_createElementBlock","_Fragment","_renderList","tab","TabsTrigger","$event","_normalizeClass","JIcon","_createElementVNode","_hoisted_1","_toDisplayString","X","_hoisted_3","TabsContent","_renderSlot","_ctx","_openBlock","_resolveDynamicComponent","_mergeProps","_hoisted_4","_hoisted_5"],"mappings":"gwBA6BA,MAAMA,EAAQC,EAIRC,EAAOC,EAMPC,EAAWC,EAAAA,SAAS,IACjB,MAAM,QAAQL,EAAM,IAAI,EAAIA,EAAM,KAAO,CAAA,CACjD,EAMKM,EAAmBC,EAAAA,IACvBP,EAAM,cAAgBI,EAAS,MAAM,OAAS,EAAIA,EAAS,MAAM,CAAC,GAAG,GAAK,KAAO,EAAA,EAOnF,IAAII,EAAkB,GAMtBC,EAAAA,MAAM,IAAMT,EAAM,YAAcU,GAAa,CACvCA,IAAa,QAAaA,IAAaJ,EAAiB,QAC1DA,EAAiB,MAAQI,EAE7B,EAAG,CAAE,UAAW,GAAM,EAMtBD,QAAML,EAAWO,GAAY,CACvB,CAACX,EAAM,aAAeW,EAAQ,OAAS,GAAK,CAACA,EAAQ,KAAKC,GAAKA,EAAE,KAAON,EAAiB,KAAK,GAAKK,EAAQ,CAAC,IAC9GL,EAAiB,MAAQK,EAAQ,CAAC,EAAE,GAExC,CAAC,EAMD,MAAME,EAAwBC,GAA2B,CACvD,GAAIN,EAAiB,OAErB,MAAMO,EAAc,OAAOD,CAAK,EAC5BC,IAAgBT,EAAiB,QACnCE,EAAkB,GAClBF,EAAiB,MAAQS,EACzBb,EAAK,qBAAsBa,CAAW,EACtCb,EAAK,YAAaa,CAAW,EAE7BC,EAAAA,SAAS,IAAM,CACbR,EAAkB,EACpB,CAAC,EAEL,EAMMS,EAAkBC,GAAkB,CAGpCV,GAAmBU,IAAUZ,EAAiB,QAElDE,EAAkB,GAClBF,EAAiB,MAAQY,EACzBhB,EAAK,qBAAsBgB,CAAK,EAChChB,EAAK,YAAagB,CAAK,EAEvBF,EAAAA,SAAS,IAAM,CACbR,EAAkB,EACpB,CAAC,EACH,EAMMW,EAAiB,CAACC,EAAUF,IAAkB,CAClDE,EAAE,gBAAA,EACFlB,EAAK,WAAYgB,CAAK,CACxB,EAMMG,EAAchB,EAAAA,SAAS,IACpBiB,KAAG,8BAA+BtB,EAAM,KAAK,CACrD,EAKKuB,EAID,CACH,QAAS,CACP,gBAAiB,YACjB,iBAAkB,UAClB,iBAAkB,WAAA,EAEpB,QAAS,CACP,gBAAiB,cACjB,iBAAkB,UAClB,iBAAkB,WAAA,CACpB,EAGIC,EAASnB,EAAAA,SAAS,IACfkB,EAAcvB,EAAM,SAAS,GAAKuB,EAAc,OACxD,EAMKE,EAAcpB,EAAAA,SAAS,IACpBiB,EAAAA,GAAG,uBAAwBE,EAAO,MAAM,iBAAkBxB,EAAM,SAAS,CACjF,8BAIC0B,EAAAA,YA+DOC,EAAAA,MAAAC,EAAAA,OAAA,EAAA,CA9DJ,cAAatB,EAAA,MACb,sBAAoBO,EACrB,YAAY,aACX,uBAAOQ,EAAA,KAAW,CAAA,qBAGnB,IA8BW,CA9BXQ,cA8BWF,EAAAA,MAAAG,EAAAA,OAAA,EAAA,CA9BA,uBAAOL,EAAA,KAAW,CAAA,qBAEzB,IAAuB,kBADzBM,EAAAA,mBA4BcC,EAAAA,SAAA,KAAAC,EAAAA,WA3BE7B,EAAA,MAAP8B,kBADTR,EAAAA,YA4BcC,EAAAA,MAAAQ,EAAAA,OAAA,EAAA,CA1BX,IAAKD,EAAI,GACT,MAAOA,EAAI,GACX,QAAKE,GAAEnB,EAAeiB,EAAI,EAAE,EAC5B,MAAKG,EAAAA,eAAEV,cAAE,6BAA+BH,EAAA,MAAO,gBAAiBA,EAAA,MAAO,gBAAgB,CAAA,CAAA,qBAGxF,IAKE,CAJMU,EAAI,oBADZR,EAAAA,YAKEY,EAAAA,QAAA,OAHC,KAAMJ,EAAI,KACX,KAAK,KACL,MAAM,eAAA,gDAIRK,EAAAA,mBAAoD,OAApDC,EAAoDC,EAAAA,gBAAnBP,EAAI,KAAK,EAAA,CAAA,EAIlCA,EAAI,wBADZH,EAAAA,mBAQS,SAAA,OANP,KAAK,SACL,MAAM,yLACL,aAAU,GAAKG,EAAI,KAAK,QACxB,QAAQd,GAAMD,EAAeC,EAAGc,EAAI,EAAE,CAAA,GAEvCL,EAAAA,YAAyBF,EAAAA,MAAAe,EAAAA,CAAA,EAAA,CAAtB,MAAM,cAAa,CAAA,yGAM5BH,EAAAA,mBAsBM,MAtBNI,EAsBM,kBArBJZ,EAAAA,mBAoBcC,EAAAA,SAAA,KAAAC,EAAAA,WAnBE7B,EAAA,MAAP8B,kBADTR,EAAAA,YAoBcC,EAAAA,MAAAiB,EAAAA,OAAA,EAAA,CAlBX,IAAG,WAAaV,EAAI,EAAE,GACtB,MAAOA,EAAI,GACZ,MAAM,mEAAA,qBAGN,IAYO,CAZPW,aAYOC,EAAA,OAAA,WAZiBZ,EAAI,EAAE,IAAK,IAAAA,CAAA,EAAnC,IAYO,CATGA,EAAI,WADZa,EAAAA,YAAArB,EAAAA,YAIEsB,EAAAA,wBAFKd,EAAI,SAAS,EAFpBe,aAIE,mBADQf,EAAI,OAAK,CAAA,CAAA,EAAA,KAAA,EAAA,IAInBa,YAAA,EAAAhB,qBAEM,MAFNmB,EAEM,CADJX,EAAAA,mBAAwD,IAAxDY,EAAwDV,EAAAA,gBAApBP,EAAI,KAAK,EAAG,OAAI,CAAA,CAAA"}
@@ -44,14 +44,14 @@ const Y = { class: "flex-1 truncate" }, q = ["aria-label", "onClick"], G = { cla
44
44
  e.stopPropagation(), r("tabClose", l);
45
45
  }, w = d(() => C("flex flex-col w-full h-full", n.class)), h = {
46
46
  default: {
47
- tabPaddingClass: "px-2 py-0.5",
47
+ tabPaddingClass: "px-2 py-1",
48
48
  tabTextSizeClass: "text-xs",
49
- listPaddingClass: "px-1.5 py-0.5"
49
+ listPaddingClass: "px-1 py-1"
50
50
  },
51
51
  minimal: {
52
- tabPaddingClass: "px-1.5 py-0.5",
52
+ tabPaddingClass: "px-1.5 py-1",
53
53
  tabTextSizeClass: "text-xs",
54
- listPaddingClass: "px-1.5 py-0.5"
54
+ listPaddingClass: "px-1 py-1"
55
55
  }
56
56
  }, p = d(() => h[n.styletype] ?? h.default), A = d(() => C("w-full justify-start", p.value.listPaddingClass, n.listClass));
57
57
  return (e, l) => (s(), u(i(J), {
@@ -1 +1 @@
1
- {"version":3,"file":"JTabs.vue2.js","sources":["../../../../src/components/molecules/JTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch, nextTick } from 'vue'\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/shadcn'\nimport type { JTabsProps, JTabsEmits } from '@/types/dynamic-tabs.types'\nimport { X } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport JIcon from '@/components/atoms/JIcon.vue'\n\n/**\n * JTabs - 기본 탭 UI 컴포넌트 (molecules)\n * Basic Tabs UI Component\n * \n * @description\n * 정적인 탭 목록을 렌더링하는 기본 탭 컴포넌트입니다.\n * 닫기 버튼, 아이콘 등을 지원합니다.\n * \n * @example\n * ```vue\n * <JTabs \n * :tabs=\"tabs\"\n * :active-tab-id=\"activeId\"\n * @tab-change=\"handleChange\"\n * @tab-close=\"handleClose\"\n * />\n * ```\n */\n\ntype StyleType = 'default' | 'minimal'\n\nconst props = withDefaults(defineProps<JTabsProps>(), {\n styletype: 'default',\n})\n\nconst emit = defineEmits<JTabsEmits>()\n\n/**\n * 안전한 tabs 배열 (undefined/null 체크)\n * Safe tabs array (undefined/null check)\n */\nconst safeTabs = computed(() => {\n return Array.isArray(props.tabs) ? props.tabs : []\n})\n\n/**\n * 현재 활성화된 탭 ID (내부 상태)\n * Current active tab ID (internal state)\n */\nconst internalActiveId = ref<string>(\n props.activeTabId || (safeTabs.value.length > 0 ? safeTabs.value[0]?.id : '') || ''\n)\n\n/**\n * 이벤트 처리 중 플래그 (중복 이벤트 방지)\n * Flag to prevent duplicate events\n */\nlet isHandlingEvent = false\n\n/**\n * props.activeTabId가 변경되면 내부 상태 동기화\n * Sync internal state when props.activeTabId changes\n */\nwatch(() => props.activeTabId, (newValue) => {\n if (newValue !== undefined && newValue !== internalActiveId.value) {\n internalActiveId.value = newValue\n }\n}, { immediate: true })\n\n/**\n * props.tabs가 변경되고 activeTabId가 없으면 첫 번째 탭 활성화\n * Activate first tab when tabs change and no activeTabId\n */\nwatch(safeTabs, (newTabs) => {\n if (!props.activeTabId && newTabs.length > 0 && !newTabs.find(t => t.id === internalActiveId.value) && newTabs[0]) {\n internalActiveId.value = newTabs[0].id\n }\n})\n\n/**\n * 탭 값 변경 핸들러 (reka-ui TabsRoot에서 직접 호출됨)\n * Tab value change handler (called directly from reka-ui TabsRoot)\n */\nconst handleTabValueChange = (value: string | number) => {\n if (isHandlingEvent) return\n \n const stringValue = String(value)\n if (stringValue !== internalActiveId.value) {\n isHandlingEvent = true\n internalActiveId.value = stringValue\n emit('update:activeTabId', stringValue)\n emit('tabChange', stringValue)\n // 다음 tick에서 플래그 리셋\n nextTick(() => {\n isHandlingEvent = false\n })\n }\n}\n\n/**\n * 탭 클릭 핸들러 (백업 방안 - reka-ui 이벤트가 작동하지 않을 경우)\n * Tab click handler (backup - in case reka-ui events don't work)\n */\nconst handleTabClick = (tabId: string) => {\n // reka-ui 이벤트가 작동하지 않을 경우 직접 처리\n // handleTabValueChange가 이미 처리했으면 중복 방지\n if (isHandlingEvent || tabId === internalActiveId.value) return\n \n isHandlingEvent = true\n internalActiveId.value = tabId\n emit('update:activeTabId', tabId)\n emit('tabChange', tabId)\n // 다음 tick에서 플래그 리셋\n nextTick(() => {\n isHandlingEvent = false\n })\n}\n\n/**\n * 탭 닫기 핸들러\n * Tab close handler\n */\nconst handleCloseTab = (e: Event, tabId: string) => {\n e.stopPropagation() // 탭 클릭 이벤트 전파 방지\n emit('tabClose', tabId)\n}\n\n/**\n * 루트 클래스\n * Root classes\n */\nconst rootClasses = computed(() => {\n return cn('flex flex-col w-full h-full', props.class)\n})\n\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n tabPaddingClass: string\n tabTextSizeClass: string\n listPaddingClass: string\n}> = {\n default: {\n tabPaddingClass: 'px-2 py-0.5',\n tabTextSizeClass: 'text-xs',\n listPaddingClass: 'px-1.5 py-0.5',\n },\n minimal: {\n tabPaddingClass: 'px-1.5 py-0.5',\n tabTextSizeClass: 'text-xs',\n listPaddingClass: 'px-1.5 py-0.5',\n },\n}\n\nconst preset = computed(() => {\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\n/**\n * 탭 리스트 클래스\n * Tabs list classes\n */\nconst listClasses = computed(() => {\n return cn('w-full justify-start', preset.value.listPaddingClass, props.listClass)\n})\n</script>\n\n<template>\n <Tabs\n :model-value=\"internalActiveId\"\n @update:model-value=\"handleTabValueChange\"\n orientation=\"horizontal\"\n :class=\"rootClasses\"\n >\n <!-- 탭 헤더 영역 / Tab Headers -->\n <TabsList :class=\"listClasses\">\n <TabsTrigger\n v-for=\"tab in safeTabs\"\n :key=\"tab.id\"\n :value=\"tab.id\"\n @click=\"handleTabClick(tab.id)\"\n :class=\"cn('!flex !items-center !gap-2', preset.tabPaddingClass, preset.tabTextSizeClass)\"\n >\n <!-- 탭 아이콘 (있을 경우) / Tab Icon -->\n <JIcon \n v-if=\"tab.icon\" \n :name=\"tab.icon\" \n size=\"sm\"\n class=\"flex-shrink-0\"\n />\n \n <!-- 탭 레이블 / Tab Label -->\n <span class=\"flex-1 truncate\">{{ tab.label }}</span>\n \n <!-- 닫기 버튼 / Close Button (항상 표시) -->\n <button\n v-if=\"tab.closable\"\n type=\"button\"\n class=\"flex-shrink-0 h-3.5 w-3.5 rounded-sm hover:bg-destructive/10 hover:text-destructive transition-colors focus:outline-none focus:ring-2 focus:ring-ring flex items-center justify-center\"\n :aria-label=\"`${tab.label} 탭 닫기`\"\n @click=\"(e) => handleCloseTab(e, tab.id)\"\n >\n <X class=\"h-2.5 w-2.5\" />\n </button>\n </TabsTrigger>\n </TabsList>\n\n <!-- 탭 콘텐츠 영역 / Tab Contents -->\n <div class=\"flex-1 w-full overflow-auto\">\n <TabsContent\n v-for=\"tab in safeTabs\"\n :key=\"`content-${tab.id}`\"\n :value=\"tab.id\"\n class=\"h-full mt-0 data-[state=active]:flex data-[state=active]:flex-col\"\n >\n <!-- 슬롯 우선 / Slot First -->\n <slot :name=\"`content-${tab.id}`\" :tab=\"tab\">\n <!-- 동적 컴포넌트 렌더링 / Dynamic Component Rendering -->\n <component\n v-if=\"tab.component\"\n :is=\"tab.component\"\n v-bind=\"tab.props || {}\"\n />\n \n <!-- 기본 콘텐츠 / Default Content -->\n <div v-else class=\"p-4\">\n <p class=\"text-muted-foreground\">{{ tab.label }} 콘텐츠</p>\n </div>\n </slot>\n </TabsContent>\n </div>\n </Tabs>\n</template>\n\n<style scoped>\n/**\n * 탭 리스트 스타일 - 하단 보더 제거\n * Tab list styles without bottom border\n */\n:deep([role=\"tablist\"]:not(.ag-side-buttons)) {\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: thin;\n scrollbar-color: rgba(0, 0, 0, 0.2) transparent;\n background: hsl(var(--background));\n padding: 0.125rem 0.375rem 0;\n gap: 0.125rem;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar) {\n height: 6px;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-track) {\n background: transparent;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb) {\n background-color: rgba(0, 0, 0, 0.2);\n border-radius: 3px;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb:hover) {\n background-color: rgba(0, 0, 0, 0.3);\n}\n\n/**\n * 다크모드에서 스크롤바 스타일\n * Scrollbar styles in dark mode\n */\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb) {\n background-color: rgba(255, 255, 255, 0.2);\n}\n\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb:hover) {\n background-color: rgba(255, 255, 255, 0.3);\n}\n\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)) {\n scrollbar-color: rgba(255, 255, 255, 0.2) transparent;\n}\n\n/**\n * 탭 버튼 스타일 - 명확한 구분\n * Tab button styles - clear distinction\n */\n:deep([role=\"tab\"]) {\n position: relative;\n padding: 0.25rem 0.625rem;\n min-height: 1.75rem;\n border-radius: 0.375rem 0.375rem 0 0;\n transition: all 0.2s ease;\n border: 1px solid transparent;\n border-bottom: none;\n}\n\n/**\n * Minimal 스타일 탭 버튼 - JSidebarAdvanced와 높이 맞춤\n */\n:deep([role=\"tablist\"][class*=\"p-0.5\"] [role=\"tab\"]) {\n padding: 0.25rem 0.5rem;\n}\n\n/**\n * 비활성 탭 - 명확하게 구분\n * Inactive tabs - clear distinction\n */\n:deep([role=\"tab\"][data-state=\"inactive\"]) {\n background: hsl(var(--muted) / 0.2);\n color: hsl(var(--muted-foreground));\n border-top: 1px solid hsl(var(--border) / 0.4);\n border-left: 1px solid hsl(var(--border) / 0.4);\n border-right: 1px solid hsl(var(--border) / 0.4);\n}\n\n:deep([role=\"tab\"][data-state=\"inactive\"]:hover) {\n background: hsl(var(--muted) / 0.4);\n color: hsl(var(--foreground));\n}\n\n/**\n * 다크모드에서 비활성 탭 - 다크 배경색 사용\n * Inactive tabs in dark mode - use dark background\n */\n.dark :deep([role=\"tab\"][data-state=\"inactive\"]) {\n background: hsl(var(--secondary));\n color: hsl(var(--secondary-foreground));\n border-top: 1px solid hsl(var(--border) / 0.5);\n border-left: 1px solid hsl(var(--border) / 0.5);\n border-right: 1px solid hsl(var(--border) / 0.5);\n}\n\n.dark :deep([role=\"tab\"][data-state=\"inactive\"]:hover) {\n background: hsl(var(--muted));\n color: hsl(var(--muted-foreground));\n}\n\n/**\n * 활성 탭 - 강조된 스타일\n * Active tab - emphasized style\n */\n:deep([role=\"tab\"][data-state=\"active\"]) {\n background: hsl(var(--background));\n color: hsl(var(--foreground));\n font-weight: 500;\n border-top: 2px solid hsl(var(--primary));\n border-left: 1px solid hsl(var(--border));\n border-right: 1px solid hsl(var(--border));\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n z-index: 1;\n}\n\n/**\n * 다크모드에서 활성 탭 - 배경색 명확히 구분\n * Active tab in dark mode - clear background distinction\n */\n.dark :deep([role=\"tab\"][data-state=\"active\"]) {\n background: hsl(var(--card));\n color: hsl(var(--card-foreground));\n border-top: 2px solid hsl(var(--primary));\n border-left: 1px solid hsl(var(--border));\n border-right: 1px solid hsl(var(--border));\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n}\n\n</style>\n"],"names":["props","__props","emit","__emit","safeTabs","computed","internalActiveId","ref","isHandlingEvent","watch","newValue","newTabs","t","handleTabValueChange","value","stringValue","nextTick","handleTabClick","tabId","handleCloseTab","rootClasses","cn","STYLE_PRESETS","preset","listClasses","_createBlock","_unref","Tabs","_createVNode","TabsList","_createElementBlock","_Fragment","_renderList","tab","TabsTrigger","$event","_normalizeClass","JIcon","_createElementVNode","_hoisted_1","_toDisplayString","e","X","_hoisted_3","TabsContent","_renderSlot","_ctx","_openBlock","_resolveDynamicComponent","_mergeProps","_hoisted_4","_hoisted_5"],"mappings":"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"}
1
+ {"version":3,"file":"JTabs.vue2.js","sources":["../../../../src/components/molecules/JTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch, nextTick } from 'vue'\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/shadcn'\nimport type { JTabsProps, JTabsEmits } from '@/types/dynamic-tabs.types'\nimport { X } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport JIcon from '@/components/atoms/JIcon.vue'\n\n/**\n * JTabs - 기본 탭 UI 컴포넌트 (molecules)\n * Basic Tabs UI Component\n * \n * @description\n * 정적인 탭 목록을 렌더링하는 기본 탭 컴포넌트입니다.\n * 닫기 버튼, 아이콘 등을 지원합니다.\n * \n * @example\n * ```vue\n * <JTabs \n * :tabs=\"tabs\"\n * :active-tab-id=\"activeId\"\n * @tab-change=\"handleChange\"\n * @tab-close=\"handleClose\"\n * />\n * ```\n */\n\ntype StyleType = 'default' | 'minimal'\n\nconst props = withDefaults(defineProps<JTabsProps>(), {\n styletype: 'default',\n})\n\nconst emit = defineEmits<JTabsEmits>()\n\n/**\n * 안전한 tabs 배열 (undefined/null 체크)\n * Safe tabs array (undefined/null check)\n */\nconst safeTabs = computed(() => {\n return Array.isArray(props.tabs) ? props.tabs : []\n})\n\n/**\n * 현재 활성화된 탭 ID (내부 상태)\n * Current active tab ID (internal state)\n */\nconst internalActiveId = ref<string>(\n props.activeTabId || (safeTabs.value.length > 0 ? safeTabs.value[0]?.id : '') || ''\n)\n\n/**\n * 이벤트 처리 중 플래그 (중복 이벤트 방지)\n * Flag to prevent duplicate events\n */\nlet isHandlingEvent = false\n\n/**\n * props.activeTabId가 변경되면 내부 상태 동기화\n * Sync internal state when props.activeTabId changes\n */\nwatch(() => props.activeTabId, (newValue) => {\n if (newValue !== undefined && newValue !== internalActiveId.value) {\n internalActiveId.value = newValue\n }\n}, { immediate: true })\n\n/**\n * props.tabs가 변경되고 activeTabId가 없으면 첫 번째 탭 활성화\n * Activate first tab when tabs change and no activeTabId\n */\nwatch(safeTabs, (newTabs) => {\n if (!props.activeTabId && newTabs.length > 0 && !newTabs.find(t => t.id === internalActiveId.value) && newTabs[0]) {\n internalActiveId.value = newTabs[0].id\n }\n})\n\n/**\n * 탭 값 변경 핸들러 (reka-ui TabsRoot에서 직접 호출됨)\n * Tab value change handler (called directly from reka-ui TabsRoot)\n */\nconst handleTabValueChange = (value: string | number) => {\n if (isHandlingEvent) return\n \n const stringValue = String(value)\n if (stringValue !== internalActiveId.value) {\n isHandlingEvent = true\n internalActiveId.value = stringValue\n emit('update:activeTabId', stringValue)\n emit('tabChange', stringValue)\n // 다음 tick에서 플래그 리셋\n nextTick(() => {\n isHandlingEvent = false\n })\n }\n}\n\n/**\n * 탭 클릭 핸들러 (백업 방안 - reka-ui 이벤트가 작동하지 않을 경우)\n * Tab click handler (backup - in case reka-ui events don't work)\n */\nconst handleTabClick = (tabId: string) => {\n // reka-ui 이벤트가 작동하지 않을 경우 직접 처리\n // handleTabValueChange가 이미 처리했으면 중복 방지\n if (isHandlingEvent || tabId === internalActiveId.value) return\n \n isHandlingEvent = true\n internalActiveId.value = tabId\n emit('update:activeTabId', tabId)\n emit('tabChange', tabId)\n // 다음 tick에서 플래그 리셋\n nextTick(() => {\n isHandlingEvent = false\n })\n}\n\n/**\n * 탭 닫기 핸들러\n * Tab close handler\n */\nconst handleCloseTab = (e: Event, tabId: string) => {\n e.stopPropagation() // 탭 클릭 이벤트 전파 방지\n emit('tabClose', tabId)\n}\n\n/**\n * 루트 클래스\n * Root classes\n */\nconst rootClasses = computed(() => {\n return cn('flex flex-col w-full h-full', props.class)\n})\n\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n tabPaddingClass: string\n tabTextSizeClass: string\n listPaddingClass: string\n}> = {\n default: {\n tabPaddingClass: 'px-2 py-1',\n tabTextSizeClass: 'text-xs',\n listPaddingClass: 'px-1 py-1',\n },\n minimal: {\n tabPaddingClass: 'px-1.5 py-1',\n tabTextSizeClass: 'text-xs',\n listPaddingClass: 'px-1 py-1',\n },\n}\n\nconst preset = computed(() => {\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\n/**\n * 탭 리스트 클래스\n * Tabs list classes\n */\nconst listClasses = computed(() => {\n return cn('w-full justify-start', preset.value.listPaddingClass, props.listClass)\n})\n</script>\n\n<template>\n <Tabs\n :model-value=\"internalActiveId\"\n @update:model-value=\"handleTabValueChange\"\n orientation=\"horizontal\"\n :class=\"rootClasses\"\n >\n <!-- 탭 헤더 영역 / Tab Headers -->\n <TabsList :class=\"listClasses\">\n <TabsTrigger\n v-for=\"tab in safeTabs\"\n :key=\"tab.id\"\n :value=\"tab.id\"\n @click=\"handleTabClick(tab.id)\"\n :class=\"cn('!flex !items-center !gap-2', preset.tabPaddingClass, preset.tabTextSizeClass)\"\n >\n <!-- 탭 아이콘 (있을 경우) / Tab Icon -->\n <JIcon \n v-if=\"tab.icon\" \n :name=\"tab.icon\" \n size=\"sm\"\n class=\"flex-shrink-0\"\n />\n \n <!-- 탭 레이블 / Tab Label -->\n <span class=\"flex-1 truncate\">{{ tab.label }}</span>\n \n <!-- 닫기 버튼 / Close Button (항상 표시) -->\n <button\n v-if=\"tab.closable\"\n type=\"button\"\n class=\"flex-shrink-0 h-3.5 w-3.5 rounded-sm hover:bg-destructive/10 hover:text-destructive transition-colors focus:outline-none focus:ring-2 focus:ring-ring flex items-center justify-center\"\n :aria-label=\"`${tab.label} 탭 닫기`\"\n @click=\"(e) => handleCloseTab(e, tab.id)\"\n >\n <X class=\"h-2.5 w-2.5\" />\n </button>\n </TabsTrigger>\n </TabsList>\n\n <!-- 탭 콘텐츠 영역 / Tab Contents -->\n <div class=\"flex-1 w-full overflow-auto\">\n <TabsContent\n v-for=\"tab in safeTabs\"\n :key=\"`content-${tab.id}`\"\n :value=\"tab.id\"\n class=\"h-full mt-0 data-[state=active]:flex data-[state=active]:flex-col\"\n >\n <!-- 슬롯 우선 / Slot First -->\n <slot :name=\"`content-${tab.id}`\" :tab=\"tab\">\n <!-- 동적 컴포넌트 렌더링 / Dynamic Component Rendering -->\n <component\n v-if=\"tab.component\"\n :is=\"tab.component\"\n v-bind=\"tab.props || {}\"\n />\n \n <!-- 기본 콘텐츠 / Default Content -->\n <div v-else class=\"p-4\">\n <p class=\"text-muted-foreground\">{{ tab.label }} 콘텐츠</p>\n </div>\n </slot>\n </TabsContent>\n </div>\n </Tabs>\n</template>\n\n<style scoped>\n/**\n * 탭 리스트 스타일 - 하단 보더 제거\n * Tab list styles without bottom border\n */\n:deep([role=\"tablist\"]:not(.ag-side-buttons)) {\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: thin;\n scrollbar-color: rgba(0, 0, 0, 0.2) transparent;\n background: hsl(var(--background));\n padding: 0 0.25rem;\n gap: 0.25rem;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar) {\n height: 6px;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-track) {\n background: transparent;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb) {\n background-color: rgba(0, 0, 0, 0.2);\n border-radius: 3px;\n}\n\n:deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb:hover) {\n background-color: rgba(0, 0, 0, 0.3);\n}\n\n/**\n * 다크모드에서 스크롤바 스타일\n * Scrollbar styles in dark mode\n */\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb) {\n background-color: rgba(255, 255, 255, 0.2);\n}\n\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)::-webkit-scrollbar-thumb:hover) {\n background-color: rgba(255, 255, 255, 0.3);\n}\n\n.dark :deep([role=\"tablist\"]:not(.ag-side-buttons)) {\n scrollbar-color: rgba(255, 255, 255, 0.2) transparent;\n}\n\n/**\n * 탭 버튼 스타일 - 명확한 구분\n * Tab button styles - clear distinction\n */\n:deep([role=\"tab\"]) {\n position: relative;\n padding: 0.25rem 0.5rem;\n min-height: 1.75rem;\n border-radius: 0.375rem 0.375rem 0 0;\n transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;\n border: 1px solid transparent;\n border-bottom: none;\n}\n\n/**\n * Minimal 스타일 탭 버튼 - JSidebarAdvanced와 높이 맞춤\n */\n:deep([role=\"tablist\"][class*=\"p-0.5\"] [role=\"tab\"]) {\n padding: 0.25rem 0.5rem;\n}\n\n/**\n * 비활성 탭 - 명확하게 구분\n * Inactive tabs - clear distinction\n */\n:deep([role=\"tab\"][data-state=\"inactive\"]) {\n background: hsl(var(--muted) / 0.2);\n color: hsl(var(--muted-foreground));\n border-top: 1px solid hsl(var(--border) / 0.4);\n border-left: 1px solid hsl(var(--border) / 0.4);\n border-right: 1px solid hsl(var(--border) / 0.4);\n}\n\n:deep([role=\"tab\"][data-state=\"inactive\"]:hover) {\n background: hsl(var(--muted) / 0.4);\n color: hsl(var(--foreground));\n}\n\n/**\n * 다크모드에서 비활성 탭 - 다크 배경색 사용\n * Inactive tabs in dark mode - use dark background\n */\n.dark :deep([role=\"tab\"][data-state=\"inactive\"]) {\n background: hsl(var(--secondary));\n color: hsl(var(--secondary-foreground));\n border-top: 1px solid hsl(var(--border) / 0.5);\n border-left: 1px solid hsl(var(--border) / 0.5);\n border-right: 1px solid hsl(var(--border) / 0.5);\n}\n\n.dark :deep([role=\"tab\"][data-state=\"inactive\"]:hover) {\n background: hsl(var(--muted));\n color: hsl(var(--muted-foreground));\n}\n\n/**\n * 활성 탭 - 강조된 스타일\n * Active tab - emphasized style\n */\n:deep([role=\"tab\"][data-state=\"active\"]) {\n background: hsl(var(--background));\n color: hsl(var(--foreground));\n font-weight: 500;\n border-top: 2px solid hsl(var(--primary));\n border-left: 1px solid hsl(var(--border));\n border-right: 1px solid hsl(var(--border));\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n z-index: 1;\n}\n\n/**\n * 다크모드에서 활성 탭 - 배경색 명확히 구분\n * Active tab in dark mode - clear background distinction\n */\n.dark :deep([role=\"tab\"][data-state=\"active\"]) {\n background: hsl(var(--card));\n color: hsl(var(--card-foreground));\n border-top: 2px solid hsl(var(--primary));\n border-left: 1px solid hsl(var(--border));\n border-right: 1px solid hsl(var(--border));\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n}\n\n</style>\n"],"names":["props","__props","emit","__emit","safeTabs","computed","internalActiveId","ref","isHandlingEvent","watch","newValue","newTabs","t","handleTabValueChange","value","stringValue","nextTick","handleTabClick","tabId","handleCloseTab","rootClasses","cn","STYLE_PRESETS","preset","listClasses","_createBlock","_unref","Tabs","_createVNode","TabsList","_createElementBlock","_Fragment","_renderList","tab","TabsTrigger","$event","_normalizeClass","JIcon","_createElementVNode","_hoisted_1","_toDisplayString","e","X","_hoisted_3","TabsContent","_renderSlot","_ctx","_openBlock","_resolveDynamicComponent","_mergeProps","_hoisted_4","_hoisted_5"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA6BA,UAAMA,IAAQC,GAIRC,IAAOC,GAMPC,IAAWC,EAAS,MACjB,MAAM,QAAQL,EAAM,IAAI,IAAIA,EAAM,OAAO,CAAA,CACjD,GAMKM,IAAmBC;AAAA,MACvBP,EAAM,gBAAgBI,EAAS,MAAM,SAAS,IAAIA,EAAS,MAAM,CAAC,GAAG,KAAK,OAAO;AAAA,IAAA;AAOnF,QAAII,IAAkB;AAMtB,IAAAC,EAAM,MAAMT,EAAM,aAAa,CAACU,MAAa;AAC3C,MAAIA,MAAa,UAAaA,MAAaJ,EAAiB,UAC1DA,EAAiB,QAAQI;AAAA,IAE7B,GAAG,EAAE,WAAW,IAAM,GAMtBD,EAAML,GAAU,CAACO,MAAY;AAC3B,MAAI,CAACX,EAAM,eAAeW,EAAQ,SAAS,KAAK,CAACA,EAAQ,KAAK,CAAAC,MAAKA,EAAE,OAAON,EAAiB,KAAK,KAAKK,EAAQ,CAAC,MAC9GL,EAAiB,QAAQK,EAAQ,CAAC,EAAE;AAAA,IAExC,CAAC;AAMD,UAAME,IAAuB,CAACC,MAA2B;AACvD,UAAIN,EAAiB;AAErB,YAAMO,IAAc,OAAOD,CAAK;AAChC,MAAIC,MAAgBT,EAAiB,UACnCE,IAAkB,IAClBF,EAAiB,QAAQS,GACzBb,EAAK,sBAAsBa,CAAW,GACtCb,EAAK,aAAaa,CAAW,GAE7BC,EAAS,MAAM;AACb,QAAAR,IAAkB;AAAA,MACpB,CAAC;AAAA,IAEL,GAMMS,IAAiB,CAACC,MAAkB;AAGxC,MAAIV,KAAmBU,MAAUZ,EAAiB,UAElDE,IAAkB,IAClBF,EAAiB,QAAQY,GACzBhB,EAAK,sBAAsBgB,CAAK,GAChChB,EAAK,aAAagB,CAAK,GAEvBF,EAAS,MAAM;AACb,QAAAR,IAAkB;AAAA,MACpB,CAAC;AAAA,IACH,GAMMW,IAAiB,CAAC,GAAUD,MAAkB;AAClD,QAAE,gBAAA,GACFhB,EAAK,YAAYgB,CAAK;AAAA,IACxB,GAMME,IAAcf,EAAS,MACpBgB,EAAG,+BAA+BrB,EAAM,KAAK,CACrD,GAKKsB,IAID;AAAA,MACH,SAAS;AAAA,QACP,iBAAiB;AAAA,QACjB,kBAAkB;AAAA,QAClB,kBAAkB;AAAA,MAAA;AAAA,MAEpB,SAAS;AAAA,QACP,iBAAiB;AAAA,QACjB,kBAAkB;AAAA,QAClB,kBAAkB;AAAA,MAAA;AAAA,IACpB,GAGIC,IAASlB,EAAS,MACfiB,EAActB,EAAM,SAAS,KAAKsB,EAAc,OACxD,GAMKE,IAAcnB,EAAS,MACpBgB,EAAG,wBAAwBE,EAAO,MAAM,kBAAkBvB,EAAM,SAAS,CACjF;2BAICyB,EA+DOC,EAAAC,CAAA,GAAA;AAAA,MA9DJ,eAAarB,EAAA;AAAA,MACb,uBAAoBO;AAAA,MACrB,aAAY;AAAA,MACX,SAAOO,EAAA,KAAW;AAAA,IAAA;iBAGnB,MA8BW;AAAA,QA9BXQ,EA8BWF,EAAAG,CAAA,GAAA;AAAA,UA9BA,SAAOL,EAAA,KAAW;AAAA,QAAA;qBAEzB,MAAuB;AAAA,oBADzBM,EA4BcC,GAAA,MAAAC,EA3BE5B,EAAA,OAAQ,CAAf6B,YADTR,EA4BcC,EAAAQ,CAAA,GAAA;AAAA,cA1BX,KAAKD,EAAI;AAAA,cACT,OAAOA,EAAI;AAAA,cACX,SAAK,CAAAE,MAAElB,EAAegB,EAAI,EAAE;AAAA,cAC5B,OAAKG,EAAEV,KAAE,8BAA+BH,EAAA,MAAO,iBAAiBA,EAAA,MAAO,gBAAgB,CAAA;AAAA,YAAA;yBAGxF,MAKE;AAAA,gBAJMU,EAAI,aADZR,EAKEY,GAAA;AAAA;kBAHC,MAAMJ,EAAI;AAAA,kBACX,MAAK;AAAA,kBACL,OAAM;AAAA,gBAAA;gBAIRK,EAAoD,QAApDC,GAAoDC,EAAnBP,EAAI,KAAK,GAAA,CAAA;AAAA,gBAIlCA,EAAI,iBADZH,EAQS,UAAA;AAAA;kBANP,MAAK;AAAA,kBACL,OAAM;AAAA,kBACL,cAAU,GAAKG,EAAI,KAAK;AAAA,kBACxB,SAAK,CAAGQ,MAAMtB,EAAesB,GAAGR,EAAI,EAAE;AAAA,gBAAA;kBAEvCL,EAAyBF,EAAAgB,CAAA,GAAA,EAAtB,OAAM,eAAa;AAAA,gBAAA;;;;;;;QAM5BJ,EAsBM,OAtBNK,GAsBM;AAAA,kBArBJb,EAoBcC,GAAA,MAAAC,EAnBE5B,EAAA,OAAQ,CAAf6B,YADTR,EAoBcC,EAAAkB,CAAA,GAAA;AAAA,YAlBX,KAAG,WAAaX,EAAI,EAAE;AAAA,YACtB,OAAOA,EAAI;AAAA,YACZ,OAAM;AAAA,UAAA;uBAGN,MAYO;AAAA,cAZPY,EAYOC,EAAA,QAAA,WAZiBb,EAAI,EAAE,MAAK,KAAAA,EAAA,GAAnC,MAYO;AAAA,gBATGA,EAAI,aADZc,KAAAtB,EAIEuB,EAFKf,EAAI,SAAS,GAFpBgB,EAIE;AAAA;;mBADQhB,EAAI,SAAK,CAAA,CAAA,GAAA,MAAA,EAAA,MAInBc,EAAA,GAAAjB,EAEM,OAFNoB,GAEM;AAAA,kBADJZ,EAAwD,KAAxDa,GAAwDX,EAApBP,EAAI,KAAK,IAAG,QAAI,CAAA;AAAA,gBAAA;;;;;;;;;;;"}
@@ -3,5 +3,5 @@
3
3
  for (const [t_key, t_val] of t_opts)
4
4
  t_merged[t_key] = t_val;
5
5
  return t_merged;
6
- };,u=t(e.default,[["__scopeId","data-v-28a4818e"]]);exports.default=u;
6
+ };,u=t(e.default,[["__scopeId","data-v-0b34d405"]]);exports.default=u;
7
7
  //# sourceMappingURL=JDynamicForm.vue.cjs.map
@@ -6,7 +6,7 @@ const m = (m_comp, m_opts) => {
6
6
  m_merged[m_key] = m_val;
7
7
  return m_merged;
8
8
  };
9
- const p = /* @__PURE__ */ m(o, [["__scopeId", "data-v-28a4818e"]]);
9
+ const p = /* @__PURE__ */ m(o, [["__scopeId", "data-v-0b34d405"]]);
10
10
  export {
11
11
  p as default
12
12
  };