{"version":3,"file":"JSpinner.vue.cjs","sources":["../../../../src/components/atoms/JSpinner.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { Loader2Icon } from 'lucide-vue-next'\r\nimport { cn } from '@/lib/utils'\r\n\r\ntype StyleType =\r\n  | 'default'   // 기본 스타일\r\n  | 'primary'   // 강조 스타일 (파랑)\r\n  | 'success'   // 성공 스타일 (초록)\r\n  | 'warning'   // 경고 스타일 (주황)\r\n  | 'danger'    // 위험 스타일 (빨강)\r\n\r\ntype LabelPosition =\r\n  | 'right'     // 오른쪽 (기본)\r\n  | 'left'      // 왼쪽\r\n  | 'top'       // 위쪽\r\n  | 'bottom'    // 아래쪽\r\n\r\nconst props = withDefaults(\r\n  defineProps<{\r\n    size?: 'xs' | 'sm' | 'md' | 'lg'\n    class?: string\r\n    /** 스타일 프리셋 */\r\n    styletype?: StyleType\r\n    /** 스피너 원의 테두리 두께 */\r\n    thickness?: number\r\n    /** 스피너 옆에 표시될 텍스트 */\r\n    label?: string\r\n    /** 라벨 위치 */\r\n    labelPosition?: LabelPosition\r\n  }>(),\r\n  {\n    size: 'sm',\n    styletype: 'default',\n    thickness: 2,\n    label: '',\r\n    labelPosition: 'right',\r\n  },\r\n)\r\n\r\n/**\r\n * 크기별 클래스 매핑\r\n */\r\nconst SIZE_CLASSES = {\n  xs: 'size-2.5',\n  sm: 'size-3',\n  md: 'size-4', \n  lg: 'size-5',\n}\n\r\n/**\r\n * 라벨 위치별 레이아웃 클래스 매핑\r\n */\r\nconst LABEL_POSITION_CLASSES = {\r\n  right: 'flex-row items-center gap-2',\r\n  left: 'flex-row-reverse items-center gap-2',\r\n  top: 'flex-col items-center gap-2',\r\n  bottom: 'flex-col-reverse items-center gap-2',\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  primary: { \r\n    class: 'text-blue-500',\r\n  },\r\n  success: { \r\n    class: 'text-green-500',\r\n  },\r\n  warning: { \r\n    class: 'text-amber-500',\r\n  },\r\n  danger: { \r\n    class: 'text-red-500',\r\n  },\r\n}\r\n\r\n/** 최종 바인딩: 직접 넘긴 props가 있으면 styletype 기본값과 병합 */\r\nconst spinnerProps = computed(() => {\r\n  const preset = STYLE_PRESETS[props.styletype!]\r\n  const finalSize = props.size || 'md'\r\n  const sizeClass = SIZE_CLASSES[finalSize as keyof typeof SIZE_CLASSES]\r\n  const finalClass = cn('animate-spin', sizeClass, preset.class, props.class)\r\n  \r\n  return {\r\n    class: finalClass,\r\n    style: {\r\n      strokeWidth: props.thickness,\r\n    },\r\n    'aria-label': '처리 중',\r\n  }\r\n})\r\n\r\n/** 컨테이너 레이아웃 클래스 */\r\nconst containerClass = computed(() => {\r\n  const position = props.labelPosition || 'right'\r\n  return cn('inline-flex', LABEL_POSITION_CLASSES[position as keyof typeof LABEL_POSITION_CLASSES])\r\n})\r\n</script>\r\n\r\n<template>\r\n  <div :class=\"containerClass\">\r\n    <Loader2Icon\r\n      role=\"status\"\r\n      v-bind=\"spinnerProps\"\r\n    />\r\n    <span v-if=\"props.label\" class=\"text-sm\">{{ props.label }}</span>\r\n  </div>\r\n</template>\r\n"],"names":["props","__props","SIZE_CLASSES","LABEL_POSITION_CLASSES","STYLE_PRESETS","spinnerProps","computed","preset","finalSize","sizeClass","cn","containerClass","position","_createElementBlock","_createVNode","_unref","_mergeProps","_openBlock","_hoisted_1","_toDisplayString"],"mappings":"uZAkBA,MAAMA,EAAQC,EAyBRC,EAAe,CACnB,GAAI,WACJ,GAAI,SACJ,GAAI,SACJ,GAAI,QAAA,EAMAC,EAAyB,CAC7B,MAAO,8BACP,KAAM,sCACN,IAAK,8BACL,OAAQ,qCAAA,EAMJC,EAAsD,CAC1D,QAAS,CAAE,MAAO,EAAA,EAClB,QAAS,CACP,MAAO,eAAA,EAET,QAAS,CACP,MAAO,gBAAA,EAET,QAAS,CACP,MAAO,gBAAA,EAET,OAAQ,CACN,MAAO,cAAA,CACT,EAIIC,EAAeC,EAAAA,SAAS,IAAM,CAClC,MAAMC,EAASH,EAAcJ,EAAM,SAAU,EACvCQ,EAAYR,EAAM,MAAQ,KAC1BS,EAAYP,EAAaM,CAAsC,EAGrE,MAAO,CACL,MAHiBE,EAAAA,GAAG,eAAgBD,EAAWF,EAAO,MAAOP,EAAM,KAAK,EAIxE,MAAO,CACL,YAAaA,EAAM,SAAA,EAErB,aAAc,MAAA,CAElB,CAAC,EAGKW,EAAiBL,EAAAA,SAAS,IAAM,CACpC,MAAMM,EAAWZ,EAAM,eAAiB,QACxC,OAAOU,KAAG,cAAeP,EAAuBS,CAA+C,CAAC,CAClG,CAAC,8BAICC,EAAAA,mBAMM,MAAA,CANA,uBAAOF,EAAA,KAAc,CAAA,GACzBG,EAAAA,YAGEC,EAAAA,qBAHFC,EAAAA,WAGE,CAFA,KAAK,QAAA,EACGX,EAAA,KAAY,EAAA,KAAA,EAAA,EAEVL,EAAM,OAAlBiB,EAAAA,UAAA,EAAAJ,EAAAA,mBAAiE,OAAjEK,EAAiEC,EAAAA,gBAArBnB,EAAM,KAAK,EAAA,CAAA"}