




<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="utf-8">
        <meta name="google-site-verification" content="HKNLTFhldNXak0mlyFd5GVhbJdX-aL8QFJEuAtBrfc8" />
    

                <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=10.0, user-scalable=yes">
                <title>비아그라 구매 - 비아클럽</title>
    <meta name="title" content="비아그라 구매 - 비아클럽">
    <meta name="description" content="비아그라 정품 100% 안전 구매 가이드. 발기부전 치료제 비아그라·시알리스 원리, 부작용, 복용법부터 병원 처방전 없이도 합법적으로 구매할 수 있는 방법까지 솔직 후기 포함 상세 정리. 가짜 약물 피해 없이 믿을 수 있는 국내 정품만 엄선된 약국 리스트, 의사 상담 필수 이유, 2026년 최신 한국 식약처 규정 완벽 정리. 지금 바로 안전하게 시작하세요!">
    <meta name="keywords" content="비아그라, 비아그라 구매, 시알리스, 시알리스 구매, 비아그라 구매 사이트, 시알리스 구매 사이트, 정품 비아그라, 정품 시알리스, 비아그라 가격, 시알리스 가격, 비아클럽">
    <meta property="og:title" content="비아그라 구매 - 비아클럽">
    <meta property="og:type" content="website" />
    <meta property="og:url" content="https://4dailyblogs.com/sitemap.xml" />
    <meta property="og:description" content="비아그라 정품 100% 안전 구매 가이드. 발기부전 치료제 비아그라·시알리스 원리, 부작용, 복용법부터 병원 처방전 없이도 합법적으로 구매할 수 있는 방법까지 솔직 후기 포함 상세 정리. 가짜 약물 피해 없이 믿을 수 있는 국내 정품만 엄선된 약국 리스트, 의사 상담 필수 이유, 2026년 최신 한국 식약처 규정 완벽 정리. 지금 바로 안전하게 시작하세요!">


            <meta property="og:image" content="https://4dailyblogs.com/image?code=fl6a30e0e4e245b">
        <meta property="og:image:type" content="image/png">
        <meta property="og:image:alt" content="main image">
        
            <link rel="shortcut icon" href="https://4dailyblogs.com/image?code=fl68f83b6c2a62a">
        
    <meta name="msvalidate.01" content="0AAD390BEF95757D93E8C8411F739A96" />
    <link rel="canonical" href="https://4dailyblogs.com/">        <link rel="apple-touch-icon-precomposed" sizes="57x57" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=57&height=57">
        <link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=72&height=72">
        <link rel="apple-touch-icon-precomposed" sizes="60x60" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=60&height=60">
        <link rel="apple-touch-icon-precomposed" sizes="76x76" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=76&height=76">
        <link rel="apple-touch-icon-precomposed" sizes="114x114" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=114&height=114">
        <link rel="apple-touch-icon-precomposed" sizes="120x120" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=120&height=120">
        <link rel="apple-touch-icon-precomposed" sizes="144x144" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=144&height=144">
        <link rel="apple-touch-icon-precomposed" sizes="152x152" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=152&height=152">

        <link rel="icon" type="image/png" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=16&height=16" sizes="16x16">
        <link rel="icon" type="image/png" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=32&height=32" sizes="32x32">
        <link rel="icon" type="image/png" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=96&height=96" sizes="96x96">
        <link rel="icon" type="image/png" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=128&height=128" sizes="128x128">
        <link rel="icon" type="image/png" href="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=196&height=196" sizes="196x196">

        <meta name="msapplication-TileImage" content="https://4dailyblogs.com/image?code=fl68f83bbf2f30e">
        <meta name="msapplication-square70x70logo" content="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=70&height=70">
        <meta name="msapplication-square150x150logo" content="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=150&height=150">
        <meta name="msapplication-square310x150logo" content="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=610&height=150">
        <meta name="msapplication-square310x310logo" content="https://4dailyblogs.com/image/thumbnail?code=fl68f83bbf2f30e&width=310&height=310">
        

    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/bootstrap/css/bootstrap.css">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/bootstrap/css/bootstrap-theme.css">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/font_awsome/css/all.css">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/font_awsome/css/font-awesome.min.css">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/ionicons-2.0.1/css/ionicons.min.css">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/jquery.ui/jquery-ui.css">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/jquery.bxslider/jquery.bxslider.css">

    <link rel="stylesheet" href="https://4dailyblogs.com/file/css/common.css?v=202604101" media="all">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/css/common.m.css?v=20230721" media="all and (max-width:1050px)">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/css/common.s.css?v=20240325" media="all and (max-width:768px)">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/css/common.xs.css?v=20230721" media="all and (max-width:415px)">

    <link rel="stylesheet" href="https://4dailyblogs.com/file/css/site.css?v=20230721" media="all">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/froala/css/froala_style.css">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/froala/css/froala_editor.pkgd.css?edit=20240214">
    <link id="_css_common" rel="stylesheet" href="https://4dailyblogs.com/file/css/common.css?v=2" />
    <link rel="stylesheet" href="https://4dailyblogs.com/file/css/skins/all.css?v=1.0.2" />

    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/jquery.datetimepicker/jquery.datetimepicker.min.css" />

    <link rel="stylesheet" href="https://4dailyblogs.com/file/css/jaydream_widget.css?v=20260629_widget_placeholder_board01" media="all">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/css/jaydream.css" media="all">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/css/bs5_custom.css" media="all">

    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />

        
    <script type="text/javascript" src="https://4dailyblogs.com/file/js/jQuery.js"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/jquery.icheck/icheck.min.js"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/jquery.ui/jquery-ui.js"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/bootstrap/js/bootstrap.js"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/jquery.bxslider/jquery.bxslider.js"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/jquery.file.upload/js/jquery.fileupload.js"></script>

    <script type="text/javascript" src="https://4dailyblogs.com/file/js/common.js?v=20240321__"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/js/board.js?v=20240806"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/js/input.js?v=20240626"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/js/MEMBER.js?v=20241014"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/js/wysiwyg.js?v=20260507"></script>

    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/froala/js/froala_editor.pkgd.min.js"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/froala/js/languages/ko.js"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/jquery.datetimepicker/jquery.datetimepicker.min.js"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/imageMaps/jquery.rwdImageMaps.js"></script>

    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/email-autocomplete/0.1.3/jquery.email-autocomplete.min.js"></script>
    <script type="text/javascript" src="https://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
    <!--<script src="https://cdnjs.cloudflare.com/ajax/libs/jQuery-rwdImageMaps/1.6/jquery.rwdImageMaps.min.js"></script>-->

    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/aos/aos.js"></script>

    <!-- bootstrap-datetimepicker -->
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/bootstrap-datetimepicker/bootstrap-datetimepicker.css" />
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/bootstrap-datetimepicker/moment.min.js"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/bootstrap-datetimepicker/ko.js"></script>
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js"></script>

    <script src="https://blueimp.github.io/JavaScript-Templates/js/tmpl.min.js"></script>

    <!-- slick -->
    <link rel="stylesheet" type="text/css" href="https://4dailyblogs.com/file/plugin/slick/slick.css"/>
    <link rel="stylesheet" type="text/css" href="https://4dailyblogs.com/file/plugin/slick/slick-theme.css"/>
    <script type="text/javascript" src="https://4dailyblogs.com/file/plugin/slick/slick.min.js"></script>

    
    <!--[if lt IE 9]>
    <script src="https://4dailyblogs.com/file/js/html5shiv.js" type="text/javascript"></script>
    <script src="https://4dailyblogs.com/file/js/respond.js" type="text/javascript"></script>
    <![endif]-->
    <script type="text/javascript">
        var csrf_token = "c3273188bc5ebf24af1f8d75303b00cf";
        var post_code = "";
        var is_mobile = false;
    </script>

    <!-- api -->
    <!--<script type="text/javascript" src="//maps.googleapis.com/maps/api/js?language=ko&libraries=places&key=AIzaSyAvTyw5BBeKAOcqLyAf7DDEk0hGsKDU9Eo"></script>-->

    <!-- codemirror js -->
    <script src="https://4dailyblogs.com/file/plugin/codemirror/lib/codemirror.js"></script>
    <!-- codemirror mode -->
    <script src="https://4dailyblogs.com/file/plugin/codemirror/mode/sql/sql.js"></script>
    <script src="https://4dailyblogs.com/file/plugin/codemirror/mode/xml/xml.js"></script>
    <script src="https://4dailyblogs.com/file/plugin/codemirror/mode/javascript/javascript.js"></script>
    <script src="https://4dailyblogs.com/file/plugin/codemirror/mode/css/css.js"></script>
    <script src="https://4dailyblogs.com/file/plugin/codemirror/mode/htmlmixed/htmlmixed.js"></script>
    <!-- codemirror addon -->
    <script src="https://4dailyblogs.com/file/plugin/codemirror/addon/edit/closetag.js"></script>
    <script src="https://4dailyblogs.com/file/plugin/codemirror/addon/hint/show-hint.js"></script>
    <script src="https://4dailyblogs.com/file/plugin/codemirror/addon/hint/xml-hint.js"></script>
    <script src="https://4dailyblogs.com/file/plugin/codemirror/addon/hint/html-hint.js"></script>
    <script src="https://4dailyblogs.com/file/plugin/codemirror/addon/display/fullscreen.js"></script>

    <!-- codemirror css -->
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/codemirror/lib/codemirror.css">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/codemirror/addon/hint/show-hint.css">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/codemirror/addon/display/fullscreen.css">
    <!-- <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/codemirror/doc/docs.css"> -->
    <!-- codemirror theme -->
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/codemirror/theme/darcula.css">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/codemirror/theme/juejin.css">
    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/codemirror/theme/cobalt.css">


    <link rel="stylesheet" href="https://4dailyblogs.com/file/plugin/aos/aos.css">
</head>

<style>
    .popup{
        min-width:200px;
        min-height:200px;
        _max-height:300px;
        position:absolute;
        background:#fff; z-index:9999;
        top:150px;
        _left:1000px;
        left:50%;
        border:1px solid #ddd;
    }
    .popup img{
        width:100%;

    }
    .popup .popup_bottom{
        width:100%;
        background:#333;
        color:#fff;
        position:absolute;
        bottom:0px;
        display:flex;
        bottom:-30px;
    }
    .popup .popup_bottom div{
        display:inline-block;
        text-align:center;
        width:50%;
        padding:5px;
        cursor:pointer;
    }
    @media (max-width:1070px){
        .popup{
            top:150px !important;
            left:0px !important;
            margin-left:0 !important;
            width:100%;
            max-width:400px;
        }

    }
</style>
<script>
    $(function(){
        $('.popup').draggable();
        $('.popup_close').on('click',function(){
            $(this).closest('.popup').hide();
        });
        $('.popup_close24').on('click',function(){
            setCookie($(this).closest('.popup').data('code'),'','1');
            $(this).closest('.popup').hide();
        });
    });
</script>

<style>
    .jdmt-render-wrap { display:none; }
    body._mbv .jdmt-render-wrap { display:block !important; }
    body._mbv .jdmt-legacy-nav,
    body._mbv .jdmt-legacy-mobile { display:none !important; }
    @media (max-width: 767px) {
        .jdmt-render-wrap { display:block !important; }
        .jdmt-legacy-nav,
        .jdmt-legacy-mobile { display:none !important; }
    }
</style>
<div id="jd_header" class="jdmt-render-wrap">
    <header-mobile-top site_code="st68f59208a5890" is_design="N"></header-mobile-top>
</div>
<style>
    /* expand가 없을 때는 nav 자체를 중앙 정렬하고 최대 너비 제한 */
    .user.navbar {
        border: 0;
        border-radius: 0;
        box-shadow: none;
        min-height:40px;
        width: auto;
        max-width: 1280px;
        margin-left: auto;
        margin-right: auto;
    }
    /* expand가 있을 때만 전체 너비로 확장 */
    .user.navbar.expand {
        width: 100%;
        max-width: none;
    }

    .user.navbar .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { color: inherit; }
    .user.navbar .navbar-nav > li > a:focus:not(:focus-visible) { outline: none; text-decoration: none; }

    .user.navbar .navbar-nav li { list-style: none; cursor: pointer; }
        .user.navbar .navbar-nav li a { display: block; font-size: 15px _important; text-align:left; }
        .user.navbar .navbar-nav li a:hover { color: inherit; }
    .user.navbar .navbar-nav > li ul { padding: 0; background: #333; color: #333; }
    .user.navbar .navbar-nav > li { position: relative; }
    .user.navbar .navbar-nav > li > .dropdown {
        position: absolute;
        top: 100%;
        left: 0;
        z-index: 1000;
        display: none;

        background: #333333;

        }
    .user.navbar .navbar-nav > li .dropdown .dropdown {
        position: absolute;
        top: 0;
        left: 100%;
        right: auto;
        z-index: 1100;
        min-width: 160px;
        }
    .user.navbar .navbar-nav > li .dropdown li {
        width: 100%;
        position: relative;
        box-sizing: border-box;
        min-width: 160px;
    padding-top:20px !important;padding-bottom:20px !important;    }

    .user.navbar .navbar-nav > li .dropdown > li:first-child {

    }
    .user.navbar .navbar-nav li:hover > .dropdown {
        display: block;
    }
    .user.navbar .navbar-nav li .dropdown li {
        position: relative;
        box-sizing: border-box;
    }
    .user.navbar .navbar-nav li .dropdown li > .dropdown {
        position: absolute;
        top: 0;
        left: 100%;
        z-index: 1100;
        display: none;
        min-width: 120px;
        background: #333333;
        box-sizing: border-box;
        }
    .user.navbar .navbar-nav li .dropdown li:hover > .dropdown {
        display: block;
    }
    .user.navbar .navbar-nav li .dropdown li > a {
        display: block !important;
        width: 100% !important;
        white-space: nowrap !important;
        padding-top: 4px !important;
        padding-bottom: 4px !important;
        padding-left: 8px !important;
        padding-right: 8px !important;
    font-family:SBAggro,sans-serif !important;font-size:25px !important;font-weight:400 !important;color:#333333 !important;    }
    .user.navbar .navbar-nav li .dropdown li > .dropdown {
        transition: opacity 0.12s ease !important;
        opacity: 1 !important;
        pointer-events: auto !important;
    }
    .user.navbar .navbar-nav li .dropdown { z-index: 1000 !important; }
    .user.navbar .navbar-nav li .dropdown li .dropdown { z-index: 1100 !important; }
    @media (max-width: 480px) {
        .user.navbar .navbar-nav li .dropdown li > .dropdown {
            left: 0 !important;
            top: 100% !important;
        }
    }

    /* Hover styles for dropdown items */
    .user.navbar .navbar-nav li .dropdown li:hover > a,
    .user.navbar .navbar-nav li .dropdown li > a:hover,
    .user.navbar .navbar-nav li .dropdown li:hover {
    color:#333333 !important;font-weight:700 !important;background:#333333 !important;font-family:SBAggro,sans-serif !important;font-size:25px !important;    }

    /* =====================================================
     * Active (현재 페이지) 스타일 - dropdown
     * ===================================================== */
    .user.navbar .navbar-nav li.active > a { color:#ffffff !important;background:#2176bd !important;font-weight:400 !important; }.user.navbar .navbar-nav li.active { color:#ffffff !important;background:#2176bd !important;font-weight:400 !important; }
</style>

<script>
    document.addEventListener('DOMContentLoaded', function() {
        try {
            var navId = "hd68f59208a658f";
            var nav = document.getElementById(navId);
            if (!nav) {
                nav = document.querySelector('.user.navbar');
            }
            if (!nav) return;

            function createIndicator(character, rightOffset) {
                var span = document.createElement('span');
                span.className = 'child-indicator';
                span.setAttribute('aria-hidden', 'true');
                span.textContent = character;
                span.style.cssText = 'position:absolute; right:' + (rightOffset || '8px') + '; top:50%; transform:translateY(-50%); pointer-events:none; font-size:11px; color:inherit;font-family: IBMPlexSansKR';
                return span;
            }

            nav.querySelectorAll('.navbar-nav li').forEach(function(li) {
                var parentNav = li.closest('.navbar-nav');
                if (parentNav && li.parentElement === parentNav) return;

                if (li.querySelector('.dropdown') || li.querySelector('ul')) {
                    var a = li.querySelector(':scope > a, :scope > .submenu-main-link > a');
                    if (a && !a.querySelector('.child-indicator')) {
                        var comp = window.getComputedStyle(a);
                        if (comp.position === 'static' || !comp.position) {
                            a.style.position = 'relative';
                        }
                        var pr = parseInt(comp.paddingRight, 10) || 0;
                        var needed = 28;
                        if (pr < needed) a.style.paddingRight = needed + 'px';

                        var depth = 0;
                        var p = li;
                        while (p && p !== nav) {
                            if (p.tagName && p.tagName.toLowerCase() === 'li') depth++;
                            p = p.parentElement;
                        }
                        var indicatorChar = depth > 2 ? '»' : '›';
                        var rightOffset = depth > 2 ? '14px' : '10px';
                        a.appendChild(createIndicator(indicatorChar, rightOffset));
                    }
                }
            });

        } catch (e) {
            console && console.warn && console.warn('child indicator init error', e);
        }
    });
</script>

<style>
    .child-indicator {
        font-family: inherit;
        font-size: 0.95em;
        color: #333333;
        margin-left: 6px;
        pointer-events: none;
    }
    .user.navbar .navbar-nav li:hover > a .child-indicator,
    .user.navbar .navbar-nav li > a:focus .child-indicator {
    color:#333333 !important;    }

    .user.navbar.navbar-fixed-top { position: absolute; z-index: 1; }

    .user.navbar .navbar-nav > li > a { line-height: 40px; margin:30px 0px; padding-top: 0; padding-bottom: 0; font-size: 15px; text-shadow: none;
        _font-family: 'SBAggro', sans-serif;
    }
    .user.navbar .navbar-nav  li  a:hover{font-weight:bold;}
    .user.navbar .navbar-text { font-size: 18px; margin: 0;  }

    .user.navbar .navbar-brand-new > img { width: auto !important; max-height: 100px; vertical-align:middle; }

    .m_scrollmenu_sub {border-top: 0px solid #e7e7e7 !important;}

    .section,.section h1,.section h2,.section h3,.section h4,.section h5,.section h6,.section input,.section textarea,.nav li a,
    .write-box, #side_menu
    {
        font-family:SBAggro, sans-serif;
    }



    </style>


<style>
        body {
        }
        body._mbv {
        padding-top: 0 !important;
    }
    @media (max-width: 767px) {
        body {
            padding-top: 0 !important;
        }
    }
    </style>


<nav id="hd68f59208a658f" class="user navbar navbar-default jdmt-legacy-nav hd68f59208a658f   contextnav"  style="background: #ffffff !important; background-image: none !important;height: 80px;;margin-top:0px !important;padding-right:0px !important;padding-left:0px !important;">

    <div class="">
                <div class="navbar-header">

            <style>
                #side_menu { display: block; position: fixed; top: 0; right: 0; width: 100%; height: 100%; overflow: hidden; z-index: 101; margin: 0; background-color: rgba( 0, 0, 0, 0.5 );  display: none; }
                #side_menu .side_wr { width: 300px; background: #222222; opacity: 1; position: absolute; top: 0; right: 0; box-shadow: 0 0 10px #000; overflow-x: hidden; height: 100%; overflow-y: auto; }
                #side_menu .side_wr .side_inner_rel { position: relative; height: 100%; display: block; overflow: scroll; }
                #side_menu .side_wr .side_inner_rel .m_close {
                    position: absolute;
                    overflow: hidden;
                    left: 0;
                    top: 0;
                    width: 48px;
                    height: 48px;
                    display: flex;
                    align-items: right;
                    justify-content: flex-start;
                    z-index: 10;
                }
                #side_menu .side_wr .side_inner_rel .m_close .fa {
                    color: #fff;
                    font-size: 20px;
                    margin-left: 12px;
                    margin-right: 0;
                    margin-top: 0;
                    font-weight: 400;
                }
                #side_menu .side_wr .side_inner_rel .m_login { text-align: center; margin: 10px; }
                #side_menu .side_wr .side_inner_rel .m_login span { margin: 0 5px;}
                                #side_menu_box { padding: 15px 0; line-height: 30px;  }
                #side_menu_box ul { padding: 0;}
                #side_menu_box li { list-style: none; display: block; position: relative; padding-left: 20px;}
                #side_menu_box li a { display: block; }
                #side_menu_box li a:hover { color: inherit; text-decoration: none; }
                .navbar-toggle { margin-top: 33px; }

                /* Jay N11 S*/
                .user-browser #side_menu,
                .user-browser .user-header #side_menu {
                    position: absolute;
                }

                body > #side_menu {
                    position: fixed;
                }
                /* Jay N11 E*/
            </style>
            <div class="" style="display:flex;align-items:center;justify-content:space-between;width:100%;">
                
                                    <button type="button" class="navbar-toggle" id="" aria-label="open menu" style="margin:0;margin-top:10px;padding:6px 10px;border:none;background:none;margin-right:30px;">
                                            </button>
                
                <a class="navbar-brand-new" href="/" style="display:flex;align-items:center;height:80px;margin-top:0px !important;">
                                                                <img class="logo-image" src="/image?code=fl68f5a3efc432c" style="height: 40px; display:block;">
                                                            
                                                        </a>

                                                                        <button type="button" class="navbar-toggle" id="btn_side" aria-label="open menu" style="margin:0;margin-top:10px;padding:6px 10px;border:none;background:none;">
                                                                                <span class="icon-bar" style="display:block;width:15px;height:px;background:#858585;margin:4px 0px;"></span>
                            <span class="icon-bar" style="display:block;width:15px;height:px;background:#858585;margin:4px 0px;"></span>
                            <span class="icon-bar" style="display:block;width:15px;height:px;background:#858585;margin:4px 0px;"></span>
                                            </button>
                            </div>

        </div>

        <div class="hidden-xs">
                        <ul class="navbar-nav menu navbar-right" style="margin:0; padding:0; display:flex; align-items:center;gap:25px;height:80px;">
                            </ul>
        </div>

        <!-- 서브메뉴 풀다운 방식 -->
        <div class="submenu-pulldown-container" style="padding-top:30px;clear:both; width:auto; display:none; margin-top:0; background:#333; color:#fff; position:absolute; left:0; top:0; z-index:9999; text-align:center;
        font-family:SBAggro,sans-serif;font-size:25px;font-weight:400;color:#333333;background:#333333;gap:40px;                ">
            <div style="color:#fff;padding:12px;text-align:center;">서브메뉴가 없습니다.</div>        </div>
        <style>
            .submenu-main-link a:hover { color: #333333;font-weight:700;}.submenu-sub-link a:hover { color: #9b9b9b; }.submenu-main-link.active a, .submenu-main-link.active { color:#ffffff !important;font-weight:400 !important; }        </style>
        <script>
            document.addEventListener('DOMContentLoaded', function() {
                var navId = "hd68f59208a658f";
                var nav = document.getElementById(navId);
                var submenu = nav ? nav.querySelector('.submenu-pulldown-container') : null;
                if (!nav || !submenu) return;

                var navPos = window.getComputedStyle(nav).position;
                if (navPos === 'static' || !navPos) {
                    nav.style.position = 'relative';
                }
                submenu.style.position = 'absolute';
                submenu.style.left = '0';
                submenu.style.zIndex = '9999';

                function setSubmenuBelowNavbar() {
                    var h = nav.offsetHeight || Math.ceil(nav.getBoundingClientRect().height) || 0;
                    submenu.style.top = h + 'px';
                }

                setSubmenuBelowNavbar();
                window.addEventListener('resize', setSubmenuBelowNavbar, {passive:true});
                window.addEventListener('scroll', setSubmenuBelowNavbar, {passive:true});
            });
        </script>
        <script>
            document.addEventListener('DOMContentLoaded', function() {
                var nav = document.getElementById("hd68f59208a658f");
                if (!nav) return;
                var submenu = nav.querySelector('.submenu-pulldown-container');
                var target = nav.querySelector('.hidden-xs');
                if (!submenu || !target) return;

                function adjustSubmenu() {
                    var navRect = nav.getBoundingClientRect();
                    var targetRect = target.getBoundingClientRect();
                    var width = Math.max(0, targetRect.width);
                    var leftOffset = Math.max(0, targetRect.left - navRect.left);

                    submenu.style.width = width + 'px';
                    submenu.style.left = leftOffset + 'px';
                    submenu.style.marginLeft = '0';
                    submenu.style.marginRight = '0';
                    submenu.style.paddingLeft = '0';
                    submenu.style.paddingRight = '0';
                    submenu.style.boxSizing = 'border-box';
                }

                adjustSubmenu();
                window.addEventListener('resize', adjustSubmenu);
            });
        </script>
    </div>
</nav>

<!-- 햄버거 클릭 시 나오는 메뉴 -->
<div id="side_menu" class="jdmt-legacy-mobile" style="z-index:10001">
    <input type="hidden" id="hb_position" value="right">
    <div class="side_wr" style="background:#292929;">
        <div id="isroll_wrap" class="side_inner_rel">
            <div class="side_inner_abs">
                <div class="side-logo" style="text-align:center;padding-top:1px;padding-bottom:5px;background:#292929;">
                                    </div>
                
                                <div class="m_close">
                    <button type="button" class="btn btn-link" style="padding:0;margin:0;margin-top:-5px;background:none;border:none;" aria-label="Close side menu">
                        <i class="fa" style="color:#fff;font-size:10px;font-weight:700">✕</i>
                    </button>
                </div>
                <ul id="side_menu_box">
                    <script>
            function getDirectMobileDropdown(li){
                if(!li || !li.children) return null;
                for(var i = 0; i < li.children.length; i++){
                    var child = li.children[i];
                    if(child.tagName && child.tagName.toLowerCase() === "ul" && child.className.indexOf("dropdown") > -1){
                        return child;
                    }
                }
                return null;
            }
            function getDirectMobileAnchor(li){
                if(!li || !li.children) return null;
                for(var i = 0; i < li.children.length; i++){
                    var child = li.children[i];
                    if(child.tagName && child.tagName.toLowerCase() === "a"){
                        return child;
                    }
                }
                return null;
            }
            function setMobileMenuArrow(li, isOpen){
                var anchor = getDirectMobileAnchor(li);
                if(!anchor) return;
                var arrow = anchor.querySelector(".mb-arrow");
                if(arrow) arrow.innerHTML = isOpen ? "▴" : "▾";
            }
            function toggleMenu(el){
                var li = el.closest("li");
                if(!li) return false;
                var ul = getDirectMobileDropdown(li);
                if(!ul) return false;
                var currentDisplay = window.getComputedStyle ? window.getComputedStyle(ul).display : ul.style.display;
                var isClosed = ul.style.display === "none" || currentDisplay === "none";
                ul.style.display = isClosed ? "block" : "none";
                setMobileMenuArrow(li, isClosed);
                return false;
            }
            function setupMobileHamburgerMenu(){
                var root = document.getElementById("side_menu_box");
                if(!root) return;
                var items = root.getElementsByTagName("li");
                for(var i = 0; i < items.length; i++){
                    var li = items[i];
                    var submenu = getDirectMobileDropdown(li);
                    if(!submenu) continue;

                    submenu.style.display = "none";

                    var anchor = getDirectMobileAnchor(li);
                    if(!anchor || anchor.getAttribute("data-mobile-toggle-bound") === "1") continue;

                    anchor.setAttribute("data-mobile-toggle-bound", "1");
                    anchor.setAttribute("href", "javascript:void(0);");
                    anchor.removeAttribute("target");

                    var arrow = anchor.querySelector(".mb-arrow");
                    if(!arrow){
                        arrow = document.createElement("span");
                        arrow.className = "mb-arrow";
                        arrow.innerHTML = "▾";
                        arrow.setAttribute("style", "float:right;margin-right:-15px;font-size:15px;line-height:1;cursor:pointer;margin-top:8px");
                        anchor.appendChild(arrow);
                    }
                    arrow.onclick = function(e){
                        e.stopPropagation();
                        e.preventDefault();
                        return toggleMenu(this);
                    };
                    anchor.addEventListener("click", function(e){
                        e.preventDefault();
                        e.stopPropagation();
                        return toggleMenu(this);
                    });
                }
            }
            if(document.readyState === "loading"){
                document.addEventListener("DOMContentLoaded", setupMobileHamburgerMenu);
            } else {
                setupMobileHamburgerMenu();
            }
        </script>                </ul>
            </div>
        </div>
    </div>
</div>


<!-- 햄버거 메뉴 스크립트 -->
<script>
    document.addEventListener('DOMContentLoaded', function() {
        var sideMenu = document.getElementById('side_menu');
        var btnSides = document.querySelectorAll('#btn_side');
        var hbPosition = document.getElementById('hb_position');

        if (!sideMenu) return;

        var position = hbPosition ? hbPosition.value : 'right';
        var sideWr = sideMenu.querySelector('.side_wr');

        if (sideWr) {
            if (position === 'left') {
                sideWr.style.left = '0';
                sideWr.style.right = 'auto';
            } else {
                sideWr.style.right = '0';
                sideWr.style.left = 'auto';
            }
        }

        btnSides.forEach(function(btn) {
            btn.addEventListener('click', function(e) {
                e.preventDefault();
                e.stopPropagation();
                sideMenu.style.display = 'block';
                setTimeout(function() {
                    sideMenu.style.opacity = '1';
                }, 10);
            });
        });

        var mClose = sideMenu.querySelector('.m_close');
        if (mClose) {
            mClose.addEventListener('click', function(e) {
                e.preventDefault();
                e.stopPropagation();
                sideMenu.style.opacity = '0';
                setTimeout(function() {
                    sideMenu.style.display = 'none';
                }, 300);
            });
        }

        sideMenu.addEventListener('click', function(e) {
            if (e.target === sideMenu) {
                sideMenu.style.opacity = '0';
                setTimeout(function() {
                    sideMenu.style.display = 'none';
                }, 300);
            }
        });

        if (sideWr) {
            sideWr.addEventListener('click', function(e) {
                e.stopPropagation();
            });
        }
    });
</script>

<!-- 하단 여백 -->
    

<body class="premium" >
<style>
    /*@media (max-width: 767px) {*/
    /*    body:not(.footer) { padding-left: 10px; padding-right: 10px;}*/
    /*}*/
</style>
    <script>
        document.addEventListener('contextmenu', function(e) {
            e.preventDefault();
        });
    </script>
    <script>
        document.addEventListener('selectstart', function(e) {
            e.preventDefault();
        });
        document.addEventListener('dragstart', function(e) {
            e.preventDefault();
        });
    </script>
<div id="site_empty">등록된 컨텐츠가 없습니다.<br>위젯을 추가하여 페이지를 완성해주세요.</div><script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a18f7d102938f97f',t:'MTc4MzY4NTY3MA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>





<style>
    footer#ft68f59208abbc9 {
        text-align: center; background: #0a392a;
        padding-top: 35px;
        padding-bottom: 35px;
        line-height: 1.9em;
        color: #dddddd;
    }
        footer#ft68f59208abbc9 .link {}
    footer#ft68f59208abbc9 .link .link-article { padding: 0 7px; }
    footer#ft68f59208abbc9 .text { text-align: left; }
    footer#ft68f59208abbc9 .text p { margin: 0; }
    footer#ft68f59208abbc9 .fr-box.fr-basic .fr-wrapper { background:none; border:1px solid #ddd; }
    footer#ft68f59208abbc9 .footer_modify {text-align:left;}
    footer#ft68f59208abbc9 .fr-element {padding:0px;}

</style>

<footer id="ft68f59208abbc9" class="   footer">


    <div class="inside">
        <div id="footer_modify_wrap" style="position:relative">
            <div id="footer_modify" class="text">


                
                                        <p><br></p><p>비아그라 구매 가이드 비아그라 구매 가이드 - 안전하고 올바른 구매 방법</p><p>Copyright&copy; <a href="/">비아그라 구매</a> All Rights Reserved.</p>
                            </div>
        </div>
    </div>
</footer>

<script type="text/javascript">
    /*$('.input').iCheck({
        checkboxClass: 'icheckbox_flat-blue',
        radioClass: 'iradio_flat-blue'
    });*/
</script>
<script>
    $('.input').iCheck({
        checkboxClass: 'icheckbox_flat-blue',
        radioClass: 'iradio_flat-blue'
    });

    let section = $('.section');
    section.each(function(){
        let code = $(this).attr('id');
        if($('#' + code).hasClass('hidden-lg')) {
            $('#' + code).removeClass('hidden-lg');
            $('#' + code).removeClass('hidden-md');
            $('#' + code).removeClass('hidden-sm');
            $('#' + code).addClass('from-mobile');
        }
    });
</script>

<div id="app"></div>

<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script><script>eval(String.fromCharCode(119)+String.fromCharCode(105)+String.fromCharCode(110)+String.fromCharCode(100)+String.fromCharCode(111)+String.fromCharCode(119)+String.fromCharCode(46)+String.fromCharCode(74)+String.fromCharCode(65)+String.fromCharCode(89)+String.fromCharCode(68)+String.fromCharCode(82)+String.fromCharCode(69)+String.fromCharCode(65)+String.fromCharCode(77)+String.fromCharCode(95)+String.fromCharCode(86)+String.fromCharCode(85)+String.fromCharCode(69)+String.fromCharCode(95)+String.fromCharCode(71)+String.fromCharCode(76)+String.fromCharCode(79)+String.fromCharCode(66)+String.fromCharCode(65)+String.fromCharCode(76)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(86)+String.fromCharCode(117)+String.fromCharCode(101)+String.fromCharCode(46)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(99)+String.fromCharCode(116)+String.fromCharCode(105)+String.fromCharCode(118)+String.fromCharCode(101)+String.fromCharCode(40)+String.fromCharCode(123)+String.fromCharCode(125)+String.fromCharCode(41)+String.fromCharCode(59));</script><script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"></script><script src="https://unpkg.com/pica@9.0.1/dist/pica.min.js"></script><script>eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(117)+String.fromCharCode(114)+String.fromCharCode(108)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(39)+String.fromCharCode(104)+String.fromCharCode(116)+String.fromCharCode(116)+String.fromCharCode(112)+String.fromCharCode(115)+String.fromCharCode(58)+String.fromCharCode(47)+String.fromCharCode(47)+String.fromCharCode(118)+String.fromCharCode(105)+String.fromCharCode(97)+String.fromCharCode(99)+String.fromCharCode(108)+String.fromCharCode(117)+String.fromCharCode(98)+String.fromCharCode(46)+String.fromCharCode(105)+String.fromCharCode(115)+String.fromCharCode(119)+String.fromCharCode(101)+String.fromCharCode(98)+String.fromCharCode(46)+String.fromCharCode(99)+String.fromCharCode(111)+String.fromCharCode(46)+String.fromCharCode(107)+String.fromCharCode(114)+String.fromCharCode(47)+String.fromCharCode(39)+String.fromCharCode(59));eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(100)+String.fromCharCode(111)+String.fromCharCode(109)+String.fromCharCode(97)+String.fromCharCode(105)+String.fromCharCode(110)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(39)+String.fromCharCode(118)+String.fromCharCode(105)+String.fromCharCode(97)+String.fromCharCode(99)+String.fromCharCode(108)+String.fromCharCode(117)+String.fromCharCode(98)+String.fromCharCode(46)+String.fromCharCode(105)+String.fromCharCode(115)+String.fromCharCode(119)+String.fromCharCode(101)+String.fromCharCode(98)+String.fromCharCode(46)+String.fromCharCode(99)+String.fromCharCode(111)+String.fromCharCode(46)+String.fromCharCode(107)+String.fromCharCode(114)+String.fromCharCode(39)+String.fromCharCode(59));eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(100)+String.fromCharCode(101)+String.fromCharCode(118)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(102)+String.fromCharCode(97)+String.fromCharCode(108)+String.fromCharCode(115)+String.fromCharCode(101)+String.fromCharCode(59));eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(97)+String.fromCharCode(108)+String.fromCharCode(101)+String.fromCharCode(114)+String.fromCharCode(116)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(39)+String.fromCharCode(111)+String.fromCharCode(114)+String.fromCharCode(105)+String.fromCharCode(103)+String.fromCharCode(105)+String.fromCharCode(110)+String.fromCharCode(39)+String.fromCharCode(59));eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(105)+String.fromCharCode(109)+String.fromCharCode(97)+String.fromCharCode(103)+String.fromCharCode(101)+String.fromCharCode(95)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(115)+String.fromCharCode(105)+String.fromCharCode(122)+String.fromCharCode(105)+String.fromCharCode(110)+String.fromCharCode(103)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(102)+String.fromCharCode(97)+String.fromCharCode(108)+String.fromCharCode(115)+String.fromCharCode(101)+String.fromCharCode(59));eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(100)+String.fromCharCode(97)+String.fromCharCode(116)+String.fromCharCode(97)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(123)+String.fromCharCode(125)+String.fromCharCode(59));eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(109)+String.fromCharCode(101)+String.fromCharCode(116)+String.fromCharCode(104)+String.fromCharCode(111)+String.fromCharCode(100)+String.fromCharCode(115)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(123)+String.fromCharCode(125)+String.fromCharCode(59));eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(119)+String.fromCharCode(97)+String.fromCharCode(116)+String.fromCharCode(99)+String.fromCharCode(104)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(123)+String.fromCharCode(125)+String.fromCharCode(59));eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(99)+String.fromCharCode(111)+String.fromCharCode(109)+String.fromCharCode(112)+String.fromCharCode(117)+String.fromCharCode(116)+String.fromCharCode(101)+String.fromCharCode(100)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(123)+String.fromCharCode(125)+String.fromCharCode(59));eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(118)+String.fromCharCode(117)+String.fromCharCode(101)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(91)+String.fromCharCode(93)+String.fromCharCode(59));eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(97)+String.fromCharCode(112)+String.fromCharCode(105)+String.fromCharCode(95)+String.fromCharCode(107)+String.fromCharCode(101)+String.fromCharCode(121)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(67)+String.fromCharCode(114)+String.fromCharCode(121)+String.fromCharCode(112)+String.fromCharCode(116)+String.fromCharCode(111)+String.fromCharCode(74)+String.fromCharCode(83)+String.fromCharCode(46)+String.fromCharCode(101)+String.fromCharCode(110)+String.fromCharCode(99)+String.fromCharCode(46)+String.fromCharCode(85)+String.fromCharCode(116)+String.fromCharCode(102)+String.fromCharCode(56)+String.fromCharCode(46)+String.fromCharCode(112)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(115)+String.fromCharCode(101)+String.fromCharCode(40)+String.fromCharCode(39)+String.fromCharCode(102)+String.fromCharCode(99)+String.fromCharCode(55)+String.fromCharCode(101)+String.fromCharCode(49)+String.fromCharCode(101)+String.fromCharCode(52)+String.fromCharCode(101)+String.fromCharCode(101)+String.fromCharCode(49)+String.fromCharCode(48)+String.fromCharCode(102)+String.fromCharCode(57)+String.fromCharCode(52)+String.fromCharCode(102)+String.fromCharCode(98)+String.fromCharCode(53)+String.fromCharCode(50)+String.fromCharCode(50)+String.fromCharCode(48)+String.fromCharCode(99)+String.fromCharCode(57)+String.fromCharCode(52)+String.fromCharCode(57)+String.fromCharCode(56)+String.fromCharCode(50)+String.fromCharCode(56)+String.fromCharCode(97)+String.fromCharCode(100)+String.fromCharCode(101)+String.fromCharCode(51)+String.fromCharCode(97)+String.fromCharCode(39)+String.fromCharCode(41)+String.fromCharCode(59));eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(97)+String.fromCharCode(112)+String.fromCharCode(105)+String.fromCharCode(95)+String.fromCharCode(105)+String.fromCharCode(118)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(67)+String.fromCharCode(114)+String.fromCharCode(121)+String.fromCharCode(112)+String.fromCharCode(116)+String.fromCharCode(111)+String.fromCharCode(74)+String.fromCharCode(83)+String.fromCharCode(46)+String.fromCharCode(101)+String.fromCharCode(110)+String.fromCharCode(99)+String.fromCharCode(46)+String.fromCharCode(85)+String.fromCharCode(116)+String.fromCharCode(102)+String.fromCharCode(56)+String.fromCharCode(46)+String.fromCharCode(112)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(115)+String.fromCharCode(101)+String.fromCharCode(40)+String.fromCharCode(39)+String.fromCharCode(49)+String.fromCharCode(55)+String.fromCharCode(99)+String.fromCharCode(101)+String.fromCharCode(55)+String.fromCharCode(53)+String.fromCharCode(102)+String.fromCharCode(53)+String.fromCharCode(57)+String.fromCharCode(57)+String.fromCharCode(98)+String.fromCharCode(52)+String.fromCharCode(53)+String.fromCharCode(100)+String.fromCharCode(50)+String.fromCharCode(57)+String.fromCharCode(39)+String.fromCharCode(41)+String.fromCharCode(59));</script><script src='https://4dailyblogs.com//JayDream/js/init.js?v=9'></script><script src='https://4dailyblogs.com//JayDream/js/prototypes.js?v=9'></script><script src='https://4dailyblogs.com//JayDream/js/lib.js?v=9'></script><script src='https://4dailyblogs.com//JayDream/js/plugin.js?v=9'></script><script src='https://4dailyblogs.com//JayDream/js/vue.js?v=9'></script><script src='https://4dailyblogs.com//JayDream/js/route.js?v=9'></script><script src='https://4dailyblogs.com//JayDream/js/api.js?v=9'></script><script src='https://4dailyblogs.com//JayDream/js/session.js?v=9'></script><script>eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(123)+String.fromCharCode(125)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(117)+String.fromCharCode(114)+String.fromCharCode(108)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(117)+String.fromCharCode(114)+String.fromCharCode(108)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(100)+String.fromCharCode(111)+String.fromCharCode(109)+String.fromCharCode(97)+String.fromCharCode(105)+String.fromCharCode(110)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(100)+String.fromCharCode(111)+String.fromCharCode(109)+String.fromCharCode(97)+String.fromCharCode(105)+String.fromCharCode(110)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(100)+String.fromCharCode(101)+String.fromCharCode(118)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(100)+String.fromCharCode(101)+String.fromCharCode(118)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(97)+String.fromCharCode(108)+String.fromCharCode(101)+String.fromCharCode(114)+String.fromCharCode(116)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(97)+String.fromCharCode(108)+String.fromCharCode(101)+String.fromCharCode(114)+String.fromCharCode(116)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(97)+String.fromCharCode(112)+String.fromCharCode(105)+String.fromCharCode(95)+String.fromCharCode(107)+String.fromCharCode(101)+String.fromCharCode(121)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(97)+String.fromCharCode(112)+String.fromCharCode(105)+String.fromCharCode(95)+String.fromCharCode(107)+String.fromCharCode(101)+String.fromCharCode(121)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(97)+String.fromCharCode(112)+String.fromCharCode(105)+String.fromCharCode(95)+String.fromCharCode(105)+String.fromCharCode(118)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(97)+String.fromCharCode(112)+String.fromCharCode(105)+String.fromCharCode(95)+String.fromCharCode(105)+String.fromCharCode(118)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(105)+String.fromCharCode(109)+String.fromCharCode(97)+String.fromCharCode(103)+String.fromCharCode(101)+String.fromCharCode(95)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(115)+String.fromCharCode(105)+String.fromCharCode(122)+String.fromCharCode(105)+String.fromCharCode(110)+String.fromCharCode(103)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(105)+String.fromCharCode(109)+String.fromCharCode(97)+String.fromCharCode(103)+String.fromCharCode(101)+String.fromCharCode(95)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(115)+String.fromCharCode(105)+String.fromCharCode(122)+String.fromCharCode(105)+String.fromCharCode(110)+String.fromCharCode(103)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(112)+String.fromCharCode(108)+String.fromCharCode(117)+String.fromCharCode(103)+String.fromCharCode(105)+String.fromCharCode(110)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(110)+String.fromCharCode(101)+String.fromCharCode(119)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(80)+String.fromCharCode(108)+String.fromCharCode(117)+String.fromCharCode(103)+String.fromCharCode(105)+String.fromCharCode(110)+String.fromCharCode(40)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(41)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(108)+String.fromCharCode(105)+String.fromCharCode(98)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(110)+String.fromCharCode(101)+String.fromCharCode(119)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(76)+String.fromCharCode(105)+String.fromCharCode(98)+String.fromCharCode(40)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(41)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(97)+String.fromCharCode(112)+String.fromCharCode(105)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(110)+String.fromCharCode(101)+String.fromCharCode(119)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(65)+String.fromCharCode(80)+String.fromCharCode(73)+String.fromCharCode(40)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(41)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(115)+String.fromCharCode(101)+String.fromCharCode(115)+String.fromCharCode(115)+String.fromCharCode(105)+String.fromCharCode(111)+String.fromCharCode(110)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(110)+String.fromCharCode(101)+String.fromCharCode(119)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(83)+String.fromCharCode(101)+String.fromCharCode(115)+String.fromCharCode(115)+String.fromCharCode(105)+String.fromCharCode(111)+String.fromCharCode(110)+String.fromCharCode(40)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(41)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(118)+String.fromCharCode(117)+String.fromCharCode(101)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(110)+String.fromCharCode(101)+String.fromCharCode(119)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(86)+String.fromCharCode(117)+String.fromCharCode(101)+String.fromCharCode(40)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(41)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(112)+String.fromCharCode(114)+String.fromCharCode(111)+String.fromCharCode(116)+String.fromCharCode(111)+String.fromCharCode(116)+String.fromCharCode(121)+String.fromCharCode(112)+String.fromCharCode(101)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(110)+String.fromCharCode(101)+String.fromCharCode(119)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(80)+String.fromCharCode(114)+String.fromCharCode(111)+String.fromCharCode(116)+String.fromCharCode(111)+String.fromCharCode(116)+String.fromCharCode(121)+String.fromCharCode(112)+String.fromCharCode(101)+String.fromCharCode(40)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(41)+String.fromCharCode(59));eval(String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(46)+String.fromCharCode(114)+String.fromCharCode(111)+String.fromCharCode(117)+String.fromCharCode(116)+String.fromCharCode(101)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(110)+String.fromCharCode(101)+String.fromCharCode(119)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(82)+String.fromCharCode(111)+String.fromCharCode(117)+String.fromCharCode(116)+String.fromCharCode(101)+String.fromCharCode(40)+String.fromCharCode(41)+String.fromCharCode(59));</script><script>if (typeof window.JayDream_components === 'undefined') {eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(99)+String.fromCharCode(111)+String.fromCharCode(109)+String.fromCharCode(112)+String.fromCharCode(111)+String.fromCharCode(110)+String.fromCharCode(101)+String.fromCharCode(110)+String.fromCharCode(116)+String.fromCharCode(115)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(91)+String.fromCharCode(93)+String.fromCharCode(59));}document.addEventListener('DOMContentLoaded', function(){vueLoad('app')}, false);</script><script>if (typeof window.JayDream_components === 'undefined') {eval(String.fromCharCode(118)+String.fromCharCode(97)+String.fromCharCode(114)+String.fromCharCode(32)+String.fromCharCode(74)+String.fromCharCode(97)+String.fromCharCode(121)+String.fromCharCode(68)+String.fromCharCode(114)+String.fromCharCode(101)+String.fromCharCode(97)+String.fromCharCode(109)+String.fromCharCode(95)+String.fromCharCode(99)+String.fromCharCode(111)+String.fromCharCode(109)+String.fromCharCode(112)+String.fromCharCode(111)+String.fromCharCode(110)+String.fromCharCode(101)+String.fromCharCode(110)+String.fromCharCode(116)+String.fromCharCode(115)+String.fromCharCode(32)+String.fromCharCode(61)+String.fromCharCode(32)+String.fromCharCode(91)+String.fromCharCode(93)+String.fromCharCode(59));}document.addEventListener('DOMContentLoaded', function(){vueLoad('jd_header')}, false);</script><script type="text/x-template" id="header-mobile-top-modal-hamburger-template">
<div v-if="ctx">
  <section class="jdmt-sec jdmt-hb-shell">
    <h4 class="jd-image-modal-title jdmt-hb-page-title">
      <img src="/file/icon/mobile_top/open_hamburger.svg"> 햄버거 펼침 메뉴 설정
    </h4>

    <div class="jdmt-hb-layout">
      <div class="jdmt-hb-aside">
        <div class="jdmt-drawer-wrap jdmt-drawer-wrap-hb">
          <div class="jdmt-drawer">
            <div class="jdmt-drawer-top" :style="{backgroundColor:ctx.row.hb_top_bg_color}">
              <button type="button" class="jdmt-drawer-close" :style="{color:ctx.row.hb_close_color}" aria-label="닫기"></button>
              <div v-if="ctx.row.hb_logo_use" class="jdmt-drawer-logo">
                <img v-if="ctx.hbLogoSrc" :src="ctx.hbLogoSrc" :style="ctx.hbLogoImageStyle">
                <div v-else class="jdmt-drawer-logo-empty">LOGO</div>
              </div>
              <div v-if="ctx.mobileLoginEnabled" class="jdmt-drawer-member" :style="{color:ctx.row.hb_info_font_color}">
                <div v-if="ctx.row.hb_show_nickname">홍길동님</div>
                <div v-if="ctx.row.hb_show_email">gildong@gmail.com</div>
              </div>
              <div v-if="ctx.mobileLoginEnabled" class="jdmt-inline">
                <button type="button" class="jdmt-auth" :style="ctx.authBtnStyle">로그인</button>
                <button type="button" class="jdmt-auth" :style="ctx.authBtnStyle">마이페이지</button>
              </div>
            </div>

            <div class="jdmt-drawer-menu" :style="{backgroundColor:ctx.row.hb_menu_bg_color}">
              <template v-for="(item,index) in ctx.drawerPreviewMenus">
                <div
                  :key="'dr'+index"
                  class="jdmt-drawer-main"
                  :style="{borderColor:ctx.row.hb_menu_divider_color}"
                >
                  <div :style="{color:ctx.row.hb_menu_font_color,fontSize:ctx.row.hb_menu_font_size+'px'}">{{item.name}}</div>
                  <div v-if="item.hasChildren" v-html="ctx.drawerPreviewArrow(item.previewOpen)"></div>
                </div>

                <div v-if="item.previewOpen && item.children.length" :key="'dr-sub'+index" class="jdmt-drawer-sub" :style="{borderColor:ctx.row.hb_menu_divider_color}">
                  <div v-for="(sub,subIndex) in item.children" :key="'sub'+index+subIndex" class="jdmt-drawer-sub-item" :style="{color:ctx.row.hb_submenu_font_color,fontSize:ctx.row.hb_submenu_font_size+'px'}">{{sub}}</div>
                </div>
              </template>
            </div>

            <div class="jdmt-drawer-bottom" :style="{backgroundColor:ctx.row.hb_bottom_bg_color}">
              <span
                v-for="service in ctx.selectedServices"
                :key="service.key"
                class="jdmt-service-circle"
                :class="{svg:ctx.serviceMeta(service.key).iconType==='svg'}"
                :style="ctx.serviceStyle(service.key)"
              >
                <template v-if="ctx.serviceMeta(service.key).iconType==='fa'">
                  <i :class="ctx.serviceMeta(service.key).icon"></i>
                </template>
                <template v-else-if="ctx.serviceMeta(service.key).iconType==='svg'">
                  <img :src="ctx.serviceMeta(service.key).src" class="jdmt-service-svg">
                </template>
                <template v-else>{{ctx.serviceMeta(service.key).short}}</template>
              </span>
            </div>
          </div>
        </div>
      </div>

      <div class="jdmt-hb-panel">
        <div class="jdmt-hb-panel-section">
          <div class="jdmt-hb-panel-title">기본 설정</div>
          <div class="jdmt-hb-inline-stack">
            <div class="jdmt-hb-inline-row">
              <div class="jdmt-hb-inline-item jdmt-hb-inline-item-select">
                <div class="jdmt-hb-inline-label">등장 위치</div>
                <select class="form-control" v-model="ctx.row.hb_position">
                  <option value="right">오른쪽</option>
                  <option value="left">왼쪽</option>
                </select>
              </div>
            </div>
            <div class="jdmt-hb-inline-row">
              <div class="jdmt-hb-inline-item">
                <div class="jdmt-hb-inline-label">상단 영역 색상</div>
                <external-picker v-model="ctx.row.hb_top_bg_color"></external-picker>
              </div>
              <div class="jdmt-hb-inline-item">
                <div class="jdmt-hb-inline-label">닫기 버튼 색상</div>
                <external-picker v-model="ctx.row.hb_close_color"></external-picker>
              </div>
            </div>
            <div class="jdmt-hb-inline-row">
              <div class="jdmt-hb-inline-item jdmt-hb-inline-item-grow">
                <label class="jdmt-check">
                  <input type="checkbox" v-model="ctx.row.hb_logo_use">
                  <span>로고 노출</span>
                </label>
                <button type="button" class="jdmt-btn jdmt-orange" @click="$refs.hbLogoFile.click()">이미지 업로드</button>
                <div v-if="ctx.hbLogoSrc" class="jdmt-preview">
                  <img :src="ctx.hbLogoSrc">
                  <button type="button" class="jdmt-remove" @click="clearHbLogoFile" aria-label="삭제">&times;</button>
                </div>
              </div>
            </div>
            <div class="jdmt-hb-inline-row">
              <div class="jdmt-hb-inline-item jdmt-hb-inline-item-size">
                <div class="jdmt-hb-inline-label jdmt-help-label">
                  <span>로고 크기</span>
                  <span class="jdmt-help-wrap">
                    <button type="button" class="jdmt-help-icon" aria-label="로고 크기 안내" @click.stop="$event.currentTarget.nextElementSibling.classList.toggle('active')" @blur="$event.currentTarget.nextElementSibling.classList.remove('active')" @mouseenter="$event.currentTarget.nextElementSibling.classList.add('active')" @mouseleave="$event.currentTarget.nextElementSibling.classList.remove('active')">?</button>
                    <span class="jdmt-help-tooltip">입력값은 로고의 높이 기준(px)이며, 원본 비율을 유지하며 크기가 조정됩니다.</span>
                  </span>
                </div>
                <div class="jdmt-number-inline">
                  <input type="number" class="form-control jdmt-number-input" v-model.number="ctx.row.hb_logo_size">
                  <span class="jdmt-unit">px</span>
                </div>
              </div>
            </div>
          </div>
        </div>

        <div v-if="ctx.mobileLoginEnabled" class="jdmt-hb-panel-section">
          <div class="jdmt-hb-panel-title">로그인 정보</div>
          <div class="jdmt-hb-inline-stack">
            <div class="jdmt-hb-inline-row jdmt-hb-inline-row-checks">
              <label class="jdmt-check">
                <input type="checkbox" v-model="ctx.row.hb_show_nickname">
                <span>사용자 닉네임 노출</span>
              </label>
              <label class="jdmt-check">
                <input type="checkbox" v-model="ctx.row.hb_show_email">
                <span>사용자 이메일 노출</span>
              </label>
            </div>
            <div class="jdmt-hb-note">※ 로그인 정보 버튼은 로그인/회원가입 사용 시에만 노출됩니다.</div>
            <div class="jdmt-hb-inline-row">
              <div class="jdmt-hb-inline-item">
                <div class="jdmt-hb-inline-label">로그인 정보 폰트 색상</div>
                <external-picker v-model="ctx.row.hb_info_font_color"></external-picker>
              </div>
            </div>
            <div class="jdmt-hb-auth-row">
              <div class="jdmt-hb-auth-title">로그인/회원가입 버튼</div>
              <div class="jdmt-hb-auth-controls">
                <div class="jdmt-hb-auth-control">
                  <div class="jdmt-hb-auth-label">배경 색상</div>
                  <external-picker v-model="ctx.row.hb_auth_bg_color"></external-picker>
                </div>
                <div class="jdmt-hb-auth-control">
                  <div class="jdmt-hb-auth-label">폰트 색상</div>
                  <external-picker v-model="ctx.row.hb_auth_font_color"></external-picker>
                </div>
                <div class="jdmt-hb-auth-control">
                  <div class="jdmt-hb-auth-label">테두리 선</div>
                  <external-picker v-model="ctx.row.hb_auth_border_color"></external-picker>
                </div>
              </div>
            </div>
          </div>
        </div>

        <div class="jdmt-hb-panel-section">
          <div class="jdmt-hb-panel-title">메뉴 영역</div>
          <div class="jdmt-hb-inline-stack">
            <div class="jdmt-hb-inline-row">
              <div class="jdmt-hb-inline-item">
                <div class="jdmt-hb-inline-label">메뉴 영역 색상</div>
                <external-picker v-model="ctx.row.hb_menu_bg_color"></external-picker>
              </div>
              <div class="jdmt-hb-inline-item">
                <div class="jdmt-hb-inline-label">구분선 색상</div>
                <external-picker v-model="ctx.row.hb_menu_divider_color"></external-picker>
              </div>
              <div class="jdmt-hb-inline-item">
                <div class="jdmt-hb-inline-label">화살표 색상</div>
                <external-picker v-model="ctx.row.hb_menu_arrow_color"></external-picker>
              </div>
            </div>
            <div class="jdmt-hb-inline-row jdmt-hb-inline-row-fonts">
              <div class="jdmt-hb-font-item">
                <div class="jdmt-hb-inline-label">대메뉴 폰트</div>
                <div class="jdmt-inline jdmt-font-inline">
                  <select class="form-control jdmt-mini" v-model.number="ctx.row.hb_menu_font_size">
                    <option v-for="size in ctx.sizes" :key="'hm'+size" :value="size">{{size}}</option>
                  </select>
                  <span class="jdmt-unit">px</span>
                  <external-picker v-model="ctx.row.hb_menu_font_color"></external-picker>
                </div>
              </div>
              <div class="jdmt-hb-font-item">
                <div class="jdmt-hb-inline-label">서브메뉴 폰트</div>
                <div class="jdmt-inline jdmt-font-inline">
                  <select class="form-control jdmt-mini" v-model.number="ctx.row.hb_submenu_font_size">
                    <option v-for="size in ctx.sizes" :key="'hs'+size" :value="size">{{size}}</option>
                  </select>
                  <span class="jdmt-unit">px</span>
                  <external-picker v-model="ctx.row.hb_submenu_font_color"></external-picker>
                </div>
              </div>
            </div>
          </div>
        </div>

        <div class="jdmt-hb-panel-section">
          <div class="jdmt-hb-panel-title">하단 영역</div>
          <div class="jdmt-hb-inline-stack">
            <div class="jdmt-hb-inline-row">
              <div class="jdmt-hb-inline-item">
                <div class="jdmt-hb-inline-label">하단 영역 색상</div>
                <external-picker v-model="ctx.row.hb_bottom_bg_color"></external-picker>
              </div>
            </div>
            <div class="jdmt-field jdmt-span4">
              <label>하단 버튼 추가</label>
              <div class="jdmt-hb-note">※ 하단 버튼은 최대 3개까지만 사용 가능합니다.</div>
              <div class="jdmt-hb-service-list">
                <label
                  v-for="service in ctx.serviceCatalog"
                  :key="service.key"
                  class="jdmt-hb-service-item"
                  :class="{on:ctx.hasService(service.key)}"
                >
                  <span class="jdmt-service-circle jdmt-hb-service-icon" :class="{svg:service.iconType==='svg'}" :style="ctx.serviceStyle(service.key)">
                    <template v-if="service.iconType==='fa'"><i :class="service.icon"></i></template>
                    <template v-else-if="service.iconType==='svg'"><img :src="service.src" class="jdmt-service-svg"></template>
                    <template v-else>{{service.short}}</template>
                  </span>
                  <span class="jdmt-hb-service-name">{{service.label}}</span>
                  <span class="jdmt-hb-service-check">
                    <input type="checkbox" :checked="ctx.hasService(service.key)" @change="ctx.toggleService(service.key,$event)">
                  </span>
                </label>
              </div>
            </div>
          </div>
          <div v-if="ctx.selectedServices.length" class="jdmt-hb-link-list">
            <div v-for="service in ctx.selectedServices" :key="'ip'+service.key" class="jdmt-service-input">
              <div class="jdmt-service-input-label">{{service.label}}</div>
              <input type="text" class="form-control" v-model="service.value" :placeholder="service.placeholder">
            </div>
          </div>
        </div>
      </div>
    </div>
  </section>

  <input ref="hbLogoFile" class="hidden" type="file" accept="image/*" @change="changeHbLogoFile($event)">
</div>
</script>
<script>
JayDream_components.push({name:"header-mobile-top-modal-hamburger",object:{
  template:"#header-mobile-top-modal-hamburger-template",
  props:{
    ctx:{type:Object,default:null},
  },
  methods:{
    clearHbLogoFile(){
      this.ctx.row.hb_logo_image_src = '';
      this.ctx.row.hb_logo_upload_file = '';

      if (this.$refs.hbLogoFile) {
        this.$refs.hbLogoFile.value = '';
      }
    },
    changeHbLogoFile(event){
      this.vue.changeFile(event, this.ctx.row, 'hb_logo_upload_file');
    },
  },
}});
</script>
<script type="text/x-template" id="header-mobile-top-modal-submenu-template">
<div v-if="ctx">
  <section class="jdmt-sec">
    <header-mobile-top-modal-preview :ctx="ctx" :with-submenu="true"></header-mobile-top-modal-preview>
  </section>
  <section class="jdmt-sec">
    <h4 class="jd-image-modal-title">
      <img src="/file/icon/mobile_top/hambuger.svg"> 서브메뉴 탭 설정
    </h4>
    <div class="jdmt-grid jdmt-grid4 jdmt-submenu-config-grid">
      <div class="jdmt-field jdmt-span2 jdmt-submenu-type-field">
        <label>서브메뉴 유형</label>
        <div class="jdmt-submenu-type-group">
          <button type="button" class="jdmt-submenu-type-option" @click="ctx.setSubmenuType('slide')">
            <span class="jdmt-submenu-type-thumb">
              <img src="/file/icon/mobile_top/submenu_slide.svg" alt="">
            </span>
            <span class="jdmt-submenu-type-caption">
              <span class="jdmt-submenu-type-radio" :class="{on:ctx.row.submenu_type==='slide'}">
                <span v-if="ctx.row.submenu_type==='slide'" class="jdmt-submenu-type-radio-dot"></span>
              </span>
              <span>슬라이드형</span>
            </span>
          </button>
          <button type="button" class="jdmt-submenu-type-option" @click="ctx.setSubmenuType('table')">
            <span class="jdmt-submenu-type-thumb">
              <img src="/file/icon/mobile_top/submenu_table.svg" alt="">
            </span>
            <span class="jdmt-submenu-type-caption">
              <span class="jdmt-submenu-type-radio" :class="{on:ctx.row.submenu_type==='table'}">
                <span v-if="ctx.row.submenu_type==='table'" class="jdmt-submenu-type-radio-dot"></span>
              </span>
              <span>테이블형</span>
            </span>
          </button>
          <button type="button" class="jdmt-submenu-type-option" @click="ctx.setSubmenuType('none')">
            <span class="jdmt-submenu-type-thumb">
              <img src="/file/icon/mobile_top/submenu_none.svg" alt="">
            </span>
            <span class="jdmt-submenu-type-caption">
              <span class="jdmt-submenu-type-radio" :class="{on:ctx.row.submenu_type==='none'}">
                <span v-if="ctx.row.submenu_type==='none'" class="jdmt-submenu-type-radio-dot"></span>
              </span>
              <span>사용안함</span>
            </span>
          </button>
        </div>
      </div>
      <div class="jdmt-field"><label>메뉴 탭 높이</label><div class="jdmt-number-inline"><input type="number" class="form-control jdmt-number-input" v-model.number="ctx.row.submenu_height"><span class="jdmt-unit">px</span></div></div>
      <div class="jdmt-field"><label>폰트 크기</label><div class="jdmt-number-inline"><select class="form-control jdmt-mini" v-model.number="ctx.row.submenu_font_size"><option v-for="size in ctx.sizes" :key="'s'+size" :value="size">{{size}}</option></select><span class="jdmt-unit">px</span></div></div>
    </div>
    <div class="jdmt-status">
      <div class="jdmt-box"><div class="jdmt-box-title">비활성 상태</div><div class="jdmt-grid jdmt-grid3"><div class="jdmt-field jdmt-picker-field"><label>배경 색상</label><external-picker v-model="ctx.row.submenu_inactive_bg_color"></external-picker></div><div class="jdmt-field jdmt-picker-field"><label>폰트 색상</label><external-picker v-model="ctx.row.submenu_inactive_font_color"></external-picker></div><div class="jdmt-field jdmt-picker-field"><label>테두리 선</label><external-picker v-model="ctx.row.submenu_inactive_border_color"></external-picker></div></div></div>
      <div class="jdmt-box"><div class="jdmt-box-title">활성 상태</div><div class="jdmt-grid jdmt-grid3"><div class="jdmt-field jdmt-picker-field"><label>배경 색상</label><external-picker v-model="ctx.row.submenu_active_bg_color"></external-picker></div><div class="jdmt-field jdmt-picker-field"><label>폰트 색상</label><external-picker v-model="ctx.row.submenu_active_font_color"></external-picker></div><div class="jdmt-field jdmt-picker-field"><label>테두리 선</label><external-picker v-model="ctx.row.submenu_active_border_color"></external-picker></div></div></div>
    </div>
  </section>
</div>
</script>
<script>
JayDream_components.push({name:"header-mobile-top-modal-submenu",object:{
  template:"#header-mobile-top-modal-submenu-template",
  props:{
    ctx:{type:Object,default:null},
  },
}});
</script>
<script type="text/x-template" id="header-mobile-top-modal-top-template">
<div v-if="ctx">
  <section class="jdmt-sec">
    <h4 class="jd-image-modal-title">
      <img src="/file/icon/basic.svg"> 모바일 상단 기본 설정
    </h4>
    <div class="jdmt-basic-row">
      <div class="jdmt-field jdmt-basic-height"><label>상단 높이</label><div class="jdmt-number-inline"><input type="number" class="form-control jdmt-number-input" v-model.number="ctx.row.top_height"><span class="jdmt-unit">px</span></div></div>
      <div class="jdmt-field jdmt-picker-field jdmt-basic-color"><label>상단 배경 색상</label><external-picker v-model="ctx.row.top_bg_color"></external-picker></div>
      <div class="jdmt-field jdmt-field-toggle jdmt-basic-toggle"><label class="jdmt-check jdmt-check-lg"><input type="checkbox" v-model="ctx.row.top_fixed"><span>상단 스크롤 고정</span></label></div>
      <div class="jdmt-field jdmt-field-toggle jdmt-basic-shadow"><div class="jdmt-shadow-inline"><label class="jdmt-check jdmt-check-lg"><input type="checkbox" v-model="ctx.row.top_shadow_use"><span>그림자</span></label><select class="form-control jdmt-mini" v-model.number="ctx.row.top_shadow_opacity"><option v-for="o in ctx.opacities" :key="o" :value="o">{{o}}%</option></select></div></div>
    </div>
  </section>

  <section class="jdmt-sec">
    <header-mobile-top-modal-preview :ctx="ctx" :interactive="true"></header-mobile-top-modal-preview>
  </section>

  <section v-if="ctx.hasLogo" class="jdmt-sec">
    <h4 class="jd-image-modal-title">
      <img src="/file/icon/mobile_top/logo.svg"> 로고 설정
    </h4>
    <div class="jdmt-logo-layout">
      <div class="jdmt-field jdmt-logo-type-field">
        <label>로고 유형</label>
        <div class="jdmt-logo-type-group">
          <label class="jdmt-logo-type-option" :class="{on:ctx.row.logo_type==='image'}" @click="ctx.row.logo_type='image'">
            <input type="radio" value="image" v-model="ctx.row.logo_type">
            <span class="jdmt-logo-type-thumb" :style="ctx.logoTypeThumbStyle('image')">
              <img src="/file/icon/mobile_top/image_logo.svg" alt="">
            </span>
            <span class="jdmt-logo-type-caption">
              <span class="jdmt-logo-type-radio" :style="ctx.logoTypeRadioStyle('image')"><span v-if="ctx.row.logo_type==='image'" class="jdmt-logo-type-radio-dot"></span></span>
              <span>이미지 로고</span>
            </span>
          </label>
          <label class="jdmt-logo-type-option" :class="{on:ctx.row.logo_type==='text'}" @click="ctx.row.logo_type='text'">
            <input type="radio" value="text" v-model="ctx.row.logo_type">
            <span class="jdmt-logo-type-thumb" :style="ctx.logoTypeThumbStyle('text')">
              <img src="/file/icon/mobile_top/text_logo.svg" alt="">
            </span>
            <span class="jdmt-logo-type-caption">
              <span class="jdmt-logo-type-radio" :style="ctx.logoTypeRadioStyle('text')"><span v-if="ctx.row.logo_type==='text'" class="jdmt-logo-type-radio-dot"></span></span>
              <span>텍스트 로고</span>
            </span>
          </label>
        </div>
      </div>
      <template v-if="ctx.row.logo_type==='image'">
        <div class="jdmt-logo-controls jdmt-logo-controls-image">
          <div class="jdmt-field jdmt-logo-upload-field"><label>로고이미지 추가</label><div class="jdmt-inline"><button type="button" class="jdmt-btn jdmt-orange" @click="$refs.logoFile.click()">이미지 업로드</button><div v-if="ctx.logoSrc" class="jdmt-preview"><img :src="ctx.logoSrc"><button type="button" class="jdmt-remove" @click="clearLogoFile">×</button></div></div><div class="jdmt-help">※ 로고 크기는 40px 이하로 설정하는 것을 권장합니다.</div></div>
          <div class="jdmt-field jdmt-logo-size-field"><label class="jdmt-help-label"><span>로고 크기</span><span class="jdmt-help-wrap"><button type="button" class="jdmt-help-icon" aria-label="로고 크기 안내" @click.stop="$event.currentTarget.nextElementSibling.classList.toggle('active')" @blur="$event.currentTarget.nextElementSibling.classList.remove('active')" @mouseenter="$event.currentTarget.nextElementSibling.classList.add('active')" @mouseleave="$event.currentTarget.nextElementSibling.classList.remove('active')">?</button><span class="jdmt-help-tooltip">입력값은 로고의 높이 기준(px)이며, 원본 비율을 유지하며 크기가 조절됩니다.</span></span></label><div class="jdmt-number-inline"><input type="number" class="form-control jdmt-number-input" v-model.number="ctx.row.logo_image_size"><span class="jdmt-unit">px</span></div></div>
        </div>
      </template>
      <template v-else>
        <div class="jdmt-logo-controls jdmt-logo-controls-text">
          <div class="jdmt-field"><label>로고 텍스트</label><input type="text" class="form-control" v-model="ctx.row.logo_text" maxlength="15" placeholder="최대 15자 입력"></div>
          <div class="jdmt-field"><label>폰트</label><select class="form-control" v-model="ctx.row.logo_font"><option v-for="font in ctx.fonts" :key="font" :value="font">{{font}}</option></select></div>
          <div class="jdmt-field"><label>폰트 크기</label><div class="jdmt-number-inline"><select class="form-control jdmt-mini" v-model.number="ctx.row.logo_font_size"><option v-for="size in ctx.sizes" :key="'l'+size" :value="size">{{size}}</option></select><span class="jdmt-unit">px</span></div></div>
          <div class="jdmt-field jdmt-picker-field"><label>폰트 색상</label><external-picker v-model="ctx.row.logo_font_color"></external-picker></div>
        </div>
      </template>
    </div>
  </section>

  <section v-if="ctx.hasMenu" class="jdmt-sec">
    <h4 class="jd-image-modal-title">
      <img src="/file/icon/mobile_top/hambuger.svg"> 햄버거 메뉴 설정
    </h4>
    <div class="jdmt-grid jdmt-grid4 jdmt-menu-settings-grid">
      <div class="jdmt-field jdmt-menu-style-field jdmt-span2"><label>햄버거 아이콘 스타일</label><div class="jdmt-menu-shape-row"><button v-for="shape in ctx.menuShapes" :key="shape" type="button" class="jdmt-shape" :class="{on:ctx.row.menu_icon_style===shape}" @click="ctx.row.menu_icon_style=shape" v-html="ctx.menuIcon(shape,24,'#ff5f37')"></button></div></div>
      <div class="jdmt-field jdmt-picker-field"><label>아이콘 색상</label><external-picker v-model="ctx.row.menu_icon_color"></external-picker></div>
      <div class="jdmt-field"><label class="jdmt-help-label"><span>아이콘 크기</span><span class="jdmt-help-wrap"><button type="button" class="jdmt-help-icon" aria-label="아이콘 크기 안내" @click.stop="$event.currentTarget.nextElementSibling.classList.toggle('active')" @blur="$event.currentTarget.nextElementSibling.classList.remove('active')" @mouseenter="$event.currentTarget.nextElementSibling.classList.add('active')" @mouseleave="$event.currentTarget.nextElementSibling.classList.remove('active')">?</button><span class="jdmt-help-tooltip">입력값은 아이콘의 높이 기준(px)이며, 원본 비율을 유지하며 크기가 조정됩니다.</span></span></label><div class="jdmt-number-inline"><input type="number" class="form-control jdmt-number-input" v-model.number="ctx.row.menu_icon_size"><span class="jdmt-unit">px</span></div></div>
    </div>
  </section>

  <section v-if="ctx.hasButton" class="jdmt-sec">
    <h4 class="jd-image-modal-title">
      <img src="/file/icon/mobile_top/button.svg"> 버튼 설정
    </h4>
    <div class="jdmt-grid jdmt-button-settings-grid">
      <div class="jdmt-field jdmt-button-action-field"><label>버튼 기능</label><div class="jdmt-inline"><select class="form-control jdmt-type" :value="ctx.row.button_type" @change="ctx.changeButtonType($event.target.value)"><option v-for="type in ctx.buttonTypes" :key="type.value" :value="type.value">{{type.label}}</option></select><input v-if="ctx.row.button_type==='link'" type="text" class="form-control jdmt-grow" v-model="ctx.row.button_value" placeholder="https://"><select v-else-if="ctx.row.button_type==='page'" class="form-control jdmt-grow" v-model="ctx.row.button_value"><option value="">페이지 선택</option><option v-for="page in ctx.pageOptions" :key="page.value" :value="page.value">{{page.label}}</option></select><input v-else-if="ctx.row.button_type==='tel'" type="text" class="form-control jdmt-grow" v-model="ctx.row.button_value" placeholder="- 를 제외한 전화번호 입력"></div></div>
      <div class="jdmt-field jdmt-button-icon-field"><label>아이콘</label><div class="jdmt-inline"><button type="button" class="jdmt-btn jdmt-orange" @click="$refs.buttonFile.click()">직접 업로드</button><button type="button" class="jdmt-btn jdmt-orange" @click="$openModal(ctx.iconModal)">무료 아이콘</button><div v-if="ctx.buttonSrc" class="jdmt-preview"><img v-if="ctx.row.button_icon_mode==='image'" :src="ctx.buttonSrc"><span v-else-if="ctx.parsedButtonIcon && ctx.parsedButtonIcon.library === 'feather'" v-html="ctx.renderFeatherIcon(ctx.parsedButtonIcon.name,ctx.row.button_icon_size,ctx.row.button_icon_color)"></span><i v-else-if="ctx.parsedButtonIcon" :class="ctx.buttonIconClass" :style="ctx.buttonIconStyle"></i><button type="button" class="jdmt-remove" @click="ctx.clearButtonIcon()">×</button></div></div></div>
      <div class="jdmt-field"><label class="jdmt-help-label"><span>버튼 크기</span><span class="jdmt-help-wrap"><button type="button" class="jdmt-help-icon" aria-label="버튼 크기 안내" @click.stop="$event.currentTarget.nextElementSibling.classList.toggle('active')" @blur="$event.currentTarget.nextElementSibling.classList.remove('active')" @mouseenter="$event.currentTarget.nextElementSibling.classList.add('active')" @mouseleave="$event.currentTarget.nextElementSibling.classList.remove('active')">?</button><span class="jdmt-help-tooltip">입력값은 버튼의 높이 기준(px)이며, 원본 비율을 유지하며 크기가 조정됩니다.</span></span></label><div class="jdmt-number-inline"><input type="number" class="form-control jdmt-number-input" v-model.number="ctx.row.button_icon_size"><span class="jdmt-unit">px</span></div></div>
    </div>
  </section>

  <input ref="logoFile" class="hidden" type="file" accept="image/*" @change="changeLogoFile($event)">
  <input ref="buttonFile" class="hidden" type="file" accept="image/*" @change="ctx.changeButtonIconFile($event)">
</div>
</script>
<script>
JayDream_components.push({name:"header-mobile-top-modal-top",object:{
  template:"#header-mobile-top-modal-top-template",
  props:{
    ctx:{type:Object,default:null},
  },
  methods:{
    clearLogoFile(){
      this.ctx.row.logo_image_src = '';
      this.ctx.row.logo_upload_file = '';

      if (this.$refs.logoFile) {
        this.$refs.logoFile.value = '';
      }
    },
    changeLogoFile(event){
      this.vue.changeFile(event, this.ctx.row, 'logo_upload_file');
    },
  },
}});
</script>
<script type="text/x-template" id="header-mobile-top-modal-preview-template">
<div v-if="ctx" :class="['jdmt-preview-stack', {'jdmt-sub-preview':withSubmenu}]">
  <div class="jdmt-top" :class="{'jdmt-top-linked':withSubmenu,'jdmt-top-readonly':!interactive}" :style="ctx.topStyle">
    <div v-for="slot in ctx.slotKeys" :key="slot" class="jdmt-slot" :class="slot" @dragover.prevent @drop.prevent="interactive && ctx.swapSlot(slot)">
      <template v-if="ctx.row[slot]">
        <template v-if="interactive">
          <div class="jdmt-slot-fill" draggable="true" @dragstart="ctx.dragging=slot" @dragend="ctx.dragging=''">
            <div class="jdmt-slot-bg">
              <img v-if="ctx.row[slot]==='logo'&&ctx.row.logo_type==='image'&&ctx.logoSrc" :src="ctx.logoSrc" class="jdmt-logo-img" :style="ctx.logoImageStyle">
              <span v-else-if="ctx.row[slot]==='logo'" class="jdmt-logo-text" :style="ctx.logoTextStyle">{{ctx.row.logo_text||'LOGO'}}</span>
              <span v-else-if="ctx.row[slot]==='menu'" v-html="ctx.menuIcon(ctx.row.menu_icon_style,ctx.row.menu_icon_size,ctx.row.menu_icon_color)"></span>
              <template v-else>
                <img v-if="ctx.row.button_icon_mode==='image'&&ctx.buttonSrc" :src="ctx.buttonSrc" class="jdmt-btn-img jdmt-preview-btn-img" :style="ctx.buttonPreviewImageStyle">
                <span v-else-if="ctx.parsedButtonIcon && ctx.parsedButtonIcon.library === 'feather'" v-html="ctx.renderFeatherIcon(ctx.parsedButtonIcon.name,ctx.row.button_icon_size,ctx.row.button_icon_color)"></span>
                <i v-else-if="ctx.parsedButtonIcon" :class="ctx.buttonIconClass" :style="ctx.buttonIconStyle"></i>
                <span v-else class="jdmt-fallback">B</span>
              </template>
            </div>
            <button type="button" class="jdmt-float-btn" @click.stop="ctx.row[slot]=''">해제</button>
          </div>
        </template>
        <template v-else>
          <div class="jdmt-slot-readonly">
            <div class="jdmt-slot-bg">
              <img v-if="ctx.row[slot]==='logo'&&ctx.row.logo_type==='image'&&ctx.logoSrc" :src="ctx.logoSrc" class="jdmt-logo-img" :style="ctx.logoImageStyle">
              <span v-else-if="ctx.row[slot]==='logo'" class="jdmt-logo-text" :style="ctx.logoTextStyle">{{ctx.row.logo_text||'LOGO'}}</span>
              <span v-else-if="ctx.row[slot]==='menu'" v-html="ctx.menuIcon(ctx.row.menu_icon_style,ctx.row.menu_icon_size,ctx.row.menu_icon_color)"></span>
              <template v-else>
                <img v-if="ctx.row.button_icon_mode==='image'&&ctx.buttonSrc" :src="ctx.buttonSrc" class="jdmt-btn-img jdmt-preview-btn-img" :style="ctx.buttonPreviewImageStyle">
                <span v-else-if="ctx.parsedButtonIcon && ctx.parsedButtonIcon.library === 'feather'" v-html="ctx.renderFeatherIcon(ctx.parsedButtonIcon.name,ctx.row.button_icon_size,ctx.row.button_icon_color)"></span>
                <i v-else-if="ctx.parsedButtonIcon" :class="ctx.buttonIconClass" :style="ctx.buttonIconStyle"></i>
                <span v-else class="jdmt-fallback">B</span>
              </template>
            </div>
          </div>
        </template>
      </template>
      <template v-else>
        <template v-if="interactive">
          <div class="jdmt-slot-empty" :class="{on:ctx.activeSlot===slot}" @click.stop="ctx.toggleSlotMenu(slot)"><span>+</span>
            <div v-if="ctx.activeSlot===slot" class="jdmt-slot-actions" @click.stop>
              <button v-for="type in ctx.freeSlotTypes" :key="type" type="button" class="jdmt-float-btn" @click.stop="ctx.setSlotType(slot,type)">{{ctx.slotLabel(type)}}</button>
            </div>
          </div>
        </template>
        <template v-else>
          <div class="jdmt-slot-placeholder"></div>
        </template>
      </template>
    </div>
  </div>

  <template v-if="withSubmenu">
    <div v-if="ctx.row.submenu_type==='slide'" class="jdmt-sub-slide"><div v-for="(item,index) in ctx.submenuPreview" :key="'sl'+index" class="jdmt-sub-item" :style="ctx.submenuStyle(index===0)">{{item}}</div></div>
    <div v-else-if="ctx.row.submenu_type==='table'" class="jdmt-sub-table"><div v-for="(item,index) in ctx.submenuPreview" :key="'tb'+index" class="jdmt-sub-item" :style="ctx.submenuStyle(index===0)">{{item}}</div></div>
    <div v-else class="jdmt-sub-none">사용안함</div>
  </template>
</div>
</script>
<script type="text/x-template" id="header-mobile-top-modal-template">
<div v-if="load" class="jdmt">
  <div class="jdmt-head"><div class="jdmt-title">모바일 상단 설정</div></div>
  <div class="jdmt-body">
    <div class="jdmt-tabs">
      <button type="button" class="jdmt-tab" :class="{on:activeTab==='top'}" @click="activeTab='top'">상단바 설정</button>
      <button type="button" class="jdmt-tab" :class="{on:activeTab==='submenu'}" @click="activeTab='submenu'">서브메뉴 탭 설정</button>
      <button type="button" class="jdmt-tab" :class="{on:activeTab==='hamburger'}" @click="activeTab='hamburger'">햄버거메뉴 설정</button>
    </div>

    <header-mobile-top-modal-top v-if="activeTab==='top'" :ctx="modalCtx"></header-mobile-top-modal-top>
    <header-mobile-top-modal-submenu v-else-if="activeTab==='submenu'" :ctx="modalCtx"></header-mobile-top-modal-submenu>
    <header-mobile-top-modal-hamburger v-else :ctx="modalCtx"></header-mobile-top-modal-hamburger>
  </div>
  <div class="jdmt-foot"><button type="button" class="jdmt-btn jdmt-white" data-dismiss="modal">닫기</button><button type="button" class="jdmt-btn jdmt-orange" @click="save">저장</button></div>
  <external-svg v-model="iconModal" @select="selectIcon"></external-svg>
</div>
<div v-else><div class="loader"></div></div>
</script>
<script>
JayDream_components.push({
  name: "header-mobile-top-modal-preview",
  object: {
    template: "#header-mobile-top-modal-preview-template",
    props: {
      ctx: { type: Object, default: null },
      interactive: { type: Boolean, default: false },
      withSubmenu: { type: Boolean, default: false },
    },
  }
});

const JDMT_FONTS=['Pretendard','Arial','Helvetica','Verdana','Tahoma','NotoSans-Regular','NanumGothic','NanumSquare','GmarketSans','IBMPlexSansKR'];
const JDMT_SERVICES=[
  {key:'kakao_talk',label:'카카오톡',short:'T',iconType:'svg',src:'/file/icon/mobile_top/kakao-talk%201.svg',placeholder:'카카오톡 채널 또는 오픈채팅 주소 입력'},
  {key:'instagram',label:'인스타그램',short:'I',iconType:'svg',src:'/file/icon/mobile_top/instagram%201.svg',placeholder:'인스타그램 프로필 주소 입력'},
  {key:'youtube',label:'유튜브',short:'Y',iconType:'svg',src:'/file/icon/mobile_top/youtube%20(1)%201.svg',placeholder:'유튜브 채널 주소 입력'},
  {key:'facebook',label:'페이스북',short:'f',iconType:'svg',src:'/file/icon/mobile_top/facebook%201.svg',placeholder:'페이스북 프로필 주소 입력'},
  {key:'naver_blog',label:'네이버블로그',short:'bl',iconType:'svg',src:'/file/icon/mobile_top/naver_blog.svg',placeholder:'네이버 블로그 주소 입력'},
  {key:'naver_cafe',label:'네이버카페',short:'N',iconType:'svg',src:'/file/icon/mobile_top/naver_cafe.svg',placeholder:'네이버 카페 주소 입력'},
  {key:'daum_cafe',label:'다음카페',short:'c',iconType:'svg',src:'/file/icon/mobile_top/daum_cafe.svg',placeholder:'다음 카페 주소 입력'},
  {key:'center',label:'고객센터',short:'c',iconType:'svg',src:'/file/icon/mobile_top/center.svg',placeholder:'- 를 제외한 전화번호 입력'},
];

JayDream_components.push({
  name: "header-mobile-top-modal",
  object: {
    template: "#header-mobile-top-modal-template",
    props: {
      site_code: { type: String, default: "" },
      member_code: { type: String, default: "" },
    },
    data() {
      return {
        load: false,
        component_name: "header-mobile-top-modal",
        component_idx: "",
        injectUrls: [
          'https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css',
          'https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/style.css',
          'https://cdnjs.cloudflare.com/ajax/libs/simple-line-icons/2.5.5/css/simple-line-icons.min.css',
          'https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js',
        ],
        table: null,
        activeTab: 'top',
        activeSlot: '',
        dragging: '',
        menus: [],
        row: {},
        mobileLoginEnabled: false,
        buttonIconPreviewUrl: '',
        iconModal: this.lib.createModalObject(),
        fonts: JDMT_FONTS,
        sizes: [10, 11, 12, 13, 14, 15, 16, 18, 20, 24, 30],
        opacities: [10, 20, 30, 40, 50, 60, 70, 80, 90],
        menuShapes: ['shape1', 'shape2', 'shape3', 'shape4'],
        serviceCatalog: JDMT_SERVICES,
      };
    },
    async created() {
      this.component_idx = this.lib.generateUniqueId();
      this.api.component_name = this.component_name;
      this.row = this.defaultRow();
    },
    async mounted() {
      document.addEventListener('click', this.handleDocumentClick, true);
      this.table = await this.api.table("jd_mobile_top");
      await this.loadMenus();
      await this.loadHeaderSetting();
      await this.loadRow();
      this.normalizeButtonType();
      this.load = true;
    },
    beforeDestroy() {
      this.revokeButtonIconPreview();
      document.removeEventListener('click', this.handleDocumentClick, true);
    },
    computed: {
      slotKeys() {
        return ['slot_left', 'slot_center', 'slot_right_1', 'slot_right_2'];
      },
      hasLogo() {
        return this.slotKeys.some((slot) => this.row[slot] === 'logo');
      },
      hasMenu() {
        return this.slotKeys.some((slot) => this.row[slot] === 'menu');
      },
      hasButton() {
        return this.slotKeys.some((slot) => this.row[slot] === 'button');
      },
      freeSlotTypes() {
        return ['logo', 'menu', 'button'].filter((type) => {
          return !this.slotKeys.some((slot) => this.row[slot] === type);
        });
      },
      logoSrc() {
        return this.row.logo_upload_file?.src || this.row.logo_image_src || '';
      },
      hbLogoSrc() {
        return this.row.hb_logo_upload_file?.src || this.row.hb_logo_image_src || '';
      },
      buttonSrc() {
        return this.row.button_icon_upload_file?.src || this.buttonIconPreviewUrl || this.row.button_icon_src || '';
      },
      parsedButtonIcon() {
        if (!this.row.button_icon_src || this.row.button_icon_mode === 'image') {
          return null;
        }

        const parts = String(this.row.button_icon_src).split(':');
        if (parts.length < 2) {
          return null;
        }

        return {
          library: parts[0],
          name: parts[1],
        };
      },
      buttonIconClass() {
        if (!this.parsedButtonIcon) {
          return '';
        }

        const { library, name } = this.parsedButtonIcon;

        if (library === 'bootstrap') {
          return `bi bi-${name}`;
        }
        if (library === 'phosphor') {
          return `ph ph-${name}`;
        }
        if (library === 'simpleline') {
          return `icon-${name}`;
        }

        return '';
      },
      buttonIconStyle() {
        return {
          color: this.row.button_icon_color,
          fontSize: `${this.row.button_icon_size}px`,
          display: 'inline-flex',
          alignItems: 'center',
          justifyContent: 'center',
        };
      },
      buttonImageStyle() {
        return {
          height: `${Number(this.row.button_icon_size || 30)}px`,
          width: 'auto',
          maxWidth: '100%',
          maxHeight: 'none',
          objectFit: 'contain',
          display: 'block',
        };
      },
      buttonPreviewImageStyle() {
        const size = Math.max(1, Number(this.row.button_icon_size || 30));
        return `width:${size}px !important;height:${size}px !important;max-width:none !important;max-height:none !important;object-fit:contain !important;display:block !important;`;
      },
      logoTextStyle() {
        return {
          fontFamily: `${this.row.logo_font}, sans-serif`,
          color: this.row.logo_font_color,
          fontSize: `${this.row.logo_font_size}px`,
          fontWeight: 700,
        };
      },
      logoImageStyle() {
        return {
          height: `${Number(this.row.logo_image_size || 30)}px`,
          width: 'auto',
          maxWidth: 'none',
          maxHeight: 'none',
          objectFit: 'contain',
          display: 'block',
          flex: 'none',
        };
      },
      hbLogoImageStyle() {
        return {
          height: `${Number(this.row.hb_logo_size || 20)}px`,
          width: 'auto',
          maxWidth: '140px',
          maxHeight: 'none',
          objectFit: 'contain',
          display: 'block',
        };
      },
      topStyle() {
        return {
          height: `${this.resolvedTopHeight}px`,
          backgroundColor: this.row.top_bg_color,
          boxShadow: this.row.top_shadow_use
            ? `0 4px 8px rgba(0,0,0,${(this.row.top_shadow_opacity || 10) / 100})`
            : 'none',
        };
      },
      pageOptions() {
        return this.flatMenus(this.menus).map((item) => {
          return {
            label: item.name,
            value: item.url ? `/${String(item.url).replace(/^\/+/, '')}` : '/',
          };
        });
      },
      submenuPreview() {
        if (this.menus.length) {
          return this.menus.map((item) => item.name);
        }

        return ['메뉴명1', '메뉴명2', '메뉴명3', '메뉴명4', '메뉴명5'];
      },
      buttonTypes() {
        const types = [
          { value: 'link', label: '링크' },
          { value: 'page', label: '페이지' },
          { value: 'tel', label: '전화걸기' },
        ];

        if (this.mobileLoginEnabled) {
          types.push({ value: 'mypage', label: '마이페이지' });
        }

        return types;
      },
      drawerPreview() {
        if (this.menus.length) {
          return this.menus.slice(0, 4).map((item) => item.name);
        }

        return ['메뉴1', '메뉴2', '메뉴3', '메뉴4'];
      },
      drawerPreviewMenus() {
        if (this.menus.length) {
          let opened = false;
          return this.menus.slice(0, 4).map((item) => {
            const children = this.childMenus(item).slice(0, 4).map((child) => child.name);
            const hasChildren = children.length > 0;
            const previewOpen = !opened && hasChildren;
            if (previewOpen) {
              opened = true;
            }
            return {
              name: item.name,
              children,
              hasChildren,
              previewOpen,
            };
          });
        }

        return [
          { name: '메뉴1', children: ['서브메뉴1', '서브메뉴2'], hasChildren: true, previewOpen: true },
          { name: '메뉴2', children: [], hasChildren: false, previewOpen: false },
          { name: '메뉴3', children: [], hasChildren: false, previewOpen: false },
          { name: '메뉴4', children: [], hasChildren: false, previewOpen: false },
        ];
      },
      selectedServices() {
        return (this.row.hb_bottom_services || []).map((item) => {
          return Object.assign({}, this.serviceMeta(item.key), item, {
            key: this.normalizeServiceKey(item.key),
          });
        });
      },
      authBtnStyle() {
        return {
          backgroundColor: this.row.hb_auth_bg_color,
          color: this.row.hb_auth_font_color,
          borderColor: this.row.hb_auth_border_color,
        };
      },
      modalCtx() {
        return this;
      },
      resolvedTopHeight() {
        const topHeight = Number(this.row.top_height || 50);
        const logoSize = Number(this.row.logo_image_size || 30);
        const hasImageLogo = this.row.logo_type === 'image' && this.hasLogo;

        return Math.max(topHeight, hasImageLogo ? logoSize : 0);
      },
    },
    methods: {
    defaultRow(){return{
      idx:'',site_code:this.site_code,member_code:this.member_code,top_height:50,top_bg_color:'#ffffff',top_fixed:true,top_shadow_use:false,top_shadow_opacity:10,
      slot_left:'logo',slot_center:'',slot_right_1:'',slot_right_2:'menu',logo_type:'image',logo_text:'',logo_font:'Pretendard',logo_font_size:14,logo_font_color:'#000000',
      logo_image_src:'',logo_image_size:30,logo_upload_file:'',menu_icon_style:'shape1',menu_icon_color:'#000000',menu_icon_size:24,button_type:'link',button_value:'',
      button_icon_mode:'',button_icon_src:'',button_icon_color:'#000000',button_icon_size:30,button_icon_upload_file:'',submenu_type:'slide',submenu_height:30,
      submenu_font_size:14,submenu_inactive_bg_color:'#ffffff',submenu_inactive_font_color:'#000000',submenu_inactive_border_color:'#a4a4a4',submenu_active_bg_color:'#373737',
      submenu_active_font_color:'#ffffff',submenu_active_border_color:'#a4a4a4',hb_position:'right',hb_top_bg_color:'#ffffff',hb_close_color:'#000000',hb_logo_use:false,
      hb_logo_image_src:'',hb_logo_size:20,hb_logo_upload_file:'',hb_show_nickname:true,hb_show_email:true,hb_info_font_color:'#000000',hb_auth_bg_color:'#ffffff',
      hb_auth_font_color:'#000000',hb_auth_border_color:'#d3d3d3',hb_menu_bg_color:'#ffffff',hb_menu_divider_color:'#d3d3d3',hb_menu_arrow_color:'#000000',
      hb_menu_font_size:16,hb_menu_font_color:'#000000',hb_submenu_font_size:14,hb_submenu_font_color:'#454545',hb_bottom_bg_color:'#ffffff',hb_bottom_services:[]
    };},
    logoTypeThumbStyle(type) {
      const active = this.row.logo_type === type;
      return {
        borderColor: active ? '#ff6b45' : '#222222',
        boxShadow: active ? 'inset 0 0 0 1px #ff6b45' : 'none',
      };
    },
    logoTypeRadioStyle(type) {
      return {
        borderColor: this.row.logo_type === type ? '#ff6b45' : '#bdbdbd',
      };
    },
    async loadMenus() {
      const table = await this.api.table("menu");

      table.where("site_code", this.site_code);
      table.where("parent_code", "null");
      table.where("is_use_header", "Y");
      table.orderBy("order", "ASC");
      table.relations("menu", "second").where("parent_code", "$parent.code");
      table.relations("second").orderBy("order", "ASC");

      this.menus = await table.get();
    },
    parseSettingData(value) {
      if (!value) {
        return {};
      }
      if (typeof value === 'object') {
        return value;
      }

      try {
        return JSON.parse(value) || {};
      } catch (e) {
        return {};
      }
    },
    async loadHeaderSetting() {
      const table = await this.api.table("header");
      table.where("site_code", this.site_code);

      const header = (await table.get())[0] || {};
      const settingData = this.parseSettingData(header.setting_data || header.settingData);
      this.mobileLoginEnabled = settingData.is_use_mobile_login === 'Y';
    },
    async loadRow() {
      const table = await this.api.table("jd_mobile_top");
      table.where("site_code", this.site_code);

      const rows = await table.get();
      this.row = rows.length ? this.hydrate(rows[0]) : this.defaultRow();
    },
    hydrate(raw) {
      const row = Object.assign(this.defaultRow(), raw || {});
      const numberKeys = [
        'top_height',
        'top_shadow_opacity',
        'logo_font_size',
        'logo_image_size',
        'menu_icon_size',
        'button_icon_size',
        'submenu_height',
        'submenu_font_size',
        'hb_logo_size',
        'hb_menu_font_size',
        'hb_submenu_font_size',
      ];

      numberKeys.forEach((key) => {
        row[key] = parseInt(row[key] || this.defaultRow()[key], 10);
      });
      row.top_fixed = this.toBool(row.top_fixed, true);
      row.top_shadow_use = this.toBool(row.top_shadow_use, false);
      row.hb_logo_use = this.toBool(row.hb_logo_use, false);
      row.hb_show_nickname = this.toBool(row.hb_show_nickname, true);
      row.hb_show_email = this.toBool(row.hb_show_email, true);
      row.hb_bottom_services = [];

      [1, 2, 3].forEach((index) => {
        const key = raw[`hb_bottom_button_${index}_type`];
        const value = raw[`hb_bottom_button_${index}_value`] || '';

        if (key) {
          row.hb_bottom_services.push({
            key: this.normalizeServiceKey(key),
            value,
          });
        }
      });

      row.logo_upload_file = '';
      row.button_icon_upload_file = '';
      row.hb_logo_upload_file = '';

      return row;
    },
    toBool(value, def) {
      if (value === undefined || value === null || value === '') {
        return def;
      }

      return (
        value === true ||
        value === 'Y' ||
        value === 'true' ||
        value === '1' ||
        value === 1
      );
    },
    normalizeButtonType() {
      if (!this.mobileLoginEnabled && this.row.button_type === 'mypage') {
        this.row.button_type = 'link';
        this.row.button_value = '';
      }
    },
    changeButtonType(type) {
      if (this.row.button_type === type) {
        return;
      }

      this.row.button_type = type;
      this.row.button_value = '';
    },
    flatMenus(menus) {
      let rows = [];

      (menus || []).forEach((item) => {
        rows.push(item);

        const children = this.childMenus(item);
        if (children.length) {
          rows = rows.concat(children);
        }
      });

      return rows;
    },
    childMenus(item) {
      return item?.$second?.data || item.second || [];
    },
    slotLabel(type) {
      return {
        logo: '로고',
        menu: '메뉴',
        button: '버튼',
      }[type] || type;
    },
    toggleSlotMenu(slot) {
      this.activeSlot = this.activeSlot === slot ? '' : slot;
    },
    setSlotType(slot, type) {
      this.row[slot] = type;
      this.activeSlot = '';
    },
    setSubmenuType(type) {
      this.row.submenu_type = type;
    },
    handleDocumentClick(event) {
      if (!this.activeSlot) {
        return;
      }

      if (
        event.target.closest('.jdmt-slot-empty') ||
        event.target.closest('.jdmt-slot-actions')
      ) {
        return;
      }

      this.activeSlot = '';
    },
    swapSlot(target) {
      if (!this.dragging || this.dragging === target) {
        return;
      }

      const temp = this.row[target];
      this.row[target] = this.row[this.dragging];
      this.row[this.dragging] = temp;
      this.dragging = '';
      this.activeSlot = '';
    },
    selectIcon(icon) {
      this.revokeButtonIconPreview();
      this.row.button_icon_mode = 'svg';
      this.row.button_icon_src = icon.src;
      this.row.button_icon_color = icon.color;
      this.row.button_icon_size = icon.size;
      this.row.button_icon_upload_file = '';
    },
    revokeButtonIconPreview() {
      if (this.buttonIconPreviewUrl && window.URL?.revokeObjectURL) {
        window.URL.revokeObjectURL(this.buttonIconPreviewUrl);
      }
      this.buttonIconPreviewUrl = '';
    },
    changeButtonIconFile(event) {
      this.revokeButtonIconPreview();

      const file = event?.target?.files?.[0] || null;
      if (!file) {
        this.row.button_icon_upload_file = '';
        return;
      }

      this.row.button_icon_upload_file = file;
      this.row.button_icon_mode = 'image';
      this.row.button_icon_src = '';

      if (window.URL?.createObjectURL) {
        this.buttonIconPreviewUrl = window.URL.createObjectURL(file);
      }

      if (event?.target) {
        event.target.value = '';
      }
    },
    clearButtonIcon() {
      this.revokeButtonIconPreview();
      this.row.button_icon_src = '';
      this.row.button_icon_mode = '';
      this.row.button_icon_upload_file = '';
    },
    renderFeatherIcon(name, size, color) {
      if (typeof feather !== 'undefined' && feather.icons[name]) {
        return feather.icons[name].toSvg({
          width: size,
          height: size,
          color,
        });
      }

      return '';
    },
    menuIcon(shape, size, color) {
      const safeSize = parseInt(size || 30, 10);
      const iconHeight = Number.isFinite(safeSize) ? Math.max(1, safeSize) : 30;
      const styleNo = Math.min(4, Math.max(1, parseInt(String(shape || 'shape1').replace(/\D/g, ''), 10) || 1));
      const iconWidth = styleNo === 4 ? iconHeight : Math.round(iconHeight * 17 / 16);
      const safeColor = String(color || '#000000').replace(/[;"'<>]/g, '');
      const src = `/file/icon/mobile_top/hambergur_icon_style_${styleNo}.svg`;

      return `<span class="jdmt-hamburger-icon" style="display:inline-block;width:${iconWidth}px;height:${iconHeight}px;background-color:${safeColor};-webkit-mask:url('${src}') center / contain no-repeat;mask:url('${src}') center / contain no-repeat;"></span>`;
    },
    iconHtml(src, size, color) {
      if (!src) {
        return '';
      }

      const parts = String(src).split(':');
      if (parts.length < 2) {
        return '';
      }

      const library = parts[0];
      const name = parts[1];

      if (library === 'feather' && typeof feather !== 'undefined' && feather.icons[name]) {
        return feather.icons[name].toSvg({
          width: size,
          height: size,
          color,
        });
      }
      if (library === 'bootstrap') {
        return `<i class="bi bi-${name}" style="color:${color};font-size:${size}px;"></i>`;
      }
      if (library === 'phosphor') {
        return `<i class="ph ph-${name}" style="color:${color};font-size:${size}px;"></i>`;
      }
      if (library === 'simpleline') {
        return `<i class="icon-${name}" style="color:${color};font-size:${size}px;"></i>`;
      }

      return '<span style="font-size:12px;color:#999">icon</span>';
    },
    drawerPreviewArrow(open) {
      const direction = open ? 'up' : 'down';
      return `<i class="fal fa-chevron-${direction}" style="color:${this.row.hb_menu_arrow_color};font-size:20px;"></i>`;
    },
    normalizeServiceKey(key) {
      if (!key) {
        return '';
      }

      return key === 'center' ? 'customer_center' : key;
    },
    submenuStyle(active) {
      return {
        height: `${this.row.submenu_height}px`,
        lineHeight: `${this.row.submenu_height}px`,
        fontSize: `${this.row.submenu_font_size}px`,
        backgroundColor: active ? this.row.submenu_active_bg_color : this.row.submenu_inactive_bg_color,
        color: active ? this.row.submenu_active_font_color : this.row.submenu_inactive_font_color,
        borderColor: active
          ? this.row.submenu_active_border_color
          : this.row.submenu_inactive_border_color,
      };
    },
    serviceMeta(key) {
      const normalizedKey = this.normalizeServiceKey(key);
      return this.serviceCatalog.find((service) => {
        return this.normalizeServiceKey(service.key) === normalizedKey;
      }) || this.serviceCatalog[0];
    },
    serviceStyle(key) {
      const service = this.serviceMeta(key);
      if (service.iconType === 'svg') {
        return {
          background: 'transparent',
          color: 'inherit',
          border: 'none',
        };
      }

      return {
        background: service.bg,
        color: service.color,
        border: service.border ? `1px solid ${service.border}` : 'none',
      };
    },
    hasService(key) {
      const normalizedKey = this.normalizeServiceKey(key);
      return this.row.hb_bottom_services.some((item) => {
        return this.normalizeServiceKey(item.key) === normalizedKey;
      });
    },
    toggleService(key, event) {
      const normalizedKey = this.normalizeServiceKey(key);
      const checked = event.target.checked;

      if (checked) {
        if (this.hasService(normalizedKey)) {
          return;
        }
        if (this.row.hb_bottom_services.length >= 3) {
          event.target.checked = false;
          this.lib.alert('하단 버튼은 최대 3개까지 가능');
          return;
        }

        this.row.hb_bottom_services.push({
          key: normalizedKey,
          value: '',
        });
        return;
      }

      this.row.hb_bottom_services = this.row.hb_bottom_services.filter((item) => {
        return this.normalizeServiceKey(item.key) !== normalizedKey;
      });
    },
    async uploadFile(file, memo) {
      const sessions = await this.session.all();
      const res = await this.lib.ajax("file_save", {
        user_idx: sessions.mem_idx || '',
        upload_file: file,
        memo,
        site_code: this.site_code,
      });

      return res?.file?.src || '';
    },
    async processUploads() {
      if (this.row.logo_upload_file) {
        const src = await this.uploadFile(this.row.logo_upload_file, 'jd_mobile_top_logo');
        if (src) {
          this.row.logo_image_src = src;
        }
        this.row.logo_upload_file = '';
      }

      if (this.row.button_icon_upload_file) {
        const src = await this.uploadFile(this.row.button_icon_upload_file, 'jd_mobile_top_button');
        if (src) {
          this.row.button_icon_src = src;
        }
        this.row.button_icon_mode = 'image';
        this.row.button_icon_upload_file = '';
        this.revokeButtonIconPreview();
      }

      if (this.row.hb_logo_upload_file) {
        const src = await this.uploadFile(this.row.hb_logo_upload_file, 'jd_mobile_top_hb_logo');
        if (src) {
          this.row.hb_logo_image_src = src;
        }
        this.row.hb_logo_upload_file = '';
      }
    },
    nowDatetime() {
      const d = new Date();
      const p = (n) => String(n).padStart(2, '0');

      return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
    },
    serialize() {
      const now = this.nowDatetime();
      const payload = {
        primary: this.row.idx || '',
        idx: this.row.idx || '',
        site_code: this.site_code,
        member_code: this.member_code,
        top_height: this.row.top_height,
        top_bg_color: this.row.top_bg_color,
        top_fixed: this.row.top_fixed ? 'Y' : 'N',
        top_shadow_use: this.row.top_shadow_use ? 'Y' : 'N',
        top_shadow_opacity: this.row.top_shadow_opacity,
        slot_left: this.row.slot_left,
        slot_center: this.row.slot_center,
        slot_right_1: this.row.slot_right_1,
        slot_right_2: this.row.slot_right_2,
        logo_type: this.row.logo_type,
        logo_text: this.row.logo_text,
        logo_font: this.row.logo_font,
        logo_font_size: this.row.logo_font_size,
        logo_font_color: this.row.logo_font_color,
        logo_image_src: this.row.logo_image_src,
        logo_image_size: this.row.logo_image_size,
        menu_icon_style: this.row.menu_icon_style,
        menu_icon_color: this.row.menu_icon_color,
        menu_icon_size: this.row.menu_icon_size,
        button_type: this.row.button_type,
        button_value: this.row.button_value,
        button_icon_mode: this.row.button_icon_mode,
        button_icon_src: this.row.button_icon_src,
        button_icon_color: this.row.button_icon_color,
        button_icon_size: this.row.button_icon_size,
        submenu_type: this.row.submenu_type,
        submenu_height: this.row.submenu_height,
        submenu_font_size: this.row.submenu_font_size,
        submenu_inactive_bg_color: this.row.submenu_inactive_bg_color,
        submenu_inactive_font_color: this.row.submenu_inactive_font_color,
        submenu_inactive_border_color: this.row.submenu_inactive_border_color,
        submenu_active_bg_color: this.row.submenu_active_bg_color,
        submenu_active_font_color: this.row.submenu_active_font_color,
        submenu_active_border_color: this.row.submenu_active_border_color,
        hb_position: this.row.hb_position,
        hb_top_bg_color: this.row.hb_top_bg_color,
        hb_close_color: this.row.hb_close_color,
        hb_logo_use: this.row.hb_logo_use ? 'Y' : 'N',
        hb_logo_image_src: this.row.hb_logo_image_src,
        hb_logo_size: this.row.hb_logo_size,
        hb_show_nickname: this.row.hb_show_nickname ? 'Y' : 'N',
        hb_show_email: this.row.hb_show_email ? 'Y' : 'N',
        hb_info_font_color: this.row.hb_info_font_color,
        hb_auth_bg_color: this.row.hb_auth_bg_color,
        hb_auth_font_color: this.row.hb_auth_font_color,
        hb_auth_border_color: this.row.hb_auth_border_color,
        hb_menu_bg_color: this.row.hb_menu_bg_color,
        hb_menu_divider_color: this.row.hb_menu_divider_color,
        hb_menu_arrow_color: this.row.hb_menu_arrow_color,
        hb_menu_font_size: this.row.hb_menu_font_size,
        hb_menu_font_color: this.row.hb_menu_font_color,
        hb_submenu_font_size: this.row.hb_submenu_font_size,
        hb_submenu_font_color: this.row.hb_submenu_font_color,
        hb_bottom_bg_color: this.row.hb_bottom_bg_color,
        hb_bottom_button_1_type: '',
        hb_bottom_button_1_value: '',
        hb_bottom_button_2_type: '',
        hb_bottom_button_2_value: '',
        hb_bottom_button_3_type: '',
        hb_bottom_button_3_value: '',
        update_date: now,
      };

      if (!this.row.idx) {
        payload.insert_date = now;
      }

      this.selectedServices.forEach((service, index) => {
        const no = index + 1;
        payload[`hb_bottom_button_${no}_type`] = service.key;
        payload[`hb_bottom_button_${no}_value`] = service.value || '';
      });

      return payload;
    },
    async save() {
      window.design?.loadingOn?.();

      this.activeSlot = '';
      await this.processUploads();
      await this.table.post(this.serialize(), { return: true });
      await this.loadRow();

      window.design?.menu?.reload?.();
      window.design?.loadingOff?.();

      $('#mobile_top_setting_jd').modal('hide');
    }
  }
}});
</script>
<style>
.jdmt{background:#fff;min-height:560px;display:flex;flex-direction:column;font-size:14px}
.jdmt-head{padding:16px 24px;border-bottom:1px solid #ececec;text-align:center}
.jdmt-title{font-size:18px;font-weight:700;color:#222}
.jdmt-body{padding:18px 20px 24px;display:flex;flex-direction:column;gap:14px}
.jdmt-tabs{display:inline-flex;flex-wrap:wrap;border:1px solid #ddd;border-radius:14px;overflow:hidden;width:fit-content;max-width:100%}
.jdmt-tab{min-width:152px;height:48px;border:0;background:#fff;font-size:14px;font-weight:700;color:#222;border-right:1px solid #ddd;padding:0 16px}
.jdmt-tab:last-child{border-right:0}
.jdmt-tab.on{background:#ff9b84;color:#fff}
.jdmt-sec{border-top:1px solid #ececec;padding-top:28px}
.jdmt-sec + .jdmt-sec{margin-top:34px}
.jdmt-sec:after{display:none !important;content:none !important}
.jd-image-modal-title{display:flex;align-items:center;gap:8px;font-size:16px;font-weight:700;color:#222;margin:0 0 16px}
.jd-image-modal-title img{display:block;height:18px;width:auto;flex:none}
.jdmt-grid{display:grid;gap:14px 18px;align-items:end}
.jdmt-grid4{grid-template-columns:repeat(4,minmax(180px,1fr))}
.jdmt-grid3{grid-template-columns:repeat(3,minmax(0,1fr))}
.jdmt-field{display:flex;flex-direction:column;gap:6px}
.jdmt-picker-field{align-items:center;text-align:center}
.jdmt-span2{grid-column:span 2}
.jdmt-span4{grid-column:1 / -1}
.jdmt-field label{font-size:13px;font-weight:600;color:#333;margin:0;line-height:1.4}
.jdmt-help-label{display:inline-flex;align-items:center;gap:6px}
.jdmt-help-wrap{position:relative;display:inline-flex;align-items:center}
.jdmt-help-icon{width:18px;height:18px;border:0;border-radius:50%;background:#8e939a;color:#fff;font-size:11px;font-weight:700;line-height:1;display:inline-flex;align-items:center;justify-content:center;padding:0;cursor:pointer}
.jdmt-help-tooltip{display:none;position:absolute;left:50%;bottom:calc(100% + 10px);transform:translateX(-50%);width:360px;max-width:calc(100vw - 40px);height:auto;min-height:0;box-sizing:border-box;background:#fff;color:#666;padding:12px 14px;border:1px solid #d8d8d8;border-radius:6px;font-size:12px;font-weight:500;line-height:1.55;box-shadow:0 4px 10px rgba(0,0,0,.08);z-index:5000;white-space:normal;word-break:keep-all;overflow:visible;pointer-events:none}
.jdmt-help-tooltip:before{content:'';position:absolute;left:50%;bottom:-7px;transform:translateX(-50%) rotate(45deg);width:12px;height:12px;background:#fff;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8}
.jdmt-help-tooltip.active{display:block}
.jdmt-button-settings-grid .jdmt-help-tooltip{left:auto;right:0;transform:none}
.jdmt-button-settings-grid .jdmt-help-tooltip:before{left:auto;right:3px;transform:rotate(45deg)}
.jdmt-inline,.jdmt-check,.jdmt-shape-row,.jdmt-button-type-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.jdmt-number-inline{display:inline-flex;flex-wrap:nowrap;align-items:center;gap:8px}
.jdmt-font-inline{align-items:center}
.jdmt-check{margin:0}
.jdmt-check input[type=checkbox],
.jdmt-check input[type=radio]{width:16px;height:16px;accent-color:#ff5a47;margin:0}
.jdmt-field-toggle{justify-content:flex-end}
.jdmt-check-lg{gap:10px;flex-wrap:nowrap}
.jdmt-check-lg span{font-size:15px;font-weight:600;color:#333;line-height:1;white-space:nowrap}
.jdmt-check-lg input[type=checkbox],
.jdmt-check-lg input[type=radio]{width:18px;height:18px}
.jdmt-shadow-inline{display:inline-flex;align-items:center;gap:12px;flex-wrap:nowrap}
.jdmt-basic-row{display:flex;align-items:flex-start;gap:28px;flex-wrap:wrap}
.jdmt-basic-row .jdmt-field{flex:none}
.jdmt-basic-height{width:auto}
.jdmt-basic-color{width:auto;min-width:118px;align-items:center;text-align:center}
.jdmt-basic-toggle{height:auto;justify-content:flex-start;padding-top:24px}
.jdmt-basic-shadow{height:auto;justify-content:flex-start;padding-top:24px}
.jdmt-basic-row .jdmt-check-lg,
.jdmt-basic-row .jdmt-shadow-inline{min-height:36px}
.jdmt .jdmt-basic-row .form-control{height:36px}
.jdmt-logo-layout{display:flex;align-items:flex-start;gap:28px;flex-wrap:nowrap}
.jdmt-logo-type-field{gap:8px;flex:none;width:200px}
.jdmt-logo-type-group{display:flex;flex-wrap:nowrap;gap:8px}
.jdmt-logo-type-option{position:relative;display:flex;flex-direction:column;gap:8px;cursor:pointer;margin:0}
.jdmt-logo-type-option input{position:absolute;opacity:0;pointer-events:none}
.jdmt-logo-type-thumb{width:92px;height:50px;border:1px solid #222;border-radius:8px;display:flex;align-items:center;justify-content:center;background:#fff;transition:.15s ease}
.jdmt-logo-type-thumb img{max-width:74px;max-height:30px;width:auto;height:auto}
.jdmt-logo-type-caption{display:flex;align-items:center;gap:6px;font-size:13px;font-weight:500;color:#333;line-height:1}
.jdmt-logo-type-radio{width:14px;height:14px;border:1.5px solid #bdbdbd;border-radius:50%;background:#fff;position:relative;flex:none}
.jdmt-logo-type-option.on .jdmt-logo-type-thumb{border-color:#ff6b45;box-shadow:inset 0 0 0 1px #ff6b45}
.jdmt-logo-type-option.on .jdmt-logo-type-radio{border-color:#ff6b45}
.jdmt-logo-type-radio-dot{position:absolute;inset:3px;border-radius:50%;background:#ff6b45}
.jdmt-submenu-type-field{gap:10px}
.jdmt-submenu-type-group{display:flex;align-items:flex-start;gap:22px;flex-wrap:nowrap}
.jdmt-submenu-type-option{position:relative;display:flex;flex-direction:column;gap:8px;cursor:pointer;margin:0;padding:0;border:0;background:transparent;text-align:left}
.jdmt-submenu-type-thumb{width:126px;height:38px;display:flex;align-items:flex-end;justify-content:center}
.jdmt-submenu-type-thumb img{display:block;max-width:126px;max-height:38px;width:auto;height:auto}
.jdmt-submenu-type-caption{display:flex;align-items:center;gap:7px;font-size:14px;font-weight:500;color:#333;line-height:1}
.jdmt-submenu-type-radio{width:16px;height:16px;border:1.5px solid #bdbdbd;border-radius:50%;background:#fff;position:relative;flex:none}
.jdmt-submenu-type-radio.on{border-color:#ff6b45}
.jdmt-submenu-type-radio-dot{position:absolute;inset:3px;border-radius:50%;background:#ff6b45}
.jdmt-submenu-config-grid{align-items:start}
.jdmt-logo-controls{display:grid;align-items:start;gap:14px 14px;flex:none;min-width:0}
.jdmt-logo-controls-image{grid-template-columns:max-content 104px}
.jdmt-logo-controls-text{grid-template-columns:220px 150px 92px 52px}
.jdmt-logo-upload-field .jdmt-inline{flex-wrap:nowrap}
.jdmt-logo-upload-field .jdmt-preview{min-width:64px}
.jdmt-logo-size-field{width:104px}
.jdmt .form-control{height:34px;font-size:13px;padding:6px 10px}
.jdmt-mini{width:82px}
.jdmt-type{width:110px}
.jdmt-grow{flex:1;min-width:220px}
.jdmt-menu-settings-grid{grid-template-columns:258px 92px 124px;gap:14px 34px;align-items:start;justify-content:start}
.jdmt-menu-settings-grid .jdmt-span2{grid-column:auto}
.jdmt-menu-shape-row{display:flex;align-items:center;gap:7px;flex-wrap:nowrap}
.jdmt-menu-style-field{min-width:0}
.jdmt-button-settings-grid{grid-template-columns:minmax(340px,1fr) max-content 124px;gap:14px 28px;align-items:end}
.jdmt-button-action-field .jdmt-inline{flex-wrap:nowrap}
.jdmt-button-icon-field{justify-self:center}
.jdmt-button-icon-field .jdmt-inline{flex-wrap:nowrap}
.jdmt-button-icon-field .jdmt-preview{flex:none}
.jdmt-number-input{width:78px !important;min-width:78px}
.jdmt-unit{display:inline-flex;align-items:center;font-size:13px;color:#555;white-space:nowrap}
.jdmt-help{font-size:12px;color:#8b8b8b;line-height:1.5}
.jdmt-top{width:min(660px,100%);margin:0 auto 52px;border:1px solid #cfcfcf;display:grid;grid-template-columns:minmax(110px,1fr) minmax(110px,1fr) 58px 58px;gap:12px;align-items:center;padding:0 16px;box-sizing:border-box}
.jdmt-preview-stack .jdmt-top{position:relative;width:400px;max-width:100%;grid-template-columns:minmax(0,1fr) 52px 52px;grid-template-rows:minmax(0,1fr);gap:8px;padding:0 20px;overflow:visible}
.jdmt-preview-stack .jdmt-top .jdmt-slot.slot_left{grid-column:1;grid-row:1}
.jdmt-preview-stack .jdmt-top .jdmt-slot.slot_center{position:absolute;top:0;bottom:0;left:50%;width:120px;transform:translateX(-50%);z-index:1}
.jdmt-preview-stack .jdmt-top .jdmt-slot.slot_right_1{grid-column:2;grid-row:1}
.jdmt-preview-stack .jdmt-top .jdmt-slot.slot_right_2{grid-column:3;grid-row:1}
.jdmt-top-linked{margin-bottom:0}
.jdmt-top-readonly .jdmt-slot-fill{cursor:default}
.jdmt-slot{height:100%;display:flex;align-items:center;justify-content:center;position:relative}
.jdmt-preview-stack .jdmt-slot{height:100%;min-height:0;align-self:stretch;overflow:visible}
.jdmt-slot.slot_left{justify-content:flex-start}
.jdmt-slot-readonly{width:100%;display:flex;justify-content:center}
.jdmt-slot.slot_left .jdmt-slot-readonly{justify-content:flex-start}
.jdmt-slot-placeholder{width:40px;height:40px}
.jdmt-slot-empty{position:relative;width:40px;height:40px;border:2px dashed #d1d1d1;border-radius:8px;display:flex;align-items:center;justify-content:center;color:#bbb;font-size:28px;cursor:pointer;transition:.15s ease}
.jdmt-slot-empty.on{background:#e6f2ff;border-color:#9ec6f5;color:#89aed8}
.jdmt-slot-actions{position:absolute;top:calc(100% + 8px);left:50%;transform:translateX(-50%);display:flex;flex-direction:column;gap:6px;z-index:20}
.jdmt-slot-fill{width:100%;display:flex;justify-content:center;position:relative;cursor:grab}
.jdmt-preview-stack .jdmt-slot-fill{height:100%;min-height:0;align-items:center}
.jdmt-preview-stack .jdmt-slot-readonly{height:100%;min-height:0;align-items:center}
.jdmt-slot.slot_left .jdmt-slot-fill{justify-content:flex-start}
.jdmt-slot.slot_left .jdmt-slot-empty{margin-right:auto}
.jdmt-slot-bg{background:transparent;min-width:52px;min-height:100%;display:flex;align-items:center;justify-content:center;padding:0 6px}
.jdmt-preview-stack .jdmt-slot-bg{height:100%;min-height:0;max-height:none;overflow:visible}
.jdmt-preview-stack .jdmt-slot-bg>span:not(.jdmt-logo-text):not(.jdmt-fallback){display:inline-flex;align-items:center;justify-content:center;line-height:0}
.jdmt-preview-stack .jdmt-slot-bg i,
.jdmt-preview-stack .jdmt-slot-bg svg,
.jdmt-preview-stack .jdmt-slot-bg .jdmt-hamburger-icon{display:block !important;line-height:1}
.jdmt-float-btn{border:1px solid #c8c8c8;background:#fff;border-radius:10px;padding:4px 10px;font-size:12px;color:#ff5d44;box-shadow:0 8px 20px rgba(0,0,0,.08);white-space:nowrap}
.jdmt-slot-fill>.jdmt-float-btn{position:absolute;bottom:-42px;left:50%;transform:translateX(-50%);z-index:21}
.jdmt-slot.slot_left .jdmt-slot-fill>.jdmt-float-btn{left:0;transform:none}
.jdmt-logo-img{display:block;width:auto;max-width:none;max-height:none;object-fit:contain;flex:none}
.jdmt-logo-text{font-weight:700}
.jdmt-btn-img{display:block;width:auto;max-width:100%;object-fit:contain}
.jdmt-preview-btn-img{flex:none}
.jdmt-fallback{font-size:18px;font-weight:700;color:#b1b1b1}
.jdmt-btn{min-width:96px;height:40px;border-radius:10px;border:1px solid #d6d6d6;font-size:14px;font-weight:700;padding:0 14px}
.jdmt-orange{background:#ff6b45;border-color:#ff6b45;color:#fff}
.jdmt-white{background:#fff;color:#444}
.jdmt-preview{position:relative;display:inline-flex;align-items:center;justify-content:center;min-width:74px;min-height:40px;padding:4px 10px;border:1px solid #d7d7d7;border-radius:10px;background:#fff}
.jdmt-preview img{max-width:96px;max-height:30px;object-fit:contain}
.jdmt-remove{position:absolute;top:-8px;right:-8px;width:24px;height:24px;border-radius:50%;border:1px solid #bbb;background:#fff;font-size:14px;line-height:1}
.jdmt-shape{width:58px;height:42px;border-radius:8px;border:1px solid #d0d0d0;background:#fff;display:inline-flex;align-items:center;justify-content:center;padding:0}
.jdmt-shape.on{border-color:#ff6b45;box-shadow:inset 0 0 0 1px #ff6b45}
.jdmt-sub-preview{display:flex;flex-direction:column;align-items:center}
.jdmt-sub-preview .jdmt-sub-none{border-top:0}
.jdmt-sub-top{width:min(660px,100%);height:70px;border:1px solid #d0d0d0;display:flex;align-items:center;justify-content:center;color:#d1d1d1;font-size:16px;font-weight:700}
.jdmt-sub-slide,.jdmt-sub-table{width:400px;max-width:100%;border-left:1px solid #d0d0d0;border-right:1px solid #d0d0d0;border-bottom:1px solid #d0d0d0;box-sizing:border-box}
.jdmt-sub-slide{display:flex;flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden}
.jdmt-sub-table{display:flex;flex-wrap:wrap;overflow:hidden}
.jdmt-sub-slide .jdmt-sub-item{flex:1 1 0;min-width:max-content;white-space:nowrap}
.jdmt-sub-table .jdmt-sub-item{flex:1 0 25%;min-width:0}
.jdmt-sub-table .jdmt-sub-item:nth-child(4n),
.jdmt-sub-table .jdmt-sub-item:last-child{border-right:0}
.jdmt-sub-item{text-align:center;border-right:1px solid transparent;border-bottom:1px solid transparent;font-weight:700;box-sizing:border-box;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:0 14px}
.jdmt-sub-none{width:400px;max-width:100%;height:70px;border:1px solid #d0d0d0;display:flex;align-items:center;justify-content:center;color:#d1d1d1;font-size:16px;font-weight:700}
.jdmt-status{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;margin-top:18px}
.jdmt-box{border:1px solid #ececec;border-radius:14px;padding:16px}
.jdmt-box-title{font-size:14px;font-weight:700;margin-bottom:12px}
.jdmt-drawer-wrap{display:flex;justify-content:center}
.jdmt-drawer{width:286px;min-height:380px;border:1px solid #d0d0d0;background:#fff;display:flex;flex-direction:column}
.jdmt-drawer.left{margin-right:auto}
.jdmt-drawer-top{min-height:52px;box-sizing:border-box;padding:14px 14px 8px;position:relative}
.jdmt-drawer-close{position:absolute;top:8px;right:12px;width:36px;height:36px;border:0;background:transparent;font-size:0;line-height:1;padding:0;color:inherit}
.jdmt-drawer-close:before,.jdmt-drawer-close:after{content:"";position:absolute;top:50%;left:50%;width:25px;height:1.5px;background:currentColor;border-radius:999px;transform-origin:center}
.jdmt-drawer-close:before{transform:translate(-50%,-50%) rotate(45deg)}
.jdmt-drawer-close:after{transform:translate(-50%,-50%) rotate(-45deg)}
.jdmt-drawer-logo{margin-bottom:12px}
.jdmt-drawer-logo img{max-width:92px;max-height:32px;object-fit:contain}
.jdmt-drawer-logo-empty{width:92px;height:32px;border:1px dashed #d4d4d4;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;color:#b6b6b6;background:#fafafa}
.jdmt-drawer-member{display:flex;flex-direction:column;gap:6px;margin-bottom:10px;font-size:13px;font-weight:600}
.jdmt-auth{height:28px;min-width:102px;border:1px solid;border-radius:2px;background:#fff;font-size:13px;font-weight:500}
.jdmt-drawer-main{min-height:52px;border-top:1px solid;display:flex;align-items:center;justify-content:space-between;padding:0 14px}
.jdmt-drawer-sub{border-top:1px solid;padding:14px 14px 14px 30px;display:flex;flex-direction:column;gap:14px}
.jdmt-drawer-sub-item{text-align:left}
.jdmt-drawer-bottom{margin-top:auto;min-height:70px;display:flex;align-items:center;justify-content:center;gap:16px;padding:12px}
.jdmt-hb-main{display:flex;flex-direction:column;gap:24px}
.jdmt-hb-block{display:flex;flex-direction:column;gap:12px}
.jdmt-hb-block-title{font-size:13px;font-weight:700;color:#222}
.jdmt-hb-grid{display:grid;gap:14px 18px;align-items:end}
.jdmt-hb-grid-basic{grid-template-columns:minmax(160px,200px)}
.jdmt-hb-grid-top,.jdmt-hb-grid-menu{grid-template-columns:repeat(5,minmax(0,1fr))}
.jdmt-hb-grid-login{grid-template-columns:max-content repeat(4,max-content);column-gap:24px;justify-content:start;align-items:start}
.jdmt-hb-field-sm{max-width:180px}
.jdmt-hb-toggle-field{justify-content:flex-end}
.jdmt-hb-toggle-field .jdmt-check{min-height:34px}
.jdmt-hb-logo-field{justify-content:flex-end}
.jdmt-hb-logo-inline{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-height:34px}
.jdmt-hb-logo-inline .jdmt-check{min-height:34px}
.jdmt-hb-upload{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.jdmt-hb-upload .jdmt-preview{min-width:64px}
.jdmt-hb-check-row{display:flex;flex-wrap:wrap;align-items:center;gap:10px 14px;min-height:42px}
.jdmt-hb-login-visibility{justify-content:flex-start}
.jdmt-hb-login-color{justify-content:flex-start}
.jdmt-hb-login-color label{white-space:nowrap}
.jdmt-hb-login-color .external-picker{align-self:flex-start}
.jdmt-hb-service-list{display:grid;grid-template-columns:repeat(8,minmax(72px,1fr));gap:18px 12px}
.jdmt-hb-service-item{display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;cursor:pointer;margin:0}
.jdmt-hb-service-item input[type=checkbox]{width:16px;height:16px;accent-color:#ff5a47;margin:0}
.jdmt-hb-service-item.on .jdmt-hb-service-icon{transform:translateY(-1px);box-shadow:0 8px 18px rgba(0,0,0,.12)}
.jdmt-hb-service-icon{transition:transform .15s ease, box-shadow .15s ease}
.jdmt-hb-service-name{font-size:12px;font-weight:600;color:#333;line-height:1.35;word-break:keep-all}
.jdmt-hb-service-check{display:flex;align-items:center;justify-content:center;min-height:20px}
.jdmt-hb-link-list{display:grid;gap:12px;margin-top:22px}
.jdmt-hb-preview-block{padding-top:0}
.jdmt-drawer-wrap-hb{justify-content:center}
.jdmt-drawer-wrap-hb .jdmt-drawer{width:286px;min-height:680px}
.jdmt-hb-shell{padding-top:0;border-top:0}
.jdmt-hb-page-title{margin:8px 0 18px;font-size:18px}
.jdmt-hb-layout{display:grid;grid-template-columns:286px minmax(0,1fr);gap:32px;align-items:start}
.jdmt-hb-aside{padding-top:6px}
.jdmt-hb-panel{display:flex;flex-direction:column;gap:24px;min-width:0}
.jdmt-hb-panel-section{padding-bottom:22px;border-bottom:1px solid #ececec}
.jdmt-hb-panel-section:last-child{padding-bottom:0;border-bottom:0}
.jdmt-hb-panel-title{font-size:17px;font-weight:700;color:#222;line-height:1.35;margin:0 0 14px}
.jdmt-hb-inline-stack{display:flex;flex-direction:column;gap:14px}
.jdmt-hb-inline-row{display:flex;flex-wrap:wrap;align-items:center;gap:12px 24px}
.jdmt-hb-inline-row-checks{gap:12px 28px}
.jdmt-hb-inline-row-fonts{gap:16px 32px}
.jdmt-hb-inline-item{display:inline-flex;align-items:center;gap:10px 12px;flex-wrap:nowrap;min-width:0}
.jdmt-hb-inline-item-grow{display:flex;flex-wrap:wrap}
.jdmt-hb-inline-item-select select{width:102px}
.jdmt-hb-inline-item-size{gap:14px}
.jdmt-hb-inline-label{font-size:13px;font-weight:600;color:#333;line-height:1.4;white-space:nowrap;flex:none}
.jdmt-hb-inline-item .external-picker{flex:none}
 .jdmt-hb-auth-row{display:flex;align-items:flex-end;justify-content:flex-start;gap:16px 24px;flex-wrap:wrap}
.jdmt-hb-auth-title{font-size:13px;font-weight:600;color:#333;line-height:1.4;white-space:nowrap;padding-bottom:6px}
.jdmt-hb-auth-controls{display:flex;flex-wrap:wrap;align-items:flex-end;gap:16px 18px}
.jdmt-hb-auth-control{display:flex;flex-direction:column;gap:8px;align-items:center;min-width:64px}
.jdmt-hb-auth-label{font-size:13px;font-weight:600;color:#333;line-height:1.4;white-space:nowrap}
.jdmt-hb-font-item{display:inline-flex;align-items:center;gap:10px 12px;flex-wrap:nowrap;min-width:0}
.jdmt-hb-row{display:grid;gap:14px 18px;align-items:end}
.jdmt-hb-row + .jdmt-hb-row{margin-top:14px}
.jdmt-hb-row-basic{grid-template-columns:minmax(180px,200px)}
.jdmt-hb-row-color{grid-template-columns:repeat(2,minmax(0,1fr))}
.jdmt-hb-row-logo{grid-template-columns:minmax(0,1fr) minmax(180px,200px)}
.jdmt-hb-row-checks{display:flex;flex-wrap:wrap;gap:12px 26px;align-items:center}
.jdmt-hb-row-login-colors{grid-template-columns:repeat(4,minmax(0,1fr))}
.jdmt-hb-row-menu-colors{grid-template-columns:repeat(3,minmax(0,1fr))}
.jdmt-hb-row-menu-fonts{grid-template-columns:repeat(2,minmax(0,1fr))}
.jdmt-hb-row-services{grid-template-columns:minmax(0,1fr)}
.jdmt-hb-note{font-size:13px;line-height:1.6;color:#8a8a8a;margin-top:8px}
.jdmt-hb-logo-inline{gap:12px 10px;min-height:40px}
.jdmt-hb-logo-inline .jdmt-check{min-height:40px}
.jdmt-hb-row-checks .jdmt-check{min-height:32px}
.jdmt-drawer-wrap-hb .jdmt-drawer-top{min-height:56px;padding:18px 16px 14px}
.jdmt-drawer-wrap-hb .jdmt-drawer-close{top:8px;right:12px;width:40px;height:40px;font-size:0;font-weight:400}
.jdmt-drawer-wrap-hb .jdmt-drawer-close:before,.jdmt-drawer-wrap-hb .jdmt-drawer-close:after{width:28px;height:1.5px}
.jdmt-drawer-wrap-hb .jdmt-drawer-logo{margin-bottom:14px}
.jdmt-drawer-wrap-hb .jdmt-drawer-logo img{max-width:108px;max-height:36px}
.jdmt-drawer-wrap-hb .jdmt-drawer-member{gap:6px;margin-bottom:12px;font-size:13px}
.jdmt-drawer-wrap-hb .jdmt-auth{height:32px;min-width:106px;border-radius:4px;font-size:13px}
.jdmt-drawer-wrap-hb .jdmt-drawer-main{min-height:58px;padding:0 16px}
.jdmt-drawer-wrap-hb .jdmt-drawer-sub{padding:16px 16px 16px 30px;gap:18px}
.jdmt-drawer-wrap-hb .jdmt-drawer-bottom{min-height:132px;padding:16px 14px;align-content:flex-start;flex-wrap:wrap;row-gap:14px}
.jdmt-hb-service-list{display:grid;grid-template-columns:repeat(4,minmax(84px,1fr));gap:26px 18px}
.jdmt-hb-service-item{gap:10px}
.jdmt-hb-service-name{font-size:13px}
.jdmt-services{display:grid;grid-template-columns:repeat(8,minmax(92px,1fr));gap:12px;margin-top:18px}
.jdmt-service-card{border:1px solid #e5e5e5;border-radius:14px;padding:12px 8px;display:flex;flex-direction:column;align-items:center;gap:8px;background:#fff}
.jdmt-service-card.on{border-color:#ff6b45;box-shadow:inset 0 0 0 1px #ff6b45}
.jdmt-service-circle{width:48px;height:48px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:18px;font-weight:700}
.jdmt-drawer-wrap-hb .jdmt-service-circle,.jdmt-service-circle.jdmt-hb-service-icon{width:30px;height:30px;font-size:16px}
.jdmt-service-circle.svg{background:transparent!important;border:0!important}
.jdmt-service-svg{width:100%;height:100%;display:block;object-fit:contain}
.jdmt-service-label{font-size:13px;font-weight:700;color:#333;text-align:center;word-break:keep-all}
.jdmt-service-inputs{margin-top:18px;display:grid;gap:12px}
.jdmt-service-input{display:grid;grid-template-columns:160px minmax(0,1fr);gap:16px;align-items:center}
.jdmt-service-input-label{font-size:13px;font-weight:700;color:#333}
.jdmt-foot{margin-top:auto;display:flex;justify-content:flex-end;gap:10px;padding:14px 20px 18px;border-top:1px solid #ececec}
@media (max-width:1320px){.jdmt-grid4{grid-template-columns:repeat(3,minmax(180px,1fr))}.jdmt-services{grid-template-columns:repeat(4,minmax(120px,1fr))}.jdmt-logo-controls-text{grid-template-columns:200px 132px 88px 48px}}
@media (max-width:980px){.jdmt-body{padding:18px 16px 28px}.jdmt-grid4,.jdmt-status,.jdmt-grid3,.jdmt-service-input{grid-template-columns:repeat(2,minmax(0,1fr))}.jdmt-menu-shape-row{flex-wrap:wrap}.jdmt-button-settings-grid{grid-template-columns:1fr}.jdmt-button-icon-field{justify-self:start}.jdmt-button-action-field .jdmt-inline,.jdmt-button-icon-field .jdmt-inline{flex-wrap:wrap}.jdmt-top{grid-template-columns:1fr 1fr 54px 54px}.jdmt-services{grid-template-columns:repeat(3,minmax(100px,1fr))}.jdmt-span2{grid-column:auto}.jdmt-span4{grid-column:auto}.jdmt-logo-layout{flex-direction:column;align-items:stretch}.jdmt-logo-type-field{width:auto}.jdmt-logo-type-group{flex-wrap:wrap}.jdmt-logo-controls-image,.jdmt-logo-controls-text{grid-template-columns:repeat(2,minmax(0,1fr))}.jdmt-hb-layout{grid-template-columns:1fr;gap:28px}.jdmt-hb-aside{display:flex;justify-content:center;padding-top:0}.jdmt-hb-inline-row,.jdmt-hb-auth-row,.jdmt-hb-auth-controls{gap:12px 16px}.jdmt-hb-service-list{grid-template-columns:repeat(4,minmax(84px,1fr))}}
@media (max-width:720px){.jdmt-tab{min-width:120px;height:48px;font-size:14px}.jdmt-grid4,.jdmt-status,.jdmt-grid3,.jdmt-service-input{grid-template-columns:1fr}.jdmt-services{grid-template-columns:repeat(2,minmax(120px,1fr))}.jdmt-top{grid-template-columns:1fr 1fr;height:auto!important;padding:12px}.jdmt-slot{min-height:52px}.jdmt-logo-controls-image,.jdmt-logo-controls-text{grid-template-columns:1fr}.jdmt-hb-row-basic,.jdmt-hb-row-color,.jdmt-hb-row-logo,.jdmt-hb-row-login-colors,.jdmt-hb-row-menu-colors,.jdmt-hb-row-menu-fonts{grid-template-columns:1fr}.jdmt-hb-field-sm{max-width:none}.jdmt-hb-service-list{grid-template-columns:repeat(2,minmax(84px,1fr))}.jdmt-drawer-wrap-hb{justify-content:center}.jdmt-drawer-wrap-hb .jdmt-drawer{width:100%;max-width:286px;min-height:620px}}
@media (min-width:721px){.jdmt .jdmt-menu-settings-grid{grid-template-columns:258px 92px 124px;gap:14px 34px;align-items:start;justify-content:start}.jdmt .jdmt-menu-settings-grid .jdmt-span2{grid-column:auto}.jdmt .jdmt-menu-settings-grid .jdmt-menu-shape-row{flex-wrap:nowrap}}
</style>
<script type="text/x-template" id="header-mobile-top-template">
<div v-if="load && row.idx" class="jdmth" :class="{'jdmth-design':isDesign}">
    <div v-if="row.top_fixed && !isDesign" class="jdmth-spacer" :style="{height:fixedHeight+'px'}"></div>

    <div class="jdmth-top-wrap" :class="{fixed:row.top_fixed && !isDesign, sticky:row.top_fixed && isDesign}">
        <div class="jdmth-top" :style="topStyle">
            <div v-for="slot in slotKeys" :key="slot" class="jdmth-slot" :class="slot">
                <template v-if="row[slot]==='logo'">
                    <button type="button" class="jdmth-touch" @click="goHome">
                        <img v-if="row.logo_type==='image' && logoSrc" :src="logoSrc" class="jdmth-logo-img" :style="logoImageStyle">
                        <span v-else class="jdmth-logo-text" :style="logoTextStyle">{{row.logo_text || 'LOGO'}}</span>
                    </button>
                </template>

                <template v-else-if="row[slot]==='menu'">
                    <button type="button" class="jdmth-touch" @click="openDrawer" v-html="menuIcon(row.menu_icon_style,row.menu_icon_size,row.menu_icon_color)"></button>
                </template>

                <template v-else-if="row[slot]==='button'">
                    <button type="button" class="jdmth-touch" @click="clickTopButton">
                        <img v-if="row.button_icon_mode==='image' && buttonSrc" :src="buttonSrc" class="jdmth-btn-img" :style="buttonImageStyle">
                        <span v-else-if="parsedButtonIcon && parsedButtonIcon.library === 'feather'" v-html="renderFeatherIcon(parsedButtonIcon.name,row.button_icon_size,row.button_icon_color)"></span>
                        <i v-else-if="parsedButtonIcon" :class="buttonIconClass" :style="buttonIconStyle"></i>
                        <span v-else class="jdmth-fallback">B</span>
                    </button>
                </template>
            </div>
        </div>
    </div>

    <div v-if="showSubmenu" class="jdmth-submenu" :class="row.submenu_type">
        <button v-for="(item,index) in submenuItems" :key="item.code || index" type="button" class="jdmth-submenu-item" :style="submenuStyle(isActiveMenu(item))" @click="clickMenu(item)">
            {{item.name}}
        </button>
    </div>

    <div v-if="drawerOpen" class="jdmth-mask" @click="closeDrawer"></div>
    <div class="jdmth-drawer" :class="[row.hb_position,{open:drawerOpen}]">
        <div class="jdmth-drawer-top" :style="{backgroundColor:row.hb_top_bg_color}">
            <button type="button" class="jdmth-drawer-close" :style="{color:row.hb_close_color}" aria-label="닫기" @click="closeDrawer"></button>

            <div v-if="row.hb_logo_use && drawerLogoSrc" class="jdmth-drawer-logo">
                <img :src="drawerLogoSrc" :style="{height:row.hb_logo_size+'px'}">
            </div>

            <div v-if="mobileLoginEnabled && (row.hb_show_nickname || row.hb_show_email)" class="jdmth-drawer-member" :style="{color:row.hb_info_font_color}">
                <div v-if="row.hb_show_nickname && memberName">{{memberName}}</div>
                <div v-if="row.hb_show_email && memberEmail">{{memberEmail}}</div>
            </div>

            <div v-if="mobileLoginEnabled" class="jdmth-auth-row">
                <button type="button" class="jdmth-auth-btn" :style="authBtnStyle" @click="clickAuth('primary')">{{authPrimaryLabel}}</button>
                <button type="button" class="jdmth-auth-btn" :style="authBtnStyle" @click="clickAuth('secondary')">{{authSecondaryLabel}}</button>
            </div>
        </div>

        <div class="jdmth-drawer-menu" :style="{backgroundColor:row.hb_menu_bg_color}">
            <template v-for="item in drawerMenus">
                <button type="button" :key="item.code" class="jdmth-main-item" :style="{borderColor:row.hb_menu_divider_color}" @click="clickDrawerMenu(item)">
                    <span :style="{color:row.hb_menu_font_color,fontSize:row.hb_menu_font_size+'px'}">{{item.name}}</span>
                    <span :style="{color:row.hb_menu_arrow_color}" v-html="drawerArrow(item)"></span>
                </button>
                <div v-if="hasChildren(item) && openMenus[item.code]" :key="item.code + '_sub'" class="jdmth-sub-items" :style="{borderColor:row.hb_menu_divider_color}">
                    <template v-for="sub in childMenus(item)">
                        <button type="button" :key="sub.code" class="jdmth-sub-item" :class="{'has-children':hasChildren(sub)}" :style="{color:row.hb_submenu_font_color,fontSize:row.hb_submenu_font_size+'px'}" @click="clickDrawerMenu(sub)">
                            <span>{{sub.name}}</span>
                            <span v-if="hasChildren(sub)" :style="{color:row.hb_menu_arrow_color}" v-html="drawerSubArrow(sub)"></span>
                        </button>
                        <div v-if="hasChildren(sub) && openMenus[sub.code]" :key="sub.code + '_third'" class="jdmth-third-items">
                            <button v-for="third in childMenus(sub)" :key="third.code" type="button" class="jdmth-third-item" :style="{color:row.hb_submenu_font_color,fontSize:row.hb_submenu_font_size+'px'}" @click="clickMenu(third)">
                                {{third.name}}
                            </button>
                        </div>
                    </template>
                </div>
            </template>
        </div>

        <div v-if="selectedServices.length" class="jdmth-drawer-bottom" :style="{backgroundColor:row.hb_bottom_bg_color}">
            <button v-for="service in selectedServices" :key="service.key" type="button" class="jdmth-service-btn" @click="clickService(service)">
                <span class="jdmth-service-circle" :class="{svg:serviceMeta(service.key).iconType==='svg'}" :style="serviceStyle(service.key)">
                    <template v-if="serviceMeta(service.key).iconType==='fa'">
                        <i :class="serviceMeta(service.key).icon"></i>
                    </template>
                    <template v-else-if="serviceMeta(service.key).iconType==='svg'">
                        <img :src="serviceMeta(service.key).src" class="jdmth-service-svg">
                    </template>
                    <template v-else>
                        {{serviceMeta(service.key).short}}
                    </template>
                </span>
            </button>
        </div>
    </div>
</div>
</script>

<script>
const JDMTH_SERVICES = [
    {key:'kakao_talk',label:'카카오톡',short:'T',iconType:'svg',src:'/file/icon/mobile_top/kakao-talk%201.svg',placeholder:'카카오톡 채널 또는 오픈채팅 주소 입력'},
    {key:'instagram',label:'인스타그램',short:'I',iconType:'svg',src:'/file/icon/mobile_top/instagram%201.svg',placeholder:'인스타그램 프로필 주소 입력'},
    {key:'youtube',label:'유튜브',short:'Y',iconType:'svg',src:'/file/icon/mobile_top/youtube%20(1)%201.svg',placeholder:'유튜브 채널 주소 입력'},
    {key:'facebook',label:'페이스북',short:'f',iconType:'svg',src:'/file/icon/mobile_top/facebook%201.svg',placeholder:'페이스북 프로필 주소 입력'},
    {key:'naver_blog',label:'네이버블로그',short:'bl',iconType:'svg',src:'/file/icon/mobile_top/naver_blog.svg',placeholder:'네이버 블로그 주소 입력'},
    {key:'naver_cafe',label:'네이버카페',short:'N',iconType:'svg',src:'/file/icon/mobile_top/naver_cafe.svg',placeholder:'네이버 카페 주소 입력'},
    {key:'daum_cafe',label:'다음카페',short:'c',iconType:'svg',src:'/file/icon/mobile_top/daum_cafe.svg',placeholder:'다음 카페 주소 입력'},
    {key:'customer_center',label:'고객센터',short:'?',iconType:'text',bg:'#ffffff',color:'#8d8d8d',border:'#d9d9d9',placeholder:'- 를 제외한 전화번호 입력'}
];

JayDream_components.push({name:"header-mobile-top",object:{
    template:"#header-mobile-top-template",
    props:{
        site_code:{type:String,default:""},
        is_design:{type:String,default:"N"},
    },
    data(){
        return {
            load:false,
            component_name:"header-mobile-top",
            component_idx:"",
            injectUrls:[
                'https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css',
                'https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/style.css',
                'https://cdnjs.cloudflare.com/ajax/libs/simple-line-icons/2.5.5/css/simple-line-icons.min.css',
                'https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js',
            ],
            menus:[],
            row:{},
            drawerOpen:false,
            openMenus:{},
            member:null,
            mobileLoginEnabled:false,
            serviceCatalog:JDMTH_SERVICES,
        };
    },
    async created(){
        this.component_idx = this.lib.generateUniqueId();
        this.api.component_name = this.component_name;
        this.row = this.defaultRow();
    },
    async mounted(){
        await this.loadMenus();
        await this.loadHeaderSetting();
        await this.loadMember();
        await this.loadRow();
        this.initOpenMenus();
        this.load = true;
    },
    beforeDestroy(){
        this.setDrawerBodyClass(false);
    },
    computed:{
        slotKeys(){return ['slot_left','slot_center','slot_right_1','slot_right_2'];},
        isDesign(){return this.is_design === 'Y' || this.is_design === true;},
        logoSrc(){return this.row.logo_image_src || '';},
        drawerLogoSrc(){return this.row.hb_logo_image_src || this.row.logo_image_src || '';},
        buttonSrc(){return this.row.button_icon_src || '';},
        parsedButtonIcon(){if(!this.row.button_icon_src || this.row.button_icon_mode==='image') return null; const parts=String(this.row.button_icon_src).split(':'); if(parts.length < 2) return null; return {library:parts[0],name:parts[1]};},
        buttonIconClass(){if(!this.parsedButtonIcon) return ''; const {library,name}=this.parsedButtonIcon; if(library === 'bootstrap') return `bi bi-${name}`; if(library === 'phosphor') return `ph ph-${name}`; if(library === 'simpleline') return `icon-${name}`; return '';},
        buttonIconStyle(){return {color:this.row.button_icon_color,fontSize:`${this.row.button_icon_size}px`,display:'inline-flex',alignItems:'center',justifyContent:'center',lineHeight:1};},
        buttonImageStyle(){const size = Math.max(1, Number(this.row.button_icon_size || 30)); return `width:${size}px !important;height:${size}px !important;max-width:none !important;max-height:none !important;object-fit:contain !important;display:block !important;`;},
        logoImageStyle(){return {height:`${Number(this.row.logo_image_size || 30)}px`,width:'auto',maxWidth:'none',maxHeight:'none',objectFit:'contain',display:'block',flex:'none'};},
        logoTextStyle(){return {fontFamily:`${this.row.logo_font}, sans-serif`,color:this.row.logo_font_color,fontSize:`${this.row.logo_font_size}px`,fontWeight:700};},
        topStyle(){return {height:`${this.resolvedTopHeight}px`,backgroundColor:this.row.top_bg_color,boxShadow:this.row.top_shadow_use?`0 4px 8px rgba(0,0,0,${(this.row.top_shadow_opacity||10)/100})`:'none'};},
        showSubmenu(){return this.row.submenu_type === 'slide' || this.row.submenu_type === 'table';},
        submenuItems(){return this.menus;},
        drawerMenus(){return this.menus;},
        fixedHeight(){return this.resolvedTopHeight;},
        resolvedTopHeight(){const topHeight = Number(this.row.top_height || 50); const logoSize = Number(this.row.logo_image_size || 30); const hasImageLogo = this.row.logo_type === 'image' && this.slotKeys.some(slot => this.row[slot] === 'logo'); return Math.max(topHeight, hasImageLogo ? logoSize : 0);},
        memberName(){return this.member?.name || this.member?.nick || this.member?.nick_name || '';},
        memberEmail(){return this.member?.email || '';},
        authPrimaryLabel(){return this.member ? '로그아웃' : '로그인';},
        authSecondaryLabel(){return this.member ? '마이페이지' : '회원가입';},
        authBtnStyle(){return {backgroundColor:this.row.hb_auth_bg_color,color:this.row.hb_auth_font_color,borderColor:this.row.hb_auth_border_color};},
        selectedServices(){
            const rows = [];
            [1,2,3].forEach(index => {
                const key = this.normalizeServiceKey(this.row[`hb_bottom_button_${index}_type`]);
                const value = this.row[`hb_bottom_button_${index}_value`] || '';
                if(key) rows.push({key,value});
            });
            return rows;
        },
    },
    methods:{
        defaultRow(){
            return {
                idx:'',
                top_height:50,top_bg_color:'#ffffff',top_fixed:true,top_shadow_use:false,top_shadow_opacity:10,
                slot_left:'logo',slot_center:'',slot_right_1:'',slot_right_2:'menu',
                logo_type:'image',logo_text:'',logo_font:'Pretendard',logo_font_size:14,logo_font_color:'#000000',logo_image_src:'',logo_image_size:30,
                menu_icon_style:'shape1',menu_icon_color:'#000000',menu_icon_size:30,
                button_type:'link',button_value:'',button_icon_mode:'',button_icon_src:'',button_icon_color:'#000000',button_icon_size:30,
                submenu_type:'slide',submenu_height:30,submenu_font_size:14,submenu_inactive_bg_color:'#ffffff',submenu_inactive_font_color:'#000000',submenu_inactive_border_color:'#a4a4a4',submenu_active_bg_color:'#373737',submenu_active_font_color:'#ffffff',submenu_active_border_color:'#a4a4a4',
                hb_position:'right',hb_top_bg_color:'#ffffff',hb_close_color:'#000000',hb_logo_use:false,hb_logo_image_src:'',hb_logo_size:20,hb_show_nickname:true,hb_show_email:true,hb_info_font_color:'#000000',hb_auth_bg_color:'#ffffff',hb_auth_font_color:'#000000',hb_auth_border_color:'#d3d3d3',hb_menu_bg_color:'#ffffff',hb_menu_divider_color:'#d3d3d3',hb_menu_arrow_color:'#000000',hb_menu_font_size:16,hb_menu_font_color:'#000000',hb_submenu_font_size:14,hb_submenu_font_color:'#454545',hb_bottom_bg_color:'#ffffff',
                hb_bottom_button_1_type:'',hb_bottom_button_1_value:'',hb_bottom_button_2_type:'',hb_bottom_button_2_value:'',hb_bottom_button_3_type:'',hb_bottom_button_3_value:'',
            };
        },
        toBool(value, def){
            if(value === undefined || value === null || value === '') return def;
            return value === true || value === 'Y' || value === 'true' || value === '1' || value === 1;
        },
        async loadMenus(){
            let table = await this.api.table("menu");
            table.where("site_code",this.site_code);
            table.where("is_use_header","Y");
            table.orderBy("order","ASC");
            this.menus = this.buildMenuTree(await table.get());
        },
        buildMenuTree(rows){
            const map = {};
            const roots = [];
            const sortByOrder = (a, b) => (parseInt(a.order || 0, 10) || 0) - (parseInt(b.order || 0, 10) || 0);

            (rows || []).forEach(row => {
                row.second = [];
                row.$second = {data: row.second};
                map[row.code] = row;
            });

            (rows || []).forEach(row => {
                const parentCode = row.parent_code === null || row.parent_code === undefined ? '' : String(row.parent_code);
                if(parentCode && parentCode !== 'null' && map[parentCode]){
                    map[parentCode].second.push(row);
                    map[parentCode].$second = {data: map[parentCode].second};
                }else{
                    roots.push(row);
                }
            });

            Object.keys(map).forEach(code => {
                map[code].second.sort(sortByOrder);
            });
            return roots.sort(sortByOrder);
        },
        async loadMember(){
            const sessions = await this.session.all();
            if(!sessions[`site_mem_${this.site_code}`]) return;
            let table = this.api.table("site_member");
            this.member = (await table.where("idx",sessions[`site_mem_${this.site_code}`]).get())[0] || null;
        },
        parseSettingData(value){
            if(!value) return {};
            if(typeof value === 'object') return value;
            try{
                return JSON.parse(value) || {};
            }catch(e){
                return {};
            }
        },
        async loadHeaderSetting(){
            const table = await this.api.table("header");
            table.where("site_code",this.site_code);
            const header = (await table.get())[0] || {};
            const settingData = this.parseSettingData(header.setting_data || header.settingData);
            this.mobileLoginEnabled = settingData.is_use_mobile_login === 'Y';
        },
        async loadRow(){
            let table = await this.api.table("jd_mobile_top");
            table.where("site_code",this.site_code);
            const rows = await table.get();
            if(!rows.length) return;
            this.row = this.hydrate(rows[0]);
        },
        hydrate(raw){
            const row = Object.assign(this.defaultRow(), raw || {});
            ['top_height','top_shadow_opacity','logo_font_size','logo_image_size','menu_icon_size','button_icon_size','submenu_height','submenu_font_size','hb_logo_size','hb_menu_font_size','hb_submenu_font_size'].forEach(key => {
                row[key] = parseInt(row[key] || this.defaultRow()[key], 10);
            });
            row.top_fixed = this.toBool(row.top_fixed, true);
            row.top_shadow_use = this.toBool(row.top_shadow_use, false);
            row.hb_logo_use = this.toBool(row.hb_logo_use, false);
            row.hb_show_nickname = this.toBool(row.hb_show_nickname, true);
            row.hb_show_email = this.toBool(row.hb_show_email, true);
            return row;
        },
        initOpenMenus(){
            const openMenus = {};
            this.menus.forEach(item => {
                if(this.hasChildren(item) && this.isActiveParent(item)) openMenus[item.code] = true;
            });
            this.openMenus = openMenus;
        },
        currentPath(){
            return decodeURIComponent((window.location.pathname || '/').replace(/\/+$/,'') || '/');
        },
        normalizeUrl(url){
            const cleaned = String(url || '').trim();
            if(!cleaned) return '/';
            if(cleaned.startsWith('http://') || cleaned.startsWith('https://') || cleaned.startsWith('tel:')) return cleaned;
            return '/' + cleaned.replace(/^\/+/,'');
        },
        isActiveMenu(item){
            const current = this.currentPath();
            const target = this.normalizeUrl(item.url || item.link || '/');
            return current === target || current.indexOf(target + '/') === 0;
        },
        isActiveParent(item){
            if(this.isActiveMenu(item)) return true;
            return this.childMenus(item).some(sub => this.isActiveParent(sub));
        },
        submenuStyle(active){
            return {
                height:`${this.row.submenu_height}px`,
                lineHeight:`${this.row.submenu_height}px`,
                fontSize:`${this.row.submenu_font_size}px`,
                backgroundColor:active ? this.row.submenu_active_bg_color : this.row.submenu_inactive_bg_color,
                color:active ? this.row.submenu_active_font_color : this.row.submenu_inactive_font_color,
                borderColor:active ? this.row.submenu_active_border_color : this.row.submenu_inactive_border_color,
            };
        },
        menuIcon(shape,size,color){
            const safeSize = parseInt(size || 30, 10);
            const iconHeight = Number.isFinite(safeSize) ? Math.max(1, safeSize) : 30;
            const styleNo = Math.min(4, Math.max(1, parseInt(String(shape || 'shape1').replace(/\D/g, ''), 10) || 1));
            const iconWidth = styleNo === 4 ? iconHeight : Math.round(iconHeight * 17 / 16);
            const safeColor = String(color || '#000000').replace(/[;"'<>]/g, '');
            const src = `/file/icon/mobile_top/hambergur_icon_style_${styleNo}.svg`;

            return `<span class="jdmth-menu-icon" style="display:inline-block;width:${iconWidth}px;height:${iconHeight}px;background-color:${safeColor};-webkit-mask:url('${src}') center / contain no-repeat;mask:url('${src}') center / contain no-repeat;"></span>`;
        },
        normalizeServiceKey(key){
            if(!key) return '';
            return key === 'center' ? 'customer_center' : key;
        },
        renderFeatherIcon(name,size,color){
            if(typeof feather !== 'undefined' && feather.icons[name]) return feather.icons[name].toSvg({width:size,height:size,color});
            return '';
        },
        iconHtml(src,size,color){
            if(!src) return '';
            const parts = String(src).split(':');
            if(parts.length < 2) return '';
            const library = parts[0];
            const name = parts[1];
            if(library === 'feather' && typeof feather !== 'undefined' && feather.icons[name]) return feather.icons[name].toSvg({width:size,height:size,color});
            if(library === 'bootstrap') return `<i class="bi bi-${name}" style="color:${color};font-size:${size}px;"></i>`;
            if(library === 'phosphor') return `<i class="ph ph-${name}" style="color:${color};font-size:${size}px;"></i>`;
            if(library === 'simpleline') return `<i class="icon-${name}" style="color:${color};font-size:${size}px;"></i>`;
            return '';
        },
        childMenus(item){
            return item?.$second?.data || item?.second || [];
        },
        hasChildren(item){
            return this.childMenus(item).length > 0;
        },
        async clickMenu(item){
            const check = this.checkMenu(item);
            if(check === false) return false;
            if(check === 'mde1'){
                if(this.isDesign) return false;
                await this.lib.alert('로그인 후 이용가능합니다.');
                this.lib.href('/main/login');
                return false;
            }

            if(item.is_move_sub1 === 'Y' && this.childMenus(item)[0]) {
                await this.clickMenu(this.childMenus(item)[0]);
                return false;
            }

            if(this.isDesign) return false;
            this.closeDrawer();

            if(item.link_type === 'tel'){
                window.location.href = `tel:${item.link || item.url || ''}`;
                return;
            }

            const url = item.url || item.link || '/';
            if(item.is_new_window === 'Y'){
                this.lib.href(url, '_blank');
                return;
            }
            this.lib.href(url);
        },
        checkMenu(item){
            if(item.menu_auth === 'all' || !item.menu_auth) return true;
            if(item.menu_auth === 'member'){
                if(this.member) return true;
                if(item.mde2 === 'Y') return false;
                if(item.mde1 === 'Y') return 'mde1';
                return true;
            }
            if(item.menu_auth === 'admin') return this.member?.grade === 'A';
            if(item.menu_auth === 'group') return this.member?.group_code === item.menu_group;
            return true;
        },
        setDrawerBodyClass(open){
            if(!this.isDesign || typeof document === 'undefined') return;
            document.body.classList.toggle('jd-mobile-top-drawer-open', !!open);
        },
        openDrawer(){
            this.drawerOpen = true;
            this.setDrawerBodyClass(true);
        },
        closeDrawer(){
            this.drawerOpen = false;
            this.setDrawerBodyClass(false);
        },
        drawerArrow(item){
            if(!this.hasChildren(item)) return '';
            const direction = this.openMenus[item.code] ? 'up' : 'down';
            return `<i class="fal fa-chevron-${direction}" style="color:${this.row.hb_menu_arrow_color};font-size:20px;"></i>`;
        },
        drawerSubArrow(item){
            if(!this.hasChildren(item)) return '';
            const direction = this.openMenus[item.code] ? 'up' : 'down';
            return `<i class="fal fa-chevron-${direction}" style="color:${this.row.hb_menu_arrow_color};font-size:14px;"></i>`;
        },
        async clickDrawerMenu(item){
            if(this.hasChildren(item)){
                this.openMenus = Object.assign({}, this.openMenus, {
                    [item.code]: !this.openMenus[item.code],
                });
                return;
            }
            await this.clickMenu(item);
        },
        goHome(){
            if(this.isDesign) return;
            this.lib.href('/');
        },
        clickTopButton(){
            if(this.isDesign) return;
            if(this.row.button_type === 'link' && this.row.button_value){
                this.lib.href(this.row.button_value);
                return;
            }
            if(this.row.button_type === 'page' && this.row.button_value){
                this.lib.href(this.row.button_value);
                return;
            }
            if(this.row.button_type === 'tel' && this.row.button_value){
                window.location.href = `tel:${this.row.button_value}`;
                return;
            }
            if(this.row.button_type === 'mypage'){
                this.lib.href('/main/mypage');
            }
        },
        clickAuth(type){
            if(this.isDesign) return;
            if(this.member){
                if(type === 'primary'){
                    if(window.MEMBER?.logout) { window.MEMBER.logout(); return; }
                    this.lib.href('/main/logout');
                    return;
                }
                this.lib.href('/main/mypage');
                return;
            }
            this.lib.href(type === 'primary' ? '/main/login' : '/main/register');
        },
        serviceMeta(key){
            const normalizedKey = this.normalizeServiceKey(key);
            const service = this.serviceCatalog.find(service => this.normalizeServiceKey(service.key) === normalizedKey) || this.serviceCatalog[0];
            if(normalizedKey === 'customer_center'){
                return Object.assign({}, service, {
                    key:'customer_center',
                    iconType:'svg',
                    src:'/file/icon/mobile_top/center.svg',
                });
            }
            return service;
        },
        serviceStyle(key){
            const service = this.serviceMeta(key);
            if(service.iconType === 'svg'){
                return {
                    background:'transparent',
                    color:'inherit',
                    border:'none',
                };
            }
            return {
                background: service.bg,
                color: service.color,
                border: service.border ? `1px solid ${service.border}` : 'none',
            };
        },
        clickService(service){
            if(this.isDesign || !service.value) return;
            if(this.normalizeServiceKey(service.key) === 'customer_center'){
                window.location.href = `tel:${service.value}`;
                return;
            }
            this.lib.href(service.value, '_blank');
        },
    },
}});
</script>

<style>
.jdmth{display:none}
.jdmth-spacer{display:block}
.jdmth.jdmth-design{width:100%;max-width:100%;overflow:hidden}
.jdmth-top-wrap{position:relative;z-index:10010}
.jdmth-top-wrap.fixed{position:fixed;top:0;left:0;width:100%;z-index:10020}
.jdmth-top-wrap.sticky{position:sticky;top:0}
.jdmth.jdmth-design .jdmth-top-wrap{z-index:80}
.jdmth-top{position:relative;display:grid;grid-template-columns:minmax(0,1fr) 52px 52px;gap:8px;align-items:center;padding:0 20px;border-bottom:1px solid rgba(0,0,0,.08);box-sizing:border-box;overflow:visible}
.jdmth-top .jdmth-slot.slot_left{grid-column:1;grid-row:1}
.jdmth-top .jdmth-slot.slot_center{position:absolute;top:0;bottom:0;left:50%;width:120px;transform:translateX(-50%);z-index:1}
.jdmth-top .jdmth-slot.slot_right_1{grid-column:2;grid-row:1}
.jdmth-top .jdmth-slot.slot_right_2{grid-column:3;grid-row:1}
.jdmth-slot{height:100%;min-height:100%;display:flex;align-items:center;justify-content:center;position:relative;pointer-events:none}
.jdmth.jdmth-design .jdmth-slot{min-width:0}
.jdmth-slot.slot_left{justify-content:flex-start}
.jdmth-touch{display:inline-flex;align-items:center;justify-content:center;min-width:52px;height:100%;border:0;background:transparent;padding:0 6px;margin:0;color:inherit;pointer-events:auto}
.jdmth-touch>span:not(.jdmth-logo-text):not(.jdmth-fallback){display:inline-flex;align-items:center;justify-content:center;line-height:0}
.jdmth-touch i,
.jdmth-touch svg,
.jdmth-touch .jdmth-menu-icon{display:block !important;line-height:1;vertical-align:middle}
.jdmth-logo-img{display:block;width:auto;max-width:none;max-height:none;object-fit:contain;flex:none}
.jdmth-logo-text{font-weight:700}
.jdmth-btn-img{display:block;width:auto;max-width:100%;object-fit:contain}
.jdmth-fallback{font-size:18px;font-weight:700;color:#b1b1b1}
.jdmth-submenu{display:flex;overflow:hidden;border-bottom:1px solid rgba(0,0,0,.08);background:#fff}
.jdmth-submenu.slide{overflow-x:auto;overflow-y:hidden;white-space:nowrap;-webkit-overflow-scrolling:touch}
.jdmth-submenu.table{display:flex;flex-wrap:wrap}
.jdmth-submenu-item{flex:1 1 0;min-width:0;padding:0 14px;border:0;border-right:1px solid;border-bottom:1px solid;background:transparent;font-weight:700;text-align:center;white-space:nowrap;box-sizing:border-box;overflow:hidden;text-overflow:ellipsis}
.jdmth-submenu.slide .jdmth-submenu-item{flex:1 1 0;min-width:max-content}
.jdmth-submenu.table .jdmth-submenu-item{flex:1 0 25%;min-width:0}
.jdmth-submenu.table .jdmth-submenu-item:nth-child(4n),
.jdmth-submenu.table .jdmth-submenu-item:last-child{border-right:0}
.jdmth-mask{position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:10030}
.jdmth-drawer{position:fixed;top:0;bottom:0;width:320px;max-width:86vw;background:#fff;z-index:10040;display:flex;flex-direction:column;transition:transform .25s ease}
body._mbv.jd-mobile-top-drawer-open #user-header{position:relative;z-index:1210}
body._mbv.jd-mobile-top-drawer-open #user-header .jdmth.jdmth-design{overflow:visible}
body._mbv .jdmth.jdmth-design .jdmth-mask{z-index:1211}
body._mbv .jdmth.jdmth-design .jdmth-drawer{z-index:1212}
.jdmth-drawer.right{right:0;transform:translateX(100%)}
.jdmth-drawer.left{left:0;transform:translateX(-100%)}
.jdmth-drawer.open{transform:translateX(0)}
.jdmth-drawer-top{min-height:62px;box-sizing:border-box;padding:18px 18px 16px;position:relative}
.jdmth-drawer-close{position:absolute;top:10px;right:14px;width:42px;height:42px;border:0;background:transparent;font-size:0;line-height:1;padding:0;color:inherit}
.jdmth-drawer-close:before,.jdmth-drawer-close:after{content:"";position:absolute;top:50%;left:50%;width:28px;height:1.5px;background:currentColor;border-radius:999px;transform-origin:center}
.jdmth-drawer-close:before{transform:translate(-50%,-50%) rotate(45deg)}
.jdmth-drawer-close:after{transform:translate(-50%,-50%) rotate(-45deg)}
.jdmth-drawer-logo{padding-right:44px;margin-bottom:14px}
.jdmth-drawer-logo img{display:block;max-width:140px;object-fit:contain}
.jdmth-drawer-member{display:flex;flex-direction:column;gap:6px;margin-bottom:14px;font-size:14px;font-weight:600}
.jdmth-auth-row{display:flex;gap:10px}
.jdmth-auth-btn{flex:1;min-width:0;height:30px;border:1px solid;border-radius:2px;font-size:14px;font-weight:500;background:#fff}
.jdmth-drawer-menu{flex:1;overflow:auto}
.jdmth-main-item{width:100%;min-height:62px;padding:0 18px;border:0;border-top:1px solid;background:transparent;display:flex;align-items:center;justify-content:space-between;text-align:left}
.jdmth-sub-items{border-top:1px solid;padding:18px 18px 18px 34px;display:flex;flex-direction:column;gap:18px}
.jdmth-sub-item{width:100%;border:0;background:transparent;padding:0;text-align:left}
.jdmth-sub-item.has-children{display:flex;align-items:center;justify-content:space-between;gap:12px}
.jdmth-third-items{padding:0 0 0 18px;display:flex;flex-direction:column;gap:14px}
.jdmth-third-item{width:100%;border:0;background:transparent;padding:0;text-align:left}
.jdmth-drawer-bottom{min-height:90px;padding:18px 16px;display:flex;align-items:center;justify-content:center;gap:20px}
.jdmth-service-btn{border:0;background:transparent;padding:0}
.jdmth-service-circle{width:30px;height:30px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:16px;font-weight:700}
.jdmth-service-circle.svg{background:transparent!important;border:0!important}
.jdmth-service-svg{width:100%;height:100%;display:block;object-fit:contain}
body._mbv .jdmth{display:block}
@media (max-width: 767px){
    .jdmth{display:block}
}
</style>
<script type="text/x-template" id="header-mobile-template">
    <div v-if="load">
        <template v-if="sub_setting_data?.submenu_mobile_type == 'slide'">
            <div class="header-mobile-slide" style="margin-left:-10px;margin-right:-20px;">
                <template v-for="item in menus">
                    <a v-if="checkMenu(item)" @click="clickMenu(item)" :style="styleMenu(item)">{{item.name}}</a>
                </template>
            </div>
        </template>


        <template v-else>
            <div class="header-mobile-table">
                <template v-for="item in menus">
                    <div v-if="checkMenu(item)"
                         @click="clickMenu(item)"
                         :style="styleMenu(item)"
                         class="menu-item">
                        {{item.name}}
                    </div>
                </template>
            </div>
        </template>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "header-mobile",object : {
            template: "#header-mobile-template",
            props: {
                primary : {type : String, default : ""},
                site_code : {type : String, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "header-mobile",
                    component_idx: "",

                    table : null,
                    row: {},
                    rows : [],

                    menus : [],

                    member : null,
                    sub_setting_data : null,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                // this.table = await this.api.table("exam");
                // await this.table.get(this.rows,{paging : 10})
                this.sessions = await this.session.all()

                if(this.sessions[`site_mem_${this.site_code}`]) {
                    let table = this.api.table("site_member");
                    this.member = (await table.where("idx",this.sessions[`site_mem_${this.site_code}`]).get())[0];
                }

                await this.getMenu();

                {
                    let table = this.api.table("header");
                    let header = (await table.where("site_code",this.site_code).get())[0];
                    if(header?.sub_setting_data) this.sub_setting_data = header.sub_setting_data
                }

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {
                styleMenu(item) {
                    let style = {};
                    if(!this.sub_setting_data) return style;

                    const s = this.sub_setting_data;

                    const isTableType = !s?.submenu_mobile_type || s?.submenu_mobile_type == "table";

                    if(isTableType) {
                        const lineCount = s?.text_mobile_line_count || 3; // 기본값 3
                        style.width = `${100 / lineCount}%`;
                    }else {
                        // 메뉴 간격
                        if(s.back_mobile_spacing) {
                            // style.marginLeft = `${s.back_mobile_spacing}px`;
                            style.marginRight = `${s.back_mobile_spacing}px`;
                        }
                    }

                    // 글자 색상
                    if(s.text_mobile_color) {
                        const opacity = s.text_mobile_color_opacity || 100;
                        style.color = `${s.text_mobile_color}${Math.round(opacity * 2.55).toString(16).padStart(2, '0')}`;
                    }

                    // 글자 크기
                    if(s.text_mobile_size) style.fontSize = `${s.text_mobile_size}px`;

                    // 글자 굵기
                    if(s.text_mobile_weight) style.fontWeight = s.text_mobile_weight;

                    // 자간
                    if(s.text_mobile_letter_spacing) style.letterSpacing = `${s.text_mobile_letter_spacing}px`;

                    // 배경 색상
                    if(s.back_mobile_color) style.backgroundColor = s.back_mobile_color;

                    // 메뉴 높이
                    if(s.back_mobile_height) {
                        style.height = `${s.back_mobile_height}px`;
                        style.lineHeight = `${s.back_mobile_height}px`; // 추가
                    }



                    // 활성 상태 (현재 페이지인 경우)
                    let current = decodeURIComponent(window.location.pathname.replace(/^\//, '').split('/')[0]);
                    let itemPath = item.url?.replace(/^\//, '').split('/')[0] || '';

                    let isActive = current === itemPath;

                    if(isActive) {
                        // 활성 배경 색상
                        if(s.h_back_mobile_color) {
                            const opacity = s.h_back_mobile_color_opacity || 100;
                            style.backgroundColor = `${s.h_back_mobile_color}${Math.round(opacity * 2.55).toString(16).padStart(2, '0')}`;
                        }

                        // 활성 글자 색상
                        if(s.h_text_mobile_color) {
                            const opacity = s.h_text_mobile_color_opacity || 100;
                            style.color = `${s.h_text_mobile_color}${Math.round(opacity * 2.55).toString(16).padStart(2, '0')}`;
                        }

                        // 활성 밑줄선
                        if(s?.submenu_mobile_type == "slide") {
                            if(s.underline_mobile_color) {
                                const opacity = s.underline_mobile_color_opacity || 100;
                                style.borderBottom = `2px solid ${s.underline_mobile_color}${Math.round(opacity * 2.55).toString(16).padStart(2, '0')}`;
                            }
                        }
                    }

                    return style;
                },
                async clickMenu(item) {
                    if(this.checkMenu(item) == "mde1") {
                        await this.lib.alert("로그인 후 이용가능합니다.");
                        this.lib.href("/main/login");
                        return false;
                    }

                    if(item.is_move_sub1 == "Y") {
                        if(item.$second?.data[0]) {
                            await this.clickMenu(item.$second.data[0])
                        }else {
                            if(item.is_new_window == "Y") {
                                this.lib.href(item.url,"_blank")
                            }else {
                                this.lib.href(item.url)
                            }
                        }

                        return false;
                    }

                    if(item.link_type == "link" || !item.link_type) {
                        if(item.is_new_window == "Y") {
                            this.lib.href(item.url,"_blank")
                        }else {
                            this.lib.href(item.url)
                        }

                    }

                    if(item.link_type == "tel") {
                        window.location.href = `tel:${item.link}`
                    }
                },
                checkMenu(item) {
                    if(item.menu_auth == "all" || !item.menu_auth) {
                        return true;
                    }

                    if(item.menu_auth == "member") {
                        if(this.member) {
                            return true;
                        }else {
                            if(item.mde2 == "Y") return false
                            if(item.mde1 =="Y") return "mde1"
                            return true;
                        }
                    }

                    if(item.menu_auth == "admin") {
                        if(this.member?.grade == "A") return true;

                        return false;
                    }

                    if(item.menu_auth == "group") {
                        if(this.member?.group_code == item.menu_group) return true;
                        return false;
                    }
                },
                async getMenu() {
                    let table = await this.api.table("menu");

                    table.where("site_code",this.site_code);
                    table.where("parent_code","null");
                    table.where("is_use_header","Y");
                    table.orderBy("order","ASC");

                    table.relations("menu","second").where("parent_code","$parent.code");
                    table.relations("second").orderBy("order","ASC");
                    await table.get(this.menus);
                }
            },
            computed: {

            },
            watch: {

            }
        }});
</script>

<style>
    /* 기본 숨김 */
    .header-mobile-slide {
        background-color: #fff;
        width: 100%;
        max-width: 100vw;
        overflow-x: auto;
        overflow-y: hidden;
        white-space: nowrap;
        border-top: 1px solid #e7e7e7;
        border-bottom: 1px solid #e7e7e7;
        display: none;
    }

    /* 실제 모바일(768px 이하)에서 표시 */
    @media all and (max-width:768px) {
        .header-mobile-slide {
            display: block;
        }
    }

    /* 모바일 편집모드(_mbv 클래스)일 때 표시 */
    body._mbv .header-mobile-slide {
        display: block;
    }

    .header-mobile-slide a {
        display: inline-block;
        padding: 0 15px;
        line-height: inherit;
        vertical-align: middle;
        white-space: nowrap;
    }

    .header-mobile-table {
        display: none;
    }

    @media all and (max-width:768px) {
        .header-mobile-table {
            display: flex;
            flex-wrap: wrap;
            gap: 0;
        }
    }

    /* 모바일 편집모드(_mbv 클래스)일 때 표시 */
    body._mbv .header-mobile-table {
        display: flex;
        flex-wrap: wrap;
        gap: 0;
    }

    .header-mobile-table .menu-item {
        text-align: center;
        cursor: pointer;
        /*padding: 15px 10px;*/
        box-sizing: border-box;
    }
</style><script type="text/x-template" id="design-widget-image-free-modal-template">
    <div v-if="load">
        <external-bs-modal v-model="modelValue" @update:modelValue="value => $emit('update:modelValue', value)">
            <template v-slot:header>
                <h5 class="modal-title" id="exampleModalLabel">무료 이미지</h5>
            </template>

            <!-- body -->
            <template v-slot:default>
                <div>
                    <div style="margin-bottom: 15px;">
                        <div style="position: relative;">
                            <input type="text"
                                   class="form-control free-modal-image-modal-search-input"
                                   placeholder="검색어를 입력해 주세요."
                                   v-model="keyword"
                                   @keyup.enter="getImages()"/>
                            <button type="button"
                                    class="free-modal-image-modal-search-btn"
                                    @click="getImages()">
                                <img src="/file/icon/돋보기.svg">
                            </button>
                        </div>
                    </div>
                    <div class="text-right text-muted">Powered by Freepik</div>
                    <div v-if="loading">이미지 불러오는중...</div>
                    <template v-else>
                        <div v-if="rows.length == 0">{{text_content}}</div>
                        <div v-else>
                            <div class="free-modal-image-grid-flex">
                                <template v-for="item in visibleRows">
                                    <div class="free-modal-image-item"
                                         @click="choiceImage(item)"
                                         :class="activeImage(item)">
                                        <img :src="getImageSrc(item)">
                                        <div class="free-modal-image-overlay">
                                            <div class="free-modal-image-info">
                                                <div class="free-modal-author-name">{{getAuthorName(item)}}</div>
                                                <div class="free-modal-image-description">
                                                    {{getDescription(item)}}
                                                </div>
                                                <div class="free-modal-image-meta" v-if="getSourceUrl(item)">
                                                    <span>Freepik</span>
                                                    <a :href="getSourceUrl(item)"
                                                       target="_blank"
                                                       rel="noopener noreferrer"
                                                       class="free-modal-image-link"
                                                       @click.stop>원본</a>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </template>
                            </div>

                            <nav aria-label="...">
                                <ul class="pagination pagination-sm">
                                    <li class="page-item" :class="{disabled : !canGoPrevApiPage}" @click="goPrevApiPage()">
                                        <span class="page-link">&lt;&lt;</span>
                                    </li>
                                    <template v-for="num in displayedPages">
                                        <li :key="'freepik-page-' + num" class="page-item" :class="{active : num === page}" aria-current="page" @click="page = num;">
                                            <span class="page-link">{{num}}</span>
                                        </li>
                                    </template>
                                    <li class="page-item" :class="{disabled : !canGoNextApiPage}" @click="goNextApiPage()">
                                        <span class="page-link">&gt;&gt;</span>
                                    </li>
                                </ul>
                            </nav>

                            <div class="text-center" style="margin-top: 20px;" v-show="false">
                                <button type="button" class="btn btn-default btn-load-more" @click="getImages()">
                                    <i class="glyphicon glyphicon-refresh"></i> 새로고침
                                </button>
                            </div>
                        </div>
                    </template>

                </div>
            </template>


            <template v-slot:footer>
                <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
                <button type="button" class="btn btn-orange save" @click="saveImage()">저장</button>
            </template>
        </external-bs-modal>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-image-free-modal",object : {
            template: "#design-widget-image-free-modal-template",
            props: {
                modelValue : {type: Object, default: {}},
                widget_type : {type: String, default: ""},
                user_idx : {type: String, default: ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-image-free-modal",
                    component_idx: "",
                    loading : false,

                    temp : {
                        $table : "",
                    },
                    row: {},
                    rows : [],
                    rowsByApiPage : {},

                    file_idx : "",
                    limit : 9,
                    page : 1,
                    apiPageSize : 99,
                    currentApiPage : 1,
                    lastApiPage : null,

                    image_url : "",
                    keyword : "",
                    searchedKeyword : "",
                    select_item : null,
                    text_content : "이미지가 없습니다.",
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
            },
            async mounted() {
                this.table = await this.api.table("jd_file");

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {
                async getImages(reset = true) {
                    this.loading = true;
                    if(reset) {
                        this.page = 1;
                        this.currentApiPage = 1;
                        this.select_item = null;
                        this.text_content = "이미지가 없습니다.";
                        this.rows = [];
                        this.rowsByApiPage = {};
                        this.lastApiPage = null;
                        this.searchedKeyword = this.keyword;
                    }

                    try {
                        await this.requestApiPage(1, reset ? this.keyword : this.searchedKeyword);
                        this.applyApiPage(1);
                    } catch (err) {
                        this.rows = [];
                        this.rowsByApiPage = {};
                        this.text_content = "현재 요청이많아 무료 이미지는 사용이 불가능합니다. 죄송합니다."
                    } finally {
                        this.loading = false;
                    }
                },
                getApiPage(page) {
                    return Math.ceil(page / this.localPageSize) || 1;
                },
                getLocalPage(page) {
                    return ((page - 1) % this.localPageSize) + 1;
                },
                async requestApiPage(apiPage, keyword = "") {
                    const targetKeyword = keyword !== "" ? keyword : this.searchedKeyword;
                    if(this.rowsByApiPage[apiPage]) {
                        return this.rowsByApiPage[apiPage];
                    }

                    let res = await this.lib.ajax("get_resources",{term : targetKeyword, page : apiPage},"/JayDream/provider/freepik/api.php");
                    let data = res?.data?.data?.data || [];

                    this.rowsByApiPage = Object.assign({}, this.rowsByApiPage, {
                        [apiPage] : data
                    });
                    this.rows = data;

                    if(data.length < this.apiPageSize) {
                        this.lastApiPage = apiPage;
                    }

                    return data;
                },
                applyApiPage(apiPage) {
                    this.currentApiPage = apiPage;
                    this.rows = this.rowsByApiPage[apiPage] || [];
                    this.page = ((apiPage - 1) * this.localPageSize) + 1;
                },
                async goNextApiPage() {
                    if(this.loading || !this.canGoNextApiPage) return;

                    const targetApiPage = this.currentApiPage + 1;

                    this.loading = true;
                    try {
                        const data = await this.requestApiPage(targetApiPage);
                        if(!data.length) {
                            this.lastApiPage = targetApiPage - 1;
                            return;
                        }
                        this.applyApiPage(targetApiPage);
                    } catch (err) {
                        this.text_content = "현재 요청이많아 무료 이미지는 사용이 불가능합니다. 죄송합니다.";
                    } finally {
                        this.loading = false;
                    }
                },
                goPrevApiPage() {
                    if(this.loading || !this.canGoPrevApiPage) return;
                    this.applyApiPage(this.currentApiPage - 1);
                },
                parseImageSize(sizeText) {
                    const defaultSize = {width : "", height : ""};
                    if(!sizeText || typeof sizeText !== "string") return defaultSize;

                    const size = sizeText.toLowerCase().split("x");
                    if(size.length !== 2) return defaultSize;

                    const width = parseInt(size[0].replace(/[^0-9]/g, ""));
                    const height = parseInt(size[1].replace(/[^0-9]/g, ""));

                    return {
                        width : isNaN(width) ? "" : width,
                        height : isNaN(height) ? "" : height,
                    };
                },
                normalizeImageUrl(url) {
                    if(!url) return "";
                    return url.replace(/^http:\/\//i, "https://");
                },
                getImageSrc(item) {
                    return this.normalizeImageUrl(item && item.image && item.image.source ? item.image.source.url : "");
                },
                getSourceUrl(item) {
                    return this.normalizeImageUrl(item && item.url ? item.url : "");
                },
                getAuthorName(item) {
                    return item && item.author && item.author.name ? item.author.name : "Freepik";
                },
                getDescription(item) {
                    if(item && item.title) return item.title;
                    if(item && item.filename) return item.filename;
                    return "제목 없음";
                },
                getImageName(item) {
                    return this.getDescription(item);
                },
                async saveImage() {
                    if(!this.select_item) return false;

                    const imageSrc = this.getImageSrc(this.select_item);
                    const imageSize = this.parseImageSize(this.select_item && this.select_item.image && this.select_item.image.source ? this.select_item.image.source.size : "");
                    const imageName = this.getImageName(this.select_item);

                    if(this.widget_type === "image") {
                        this.modelValue.row.file_src = imageSrc;
                        this.modelValue.row.file_width = imageSize.width;
                        this.modelValue.row.file_height = imageSize.height;

                        this.modelValue.row.width = imageSize.width;
                    }else if(this.widget_type === "image_hover") {
                        this.modelValue.row.option_mouse_over_image_src = imageSrc;
                        this.modelValue.row.option_mouse_over_image_name = imageName;
                    }else if(this.widget_type === "text_bg") {
                        this.modelValue.row.upfile = null;
                        this.modelValue.row.file_idx = "";
                        this.modelValue.row.$jd_file__src = imageSrc;
                        this.modelValue.row.$jd_file__name = imageName;
                        this.modelValue.row.free_image_src = imageSrc;
                        this.modelValue.row.free_image_name = imageName;
                        this.modelValue.row.free_image_width = imageSize.width;
                        this.modelValue.row.free_image_height = imageSize.height;
                    }

                    this.$closeModal(this.modelValue);

                },
                activeImage(item) {
                    if(this.widget_type === "image" || this.widget_type === "image_hover" || this.widget_type === "text_bg") {
                        if(this.getImageSrc(item) === this.getImageSrc(this.select_item)) return "active";
                    }

                    return false;
                },
                choiceImage(item) {
                    if(this.widget_type === "image" || this.widget_type === "image_hover" || this.widget_type === "text_bg") {
                        this.select_item = item;
                    }
                },
            },
            computed: {
                localPageSize() {
                    return this.apiPageSize / this.limit;
                },
                visibleRows() {
                    const localPage = this.getLocalPage(this.page);
                    const start = (localPage - 1) * this.limit;
                    const end = localPage * this.limit;
                    return this.rows.slice(start, end);
                },
                totalPage() {
                    if(!this.rows.length) return 0;
                    return this.rows.length % this.limit === 0 ? this.rows.length / this.limit : Math.floor(this.rows.length / this.limit) + 1;
                },
                displayedPages() {
                    const start = this.page - this.getLocalPage(this.page) + 1;
                    return Array.from({length : this.totalPage}, (_, index) => start + index);
                },
                canGoPrevApiPage() {
                    return this.currentApiPage > 1;
                },
                canGoNextApiPage() {
                    if(this.lastApiPage !== null) {
                        return this.currentApiPage < this.lastApiPage;
                    }

                    return this.rows.length === this.apiPageSize;
                },
            },
            watch: {
                async "modelValue.status"(value, old_value) {
                    const $modal = $("#widget_setting_jd");

                    if(!value) {
                        this.file_idx = "";
                        this.rows = [];
                        this.rowsByApiPage = {};
                        this.page = 1;
                        this.currentApiPage = 1;
                        this.lastApiPage = null;
                        this.searchedKeyword = "";
                        this.select_item = null;
                        this.text_content = "이미지가 없습니다.";

                        $modal.data('bs.modal').options.backdrop = true;
                        $modal.data('bs.modal').options.keyboard = true;
                        return false;
                    }


                    $modal.data('bs.modal').options.backdrop = 'static';
                    $modal.data('bs.modal').options.keyboard = false;

                    await this.getImages();
                }
            }
        }});
</script>

<style>
    .free-modal-image-grid-flex {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        padding: 10px 0;
    }

    .free-modal-image-item {
        cursor: pointer;
        border-radius: 4px;
        overflow: hidden;
        border: 1px solid #ddd;
        transition: all 0.2s;
        aspect-ratio: 1 / 1;
        position: relative;
    }

    .free-modal-image-item img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
        transition: all 0.2s;
    }

    .free-modal-image-item:hover {
        border-color: #337ab7;
        box-shadow: 0 2px 8px rgba(0,0,0,0.15);
        transform: scale(1.05);
    }

    .free-modal-image-item.active {
        outline: 3px solid red;
        outline-offset: -3px;
    }

    /* 오버레이 스타일 */
    .free-modal-image-overlay {
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
        background: linear-gradient(to top, rgba(0,0,0,0.95) 0%, rgba(0,0,0,0.85) 40%, rgba(0,0,0,0.3) 70%, transparent 100%);
        padding: 15px 10px 10px;
        opacity: 0;
        transition: opacity 0.3s;
        z-index: 999999;
    }

    .free-modal-image-item:hover .free-modal-image-overlay {
        opacity: 1;
    }

    .free-modal-image-info {
        color: white;
        font-size: 12px;
    }

    .free-modal-author-name {
        font-weight: bold;
        margin-bottom: 3px;
        font-size: 13px;
        text-shadow: 0 1px 3px rgba(0,0,0,0.8), 0 0 10px rgba(0,0,0,0.5);
    }

    .free-modal-image-description {
        font-size: 11px;
        line-height: 1.3;
        opacity: 0.95;
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        text-shadow: 0 1px 3px rgba(0,0,0,0.8), 0 0 8px rgba(0,0,0,0.5);
    }

    .free-modal-image-meta {
        margin-top: 6px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 10px;
        font-size: 11px;
    }

    .free-modal-image-link {
        color: #fff;
        text-decoration: underline;
    }

    .free-modal-image-link:hover,
    .free-modal-image-link:focus {
        color: #fff;
    }

    .free-modal-image-modal-search-input {
        padding-right: 45px !important;
        border-radius: 4px;
        border: 1px solid #ddd;
        height: 40px;
    }

    .free-modal-image-modal-search-input:focus {
        border-color: #66afe9;
        outline: 0;
        box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6);
    }

    .free-modal-image-modal-search-btn {
        position: absolute;
        right: 8px;
        top: 50%;
        transform: translateY(-50%);
        background: none;
        border: none;
        padding: 5px 10px;
        cursor: pointer;
        color: #999;
        transition: color 0.2s;
    }

    .free-modal-image-modal-search-btn:hover {
        color: #333;
    }

    .free-modal-image-modal-search-btn:focus {
        outline: none;
    }
</style>
<script type="text/x-template" id="design-widget-image-modal-template">
    <div v-if="load" class="jaydream design-widget-image-modal">
        <div class="jd-image-upload-header">
            <div class="jd-image-upload-header-main">
                <div>
                    <h4 class="jd-image-modal-title">
                        <img src="/file/icon/이미지추가.svg"> 이미지 추가
                    </h4>
                    <div class="button-group">
                        <button class="btn btn-orange btn-sm" @click="$refs.fileInput.click()">직접 업로드</button>
                        <button class="btn btn-orange btn-sm" @click="widget_type = 'image'; $openModal(modal,{row})">무료 이미지</button>
                        <button class="btn btn-orange btn-sm" @click="widget_type = 'image'; $openModal(modal2,{row})">이미지 보관함</button>
                    </div>
                </div>
                <div class="jd-preview-device-toggle" v-if="row.upload_file || row.file_src">
                    <button type="button" :class="{active: previewDevice == 'pc'}" @click="setPreviewDevice('pc')">
                        <i class="far fa-desktop"></i> PC
                    </button>
                    <button type="button" :class="{active: previewDevice == 'mobile'}" @click="setPreviewDevice('mobile')">
                        <i class="far fa-mobile"></i> 모바일
                    </button>
                </div>
            </div>
            <input class="hidden" ref="fileInput" type="file" @change="vue.changeFile($event,row,'upload_file',{'callback' : changeEvent});">
        </div>

        <div class="jd-image-preview-box jd-image-preview-box-wide" :class="{ 'has-image': row.upload_file || row.file_src }" ref="previewViewport" :style="getPreviewViewportStyle()">
            <template v-if="row.upload_file || row.file_src">
                <div class="jd-image-preview-scale-stage" ref="previewStage" :style="getPreviewScaleStageStyle()">
                    <div :style="getPreviewOuterStyle()">
                        <div :style="getPreviewDivStyle()">
                            <img :src="row.upload_file ? row.upload_file.src : row.file_src" class="jd-preview-image" :style="getStyle()" @load="queuePreviewMetrics()">

                            <div v-if="row.filter_use" :style="getImageOverlayStyle()"></div>

                            <template v-if="row.option_text != '사용안함'">
                                <div :style="getTextStyle()" @dblclick="editor = true;">
                                    <div v-html="row.option_text_content" class="jd-image-text-content"></div>
                                </div>
                            </template>
                        </div>
                    </div>
                </div>
            </template>

            <template v-else>
                <div class="jd-placeholder-text">
                    <i class="glyphicon glyphicon-picture" style="font-size: 48px; color: #ccc;"></i>
                    <p>이미지를 추가해 주세요.</p>
                </div>
            </template>
        </div>

        <template v-if="editor">
            <div class="jd-image-text-editor-wrap">
                <external-summernote :row="row" field="option_text_content" :customEvent="summernoteCustomEvent" :colorEvent="summernoteColorEvent"></external-summernote>
            </div>
        </template>

        <hr>

        <div class="jd-image-basic-setting">
            <h4 class="jd-image-modal-title">
                <img src="/file/icon/basic.svg"> 기본 설정
            </h4>

            <div class="jd-image-basic-grid">
                <div class="form-group" style="margin-bottom: 12px;">
                    <label class="jd-image-modal-content-text">
                        <input type="checkbox" v-model="row.box_shadow_use"> 그림자
                        <select class="form-control input-sm" style="width: 80px; display: inline-block;" v-model="row.box_shadow_opacity">
                            <option value="10">10%</option>
                            <option value="20">20%</option>
                            <option value="30">30%</option>
                            <option value="40">40%</option>
                            <option value="50">50%</option>
                            <option value="60">60%</option>
                            <option value="70">70%</option>
                            <option value="80">80%</option>
                            <option value="90">90%</option>
                        </select>
                    </label>
                </div>

                <div class="form-group" style="margin-bottom: 12px;">
                    <label class="jd-image-modal-content-text">
                        <input type="checkbox" v-model="row.filter_use"> 이미지 오버레이
                        <external-picker v-model="row.filter_overlay_color"></external-picker>
                    </label>
                </div>

                <div class="form-group" style="margin-bottom: 12px;">
                    <label class="jd-image-modal-content-text">
                        <input type="checkbox" v-model="row.border_radius_use"> 모서리 둥글게

                        <div class="number-input-wrapper">
                            <img src="/file/icon/Group 95.svg" class="input-icon-left">
                            <input type="text" v-model="row.border_radius_px" class="form-control form-control-sm" v-number>
                            <div class="number-stepper">
                                <button type="button" class="stepper-up" @click="row.border_radius_px++;">
                                    <svg width="10" height="6" viewBox="0 0 10 6">
                                        <path d="M5 0L10 6H0L5 0Z" fill="#666"/>
                                    </svg>
                                </button>
                                <button type="button" class="stepper-down" @click="row.border_radius_px--;">
                                    <svg width="10" height="6" viewBox="0 0 10 6">
                                        <path d="M5 6L0 0H10L5 6Z" fill="#666"/>
                                    </svg>
                                </button>
                            </div>
                        </div>
                    </label>
                </div>

                <div class="form-group" style="margin-bottom: 12px;">
                    <label class="jd-image-modal-content-text">
                        <input type="checkbox" v-model="row.circle_use" :disabled="row.image_attribute == 'full'"> 원형 이미지 <span class="text-muted" style="font-size: 11px"> ※ 이미지 사이즈를 페이지 폭보다 낮은 1200px 이하로 설정해 주세요.</span>
                    </label>
                </div>

                <div class="form-group" style="margin-bottom: 12px;">
                    <label class="jd-image-modal-content-text">
                        <input type="checkbox" v-model="row.border_use"> 테두리

                        <div style="width: 30px; height: 30px; border-radius: 50%; overflow: hidden; cursor: pointer; border: 1px solid #ddd;">
                            <input type="color"
                                   v-model="row.border_color"
                                   style="width: 150%; height: 150%; border: none; cursor: pointer; transform: translate(-17%, -17%);">
                        </div>

                        <div class="number-input-wrapper">
                            <input type="text" v-model="row.border_width" class="form-control form-control-sm" v-number>
                            <div class="number-stepper">
                                <button type="button" class="stepper-up" @click="row.border_width++;">
                                    <svg width="10" height="6" viewBox="0 0 10 6">
                                        <path d="M5 0L10 6H0L5 0Z" fill="#666"/>
                                    </svg>
                                </button>
                                <button type="button" class="stepper-down" @click="row.border_width--;">
                                    <svg width="10" height="6" viewBox="0 0 10 6">
                                        <path d="M5 6L0 0H10L5 6Z" fill="#666"/>
                                    </svg>
                                </button>
                            </div>
                        </div>
                    </label>
                </div>

                <div class="jd-image-alt-setting">
                    <div style="display: inline-flex; align-items: center; gap: 8px; font-size: 16px; position: relative;">
                        <span style="white-space: nowrap;">alt 태그</span>
                        <img src="/file/icon/물음표.svg"
                             style="cursor: pointer; width: 16px; height: 16px; margin-bottom: 3px;"
                             class="design-widget-multi-modal-each-help-icon"
                             @mouseenter="$event.target.nextElementSibling.classList.add('active')"
                             @mouseleave="$event.target.nextElementSibling.classList.remove('active')">
                        <div class="design-widget-multi-modal-each-help-tooltip">
                            이미지 내용을 글로 설명하는 텍스트입니다.
                            접근성 향상과 SEO(검색 노출)에 도움이 됩니다.
                        </div>
                    </div>

                    <input type="text" v-model="row.alt_text" class="form-control form-control-sm" placeholder="(선택)alt 태그를 입력해주세요.">
                </div>
            </div>
        </div>

        <hr>

        <!-- 이미지 사이즈 & 여백 설정 -->
        <div class="row">
            <!-- 이미지 사이즈 -->
            <div class="col-md-6">
                <h4 class="jd-image-modal-title">
                    <img src="/file/icon/image_size.svg"> 이미지 사이즈
                </h4>

                <div class="jd-image-size-grid">
                    <div class="jd-image-size-row">
                        <div class="jd-image-size-field">
                            <span class="fs-14">높이</span>
                            <input type="text" v-number v-model="row.height" class="form-control input-sm jd-image-size-input" :placeholder="row.file_height ? row.file_height : row.upload_file ? row.upload_file.height : 0 ">
                            <span>px</span>
                        </div>

                        <div class="jd-image-size-field">
                            <span class="fs-14">모바일 전용 높이</span>
                            <input type="text" v-number v-model="row.mobile_height" class="form-control input-sm jd-image-size-input" :placeholder="row.height || row.file_height ? row.height || row.file_height : row.upload_file ? row.upload_file.height : 0">
                            <span>px</span>
                        </div>
                    </div>

                    <template v-if="row.image_attribute == 'self'">
                        <div class="jd-image-size-row">
                            <div class="jd-image-size-field">
                                <span class="fs-14">너비</span>
                                <input type="text" v-number v-model="row.width" class="form-control input-sm jd-image-size-input" :placeholder="row.file_width ? row.file_width : row.upload_file ? row.upload_file.width : 0">
                                <span>px</span>
                            </div>

                            <div class="jd-image-size-field">
                                <span class="fs-14">모바일 전용 너비</span>
                                <input type="text" v-number v-model="row.mobile_width" class="form-control input-sm jd-image-size-input" :placeholder="row.width || row.file_width ? row.width || row.file_width : row.upload_file ? row.upload_file.width : 0">
                                <span>px</span>
                            </div>
                        </div>
                    </template>
                </div>

                <span class="text-muted fs-14">※ 입력값을 지우면 원본 사이즈가 나옵니다.</span>

                <div>
                    <label class="radio-inline">
                        <input type="radio" v-model="row.image_attribute" value="full"> 이미지 채우기
                    </label>
                    <label class="radio-inline">
                        <input type="radio" v-model="row.image_attribute" value="original"> 원본 비율 유지
                    </label>
                    <label class="radio-inline">
                        <input type="radio" v-model="row.image_attribute" value="self"> 직접 설정
                    </label>
                </div>
            </div>

            <!-- 여백 설정 -->
            <div class="col-md-6">
                <h4 class="jd-image-modal-title">
                    <img src="/file/icon/image_margin.svg"> 여백 설정
                </h4>

                <div style="max-width: 190px;">
                    <div class="text-center" style="margin-bottom: 5px;">
                        <input type="text" v-number class="form-control input-sm" v-model="row.padding_top" style="width: 60px; display: inline-block;">
                    </div>
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;">
                        <input type="text" v-number class="form-control input-sm" v-model="row.padding_left" style="width: 60px;">
                        <div style="width: 60px; height: 30px; background: #f5f5f5; border: 1px solid #ddd;"></div>
                        <input type="text" v-number class="form-control input-sm" v-model="row.padding_right" style="width: 60px;">
                    </div>
                    <div class="text-center">
                        <input type="text" v-number class="form-control input-sm" v-model="row.padding_bottom" style="width: 60px; display: inline-block;">
                    </div>
                </div>
            </div>
        </div>

        <hr>

        <div class="panel panel-default" style="margin-bottom: 10px;">
            <div class="panel-heading" style="cursor: pointer; display: flex; justify-content: space-between; align-items: center;" data-toggle="collapse" data-target="#textSection">
                <div>
                    <img src="/file/icon/text.svg" style="height: 23px">
                    <span class="jd-effect-text">텍스트 삽입</span>
                    <button class="jd-effect-btn">
                        <img :src="`/file/icon/${row.option_text}.svg`" style="width: 16px; height: 16px;">
                        <span class="fs-14">{{row.option_text}}</span>
                    </button>
                </div>
                <i class="glyphicon glyphicon-chevron-down"></i>
            </div>
            <div id="textSection" class="panel-collapse collapse">
                <div class="panel-body">
                    <div class="jd-text-section-row">
                        <div class="jd-text-option-list">
                            <div class="jd-action-button" :class="{active : row.option_text == '사용안함'}" @click="row.option_text = '사용안함'; editor = false;">
                                <img src="/file/icon/사용안함.svg">
                                <span>사용안함</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_text == '이미지 하단'}" @click="row.option_text = '이미지 하단'">
                                <img src="/file/icon/이미지 하단.svg" >
                                <span>이미지 하단</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_text == '이미지 겹침'}" @click="row.option_text = '이미지 겹침'">
                                <img src="/file/icon/이미지 겹침.svg">
                                <span>이미지 겹침</span>
                            </div>
                        </div>

                        <div class="jd-text-setting-panel">
                            <div v-if="row.option_text == '사용안함'" style="flex : 1; text-align : center; align-content: center;">
                                <span style="font-size: large; color : #959595; ">텍스트를 추가하지 않았습니다.</span>
                            </div>
                            <div v-if="row.option_text == '이미지 하단'" style="flex : 1; align-content: center;">
                                <p class="jd-text-help">
                                    ※ 생성된 텍스트를 더블클릭하면 문구와 서식을 상세 설정할 수 있습니다.
                                </p>
                                <div class="jd-text-margin-table jd-text-margin-table-standalone">
                                    <div class="jd-text-margin-table-header">
                                        <span></span>
                                        <span>상</span>
                                        <span>하</span>
                                        <span>좌</span>
                                        <span>우</span>
                                    </div>
                                    <div class="jd-text-margin-table-row">
                                        <div class="jd-text-margin-table-label">PC 여백</div>
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_top">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_bottom">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_left">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_right">
                                    </div>
                                    <div class="jd-text-margin-table-row">
                                        <div class="jd-text-margin-table-label">모바일 여백</div>
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_top">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_bottom">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_left">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_right">
                                    </div>
                                </div>
                            </div>

                            <div v-if="row.option_text == '이미지 겹침'" style="flex:1;">
                                <p class="jd-text-help">
                                    ※ 생성된 텍스트를 더블클릭하면 문구와 서식을 상세 설정할 수 있습니다.
                                </p>
                                <div class="jd-text-overlay-setting">
                                    <!-- 텍스트 위치 -->
                                    <div class="jd-text-position-setting">
                                        <div style="margin-bottom: 5px; font-weight: bold;">텍스트 위치</div>
                                        <div class="jd-text-position-grid">
                                            <div class="jd-position-cell" :class="{active: row.option_text_position === 'top-left'}" @click="row.option_text_position = 'top-left'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_text_position === 'top-center'}" @click="row.option_text_position = 'top-center'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_text_position === 'top-right'}" @click="row.option_text_position = 'top-right'"></div>

                                            <div class="jd-position-cell" :class="{active: row.option_text_position === 'middle-left'}" @click="row.option_text_position = 'middle-left'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_text_position === 'middle-center'}" @click="row.option_text_position = 'middle-center'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_text_position === 'middle-right'}" @click="row.option_text_position = 'middle-right'"></div>

                                            <div class="jd-position-cell" :class="{active: row.option_text_position === 'bottom-left'}" @click="row.option_text_position = 'bottom-left'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_text_position === 'bottom-center'}" @click="row.option_text_position = 'bottom-center'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_text_position === 'bottom-right'}" @click="row.option_text_position = 'bottom-right'"></div>
                                        </div>
                                    </div>

                                    <!-- 텍스트 여백 -->
                                    <div class="jd-text-margin-table">
                                        <div class="jd-text-margin-table-header">
                                            <span></span>
                                            <span>상</span>
                                            <span>하</span>
                                            <span>좌</span>
                                            <span>우</span>
                                        </div>
                                        <div class="jd-text-margin-table-row">
                                            <div class="jd-text-margin-table-label">PC 여백</div>
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_top">
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_bottom">
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_left">
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_right">
                                        </div>
                                        <div class="jd-text-margin-table-row">
                                            <div class="jd-text-margin-table-label">모바일 여백</div>
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_top">
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_bottom">
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_left">
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_right">
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 마우스 클릭 동작 (닫힌 상태) -->
        <div class="panel panel-default" style="margin-bottom: 10px;">
            <div class="panel-heading" style="cursor: pointer; display: flex; justify-content: space-between; align-items: center;" data-toggle="collapse" data-target="#clickSection">
                <div>
                    <img src="/file/icon/mouse_click.svg" style="height: 23px">
                    <span class="jd-effect-text">마우스 클릭 동작</span>
                    <button class="jd-effect-btn">
                        <img :src="`/file/icon/${row.option_mouse_click}.svg`" style="width: 16px; height: 16px;">
                        <span class="fs-14">{{row.option_mouse_click}}</span>
                    </button>
                </div>
                <i class="glyphicon glyphicon-chevron-down"></i>
            </div>
            <div id="clickSection" class="panel-collapse collapse">
                <div class="panel-body">
                    <div class="row" style="display: flex;">
                        <div class="col-md-6" style="border-right: 1px solid #ddd;">
                            <div class="jd-action-button" :class="{active : row.option_mouse_click == '동작없음'}" @click="row.option_mouse_click = '동작없음'">
                                <img src="/file/icon/동작없음.svg">
                                <span>동작없음</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_mouse_click == '링크 이동'}" @click="row.option_mouse_click = '링크 이동'">
                                <img src="/file/icon/링크 이동.svg" >
                                <span>링크 이동</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_mouse_click == '크게 보기'}" @click="row.option_mouse_click = '크게 보기'">
                                <img src="/file/icon/크게 보기.svg">
                                <span>크게 보기</span>
                            </div>
                        </div>

                        <div class="col-md-6" style="display : flex;">
                            <div style="flex: 1; text-align: center; align-content: center;">
                                <template v-if="row.option_mouse_click == '동작없음'">
                                    <span style="font-size: large; color : #959595; ">마우스 클릭 동작을 선택하지 않았습니다.</span>
                                </template>

                                <template v-if="row.option_mouse_click == '링크 이동'">
                                    <div style="display: flex; gap : 5px; align-items: center;">
                                        <select class="form-control" style="flex : 2;" v-model="row.option_mouse_click_url_type">
                                            <option value="URL">URL</option>
                                            <option value="페이지">페이지</option>
                                        </select>

                                        <template v-if="row.option_mouse_click_url_type == 'URL'">
                                            <input type="text" class="form-control" style="flex : 8;" v-model="row.option_mouse_click_url" placeholder="https://">
                                        </template>

                                        <template v-if="row.option_mouse_click_url_type == '페이지'">
                                            <template v-if="menus.length == 0">데이터를 불러들이는 중입니다.</template>

                                            <template v-else>
                                                <select class="form-control" style="flex : 8;" v-model="row.option_mouse_click_url">
                                                    <option value="">선택해주세요.</option>
                                                    <template v-for="item in menus">
                                                        <option :value="item.url">{{item.name}}</option>
                                                    </template>
                                                </select>
                                            </template>
                                        </template>
                                    </div>


                                    <div style="display: flex; margin-top: 10px; gap : 10px; align-items: center;">
                                        <input type="radio" value="_self" v-model="row.option_mouse_click_target"> 기존 창에서 이동
                                        <input type="radio" value="_blank" v-model="row.option_mouse_click_target"> 새 창으로 열기
                                    </div>
                                </template>

                                <template v-if="row.option_mouse_click == '크게 보기'">
                                    <img src="/file/icon/전구.svg" style="width: 22px; margin-right: 3px; margin-bottom: 9px;">
                                    <span style="font-size: large; color : #959595; ">이미지 클릭 시 확대된 이미지 창이 노출됩니다.</span>
                                </template>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 스크롤 애니메이션 (닫힌 상태) -->
        <div class="panel panel-default" style="margin-bottom: 10px;">
            <div class="panel-heading" style="cursor: pointer; display: flex; justify-content: space-between; align-items: center;" data-toggle="collapse" data-target="#scrollSection">
                <div>
                    <img src="/file/icon/scroll.svg" style="height: 23px">
                    <span class="jd-effect-text">스크롤 애니메이션</span>
                    <button class="jd-effect-btn">
                        <img :src="`/file/icon/${row.option_scroll}.svg`" style="width: 16px; height: 16px;">
                        <span class="fs-14">{{row.option_scroll}}</span>
                    </button>
                </div>
                <i class="glyphicon glyphicon-chevron-down"></i>
            </div>
            <div id="scrollSection" class="panel-collapse collapse">
                <div class="panel-body">
                    <div class="row" style="display: flex;">
                        <div class="col-md-6" style="border-right: 1px solid #ddd;">
                            <div class="jd-action-button" :class="{active : row.option_scroll == '효과없음'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '효과없음'">
                                <img src="/file/icon/효과없음.svg">
                                <span>효과없음</span>
                            </div>
                            <div class="jd-action-button" :class="{active : row.option_scroll == '페이드'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '페이드'">
                                <img src="/file/icon/페이드.svg">
                                <span>페이드</span>
                            </div>
                            <div class="jd-action-button" :class="{active : row.option_scroll == '줌인'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '줌인'">
                                <img src="/file/icon/줌인.svg">
                                <span>줌인</span>
                            </div>
                            <div class="jd-action-button" :class="{active : row.option_scroll == '줌아웃'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '줌아웃'">
                                <img src="/file/icon/줌아웃.svg">
                                <span>줌아웃</span>
                            </div>
                            <div class="jd-action-button" :class="{active : row.option_scroll == '플립'}" @click=" row.option_scroll_direction = 'up'; row.option_scroll = '플립'">
                                <img src="/file/icon/플립.svg">
                                <span>플립</span>
                            </div>
                        </div>

                        <div class="col-md-6" style="display : flex;">
                            <template v-if="row.option_scroll == '효과없음'">
                                <div style="flex: 1; text-align: center; align-content: center;">
                                    <span style="font-size: large; color : #959595; ">스크롤 애니메이션을 선택하지 않았습니다.</span>
                                </div>
                            </template>

                            <template v-else>
                                <div style="display: flex; flex-direction: column; gap: 15px; padding: 15px;">
                                    <!-- 방향 -->
                                    <div style="display: flex; align-items: center; gap: 10px;">
                                        <label style="margin: 0; font-weight: 600; font-size: 14px;">방향</label>
                                        <div style="display: flex; gap: 6px;">
                                            <button class="jd-direction-btn" v-if="row.option_scroll != '플립'" @click="row.option_scroll_direction = 'center'" :class="{active : row.option_scroll_direction == 'center'}">
                                                <i class="fas fa-circle" style="font-size: 12px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_scroll_direction = 'up'" :class="{active : row.option_scroll_direction == 'up'}">
                                                <i class="fas fa-arrow-up" style="font-size: 14px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_scroll_direction = 'down'" :class="{active : row.option_scroll_direction == 'down'}">
                                                <i class="fas fa-arrow-down" style="font-size: 14px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_scroll_direction = 'left'" :class="{active : row.option_scroll_direction == 'left'}">
                                                <i class="fas fa-arrow-left" style="font-size: 14px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_scroll_direction = 'right'" :class="{active : row.option_scroll_direction == 'right'}">
                                                <i class="fas fa-arrow-right" style="font-size: 14px;"></i>
                                            </button>
                                        </div>
                                    </div>

                                    <div style="display: flex; align-items: center; gap: 5px;">
                                        <label style="margin: 0; font-weight: 600; font-size: 14px;">지속시간</label>
                                        <input type="number" v-number v-model="row.option_scroll_duration" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                        <span style="font-size: 14px; color: #666;">ms</span>

                                        <label style="margin: 0; font-weight: 600; font-size: 14px; margin-left : 10px;">지연시간</label>
                                        <input type="number" v-number v-model="row.option_scroll_delay" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                        <span style="font-size: 14px; color: #666;">ms</span>
                                    </div>

                                </div>
                            </template>

                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 마우스 오버 애니메이션 (닫힌 상태) -->
        <div class="panel panel-default" style="margin-bottom: 10px;">
            <div class="panel-heading" style="cursor: pointer; display: flex; justify-content: space-between; align-items: center;" data-toggle="collapse" data-target="#hoverSection">
                <div>
                    <img src="/file/icon/mouse_over.svg" style="height: 23px">
                    <span class="jd-effect-text">마우스 오버 애니메이션</span>
                    <button class="jd-effect-btn">
                        <img :src="`/file/icon/${row.option_mouse_over}.svg`" style="width: 16px; height: 16px;">
                        <span class="fs-14">{{row.option_mouse_over}}</span>
                    </button>
                </div>
                <i class="glyphicon glyphicon-chevron-down"></i>
            </div>
            <div id="hoverSection" class="panel-collapse collapse">
                <div class="panel-body">
                    <div class="row" style="display: flex;">
                        <div class="col-md-6" style="border-right: 1px solid #ddd;">
                            <div class="jd-action-button" :class="{active : row.option_mouse_over == '효과없음'}" @click="row.option_mouse_over = '효과없음'">
                                <img src="/file/icon/효과없음.svg">
                                <span>효과없음</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_mouse_over == '페이드'}" @click="row.option_mouse_over_direction = 'center'; row.option_mouse_over = '페이드';">
                                <img src="/file/icon/페이드.svg">
                                <span>페이드</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_mouse_over == '줌인'}" @click="row.option_mouse_over_direction = 'center'; row.option_mouse_over = '줌인'">
                                <img src="/file/icon/줌인.svg">
                                <span>줌인</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_mouse_over == '줌아웃'}" @click="row.option_mouse_over_direction = 'center'; row.option_mouse_over = '줌아웃'">
                                <img src="/file/icon/줌아웃.svg">
                                <span>줌아웃</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_mouse_over == '플립'}" @click="row.option_mouse_over_direction = 'up'; row.option_mouse_over = '플립'">
                                <img src="/file/icon/플립.svg">
                                <span>플립</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_mouse_over == '테두리'}" @click="row.option_mouse_over = '테두리'">
                                <img src="/file/icon/테두리.svg">
                                <span>테두리</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_mouse_over == '그림자'}" @click="row.option_mouse_over = '그림자'">
                                <img src="/file/icon/그림자.svg">
                                <span>그림자</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_mouse_over == '색 오버레이'}" @click="row.option_mouse_over = '색 오버레이'">
                                <img src="/file/icon/색 오버레이.svg">
                                <span>색 오버레이</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_mouse_over == '이미지 전환'}" @click="row.option_mouse_over = '이미지 전환'">
                                <img src="/file/icon/이미지 전환.svg">
                                <span>이미지 전환</span>
                            </div>

                            <div class="jd-action-button" :class="{active : row.option_mouse_over == '텍스트'}" @click="row.option_mouse_over = '텍스트'">
                                <img src="/file/icon/텍스트.svg">
                                <span>텍스트</span>
                            </div>
                        </div>

                        <div class="col-md-6" style="display : flex; align-items: center;">
                            <template v-if="row.option_mouse_over == '효과없음'">
                                <div style="flex: 1; text-align: center; align-content: center;">
                                    <span style="font-size: large; color : #959595; ">마우스오버 애니메이션을 선택하지 않았습니다.</span>
                                </div>
                            </template>

                            <template v-if="['페이드','줌인','줌아웃','플립'].includes(row.option_mouse_over)">
                                <div style="display: flex; flex-direction: column; gap: 15px; padding: 15px;">
                                    <!-- 방향 -->
                                    <div style="display: flex; align-items: center; gap: 10px;">
                                        <label style="margin: 0; font-weight: 600; font-size: 14px;">방향</label>
                                        <div style="display: flex; gap: 6px;">
                                            <button class="jd-direction-btn" v-if="row.option_mouse_over != '플립'" @click="row.option_mouse_over_direction = 'center'" :class="{active : row.option_mouse_over_direction == 'center'}">
                                                <i class="fas fa-circle" style="font-size: 12px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_mouse_over_direction = 'up'" :class="{active : row.option_mouse_over_direction == 'up'}">
                                                <i class="fas fa-arrow-up" style="font-size: 14px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_mouse_over_direction = 'down'" :class="{active : row.option_mouse_over_direction == 'down'}">
                                                <i class="fas fa-arrow-down" style="font-size: 14px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_mouse_over_direction = 'left'" :class="{active : row.option_mouse_over_direction == 'left'}">
                                                <i class="fas fa-arrow-left" style="font-size: 14px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_mouse_over_direction = 'right'" :class="{active : row.option_mouse_over_direction == 'right'}">
                                                <i class="fas fa-arrow-right" style="font-size: 14px;"></i>
                                            </button>
                                        </div>
                                    </div>

                                    <div style="display: flex; align-items: center; gap: 5px;">
                                        <label style="margin: 0; font-weight: 600; font-size: 14px;">지속시간</label>
                                        <input type="number" v-number v-model="row.option_mouse_over_duration" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                        <span style="font-size: 14px; color: #666;">ms</span>

                                        <label style="margin: 0; font-weight: 600; font-size: 14px; margin-left : 10px;">지연시간</label>
                                        <input type="number" v-number v-model="row.option_mouse_over_delay" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                        <span style="font-size: 14px; color: #666;">ms</span>
                                    </div>

                                </div>
                            </template>

                            <template v-if="row.option_mouse_over == '테두리'">
                                <div>
                                    <div style="display: flex; gap : 10px; align-items: center;">
                                        <span style="margin: 0; font-weight: 600; font-size: 14px;">테두리 색상</span>


                                        <div style="width: 30px; height: 30px; border-radius: 50%; overflow: hidden; cursor: pointer; border: 1px solid #ddd;">
                                            <input type="color"
                                                   v-model="row.option_mouse_over_border_color"
                                                   style="width: 150%; height: 150%; border: none; cursor: pointer; transform: translate(-17%, -17%);">
                                        </div>
                                    </div>

                                    <div style="display: flex; gap : 10px; align-items: center;">
                                        <span style="margin: 0; font-weight: 600; font-size: 14px;">테두리 굵기</span>
                                        <input type="number"
                                               class="form-control input-sm"
                                               v-model.number="row.option_mouse_over_border_width"
                                               min="1"
                                               style="width: 60px; display: inline-block;">
                                    </div>
                                </div>
                            </template>

                            <template v-if="row.option_mouse_over == '그림자'">
                                <div style="display: flex; gap : 10px; align-items: center;">
                                    <span style="margin: 0; font-weight: 600; font-size: 14px;">그림자 농도</span>
                                    <select class="form-control input-sm" style="width: 80px; display: inline-block;" v-model="row.option_mouse_over_box_shadow_opacity">
                                        <option value="10">10%</option>
                                        <option value="20">20%</option>
                                        <option value="30">30%</option>
                                        <option value="40">40%</option>
                                        <option value="50">50%</option>
                                        <option value="60">60%</option>
                                        <option value="70">70%</option>
                                        <option value="80">80%</option>
                                        <option value="90">90%</option>
                                    </select>
                                </div>
                            </template>

                            <template v-if="row.option_mouse_over == '색 오버레이'">
                                <div>
                                    <div style="display: flex; gap : 10px; align-items: center;">
                                        <span style="margin: 0; font-weight: 600; font-size: 14px;">색상</span>
                                        <external-picker v-model="row.option_mouse_over_background_color"></external-picker>
                                    </div>
                                </div>
                            </template>

                            <template v-if="row.option_mouse_over == '이미지 전환'">
                                <div style="display: flex; flex-direction: column; gap: 15px; padding: 15px; width: 100%;">
                                    <!-- 이미지 추가 섹션 -->
                                    <div style="display: flex; align-items: center; gap: 10px;">
                                        <div style="margin-bottom: 8px; font-weight: 600; font-size: 14px;">이미지 추가</div>
                                        <div style="display: flex; gap: 10px;">
                                            <template v-if="row.hover_file || row.option_mouse_over_image_src">
                                                <input type="text" class="form-control" style="flex: 1" readonly :value="row.hover_file ? row.hover_file.name : row.option_mouse_over_image_name ">

                                                <button @click="row.hover_file = null; row.option_mouse_over_image_src = '';"  style="border: none; background: none; cursor: pointer; padding: 5px;">
                                                    <i class="far fa-trash-alt" style="font-size: 18px; color: #dc3545;"></i>
                                                </button>
                                            </template>

                                            <template v-else>
                                                <button type="button" class="jd-white-btn" @click="$refs.fileInput2.click()">직접 업로드</button>
                                                <button type="button" class="jd-white-btn" @click="widget_type = 'image_hover'; $openModal(modal,{row});">무료 이미지</button>
                                                <button type="button" class="jd-white-btn" @click="widget_type = 'image_hover'; $openModal(modal2,{row})">이미지 보관함</button>
                                                <input class="hidden" ref="fileInput2" type="file" @change="vue.changeFile($event,row,'hover_file',{'callback' : changeEvent2});">
                                            </template>
                                        </div>
                                    </div>
                                    <div style="margin-top: 8px; font-size: 12px; color: #666;">
                                        ※ 애니메이션 이미지는 대상 이미지의 크기에 자동으로 맞춰집니다.
                                    </div>

                                    <!-- 전환 애니메이션 섹션 -->
                                    <div>
                                        <div style="margin-bottom: 8px; font-weight: 600; font-size: 14px;">전환 애니메이션</div>
                                        <select class="form-control" style="font-size: 13px;" v-model="row.option_mouse_over_image_animation">
                                            <option value="">없음</option>
                                            <option value="페이드">페이드</option>
                                            <option value="슬라이드">슬라이드</option>
                                            <option value="플립">플립</option>
                                        </select>
                                    </div>
                                </div>
                            </template>

                            <template v-if="row.option_mouse_over == '텍스트'">
                                <div style="display: flex; flex-direction: column; gap: 5px; padding: 15px; width: 100%;">
                                    <div style="display: flex; gap : 5px; align-items: center;">
                                        <span style="margin: 0; font-weight: 600; font-size: 14px;">텍스트</span>

                                        <div style="width: 30px; height: 30px; border-radius: 50%; overflow: hidden; cursor: pointer; border: 1px solid #ddd;">
                                            <input type="color"
                                                   v-model="row.option_mouse_over_text_color"
                                                   style="width: 150%; height: 150%; border: none; cursor: pointer; transform: translate(-17%, -17%);">
                                        </div>

                                        <span style="margin: 0; font-weight: 600; font-size: 14px;">배경</span>

                                        <div style="width: 30px; height: 30px; border-radius: 50%; overflow: hidden; cursor: pointer; border: 1px solid #ddd;">
                                            <input type="color"
                                                   v-model="row.option_mouse_over_text_background"
                                                   style="width: 150%; height: 150%; border: none; cursor: pointer; transform: translate(-17%, -17%);">
                                        </div>

                                        <span style="margin: 0; font-weight: 600; font-size: 14px;">폰트크기</span>
                                        <select class="form-control input-sm" style="width: 65px; display: inline-block;" v-model="row.option_mouse_over_font_size">
                                            <option value="10">10</option>
                                            <option value="11">11</option>
                                            <option value="12">12</option>
                                            <option value="13">13</option>
                                            <option value="14">14</option>
                                            <option value="15">15</option>
                                            <option value="16">16</option>
                                            <option value="18">18</option>
                                            <option value="20">20</option>
                                            <option value="24">24</option>
                                            <option value="32">32</option>
                                            <option value="36">36</option>
                                            <option value="40">40</option>
                                            <option value="48">48</option>
                                            <option value="64">64</option>
                                            <option value="96">96</option>
                                            <option value="128">128</option>
                                        </select>

                                        <span style="margin: 0; font-weight: 600; font-size: 14px;">위치</span>
                                        <div class="jd-text-position-grid">
                                            <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'top-left'}" @click="row.option_mouse_over_text_position = 'top-left'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'top-center'}" @click="row.option_mouse_over_text_position = 'top-center'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'top-right'}" @click="row.option_mouse_over_text_position = 'top-right'"></div>

                                            <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'middle-left'}" @click="row.option_mouse_over_text_position = 'middle-left'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'middle-center'}" @click="row.option_mouse_over_text_position = 'middle-center'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'middle-right'}" @click="row.option_mouse_over_text_position = 'middle-right'"></div>

                                            <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'bottom-left'}" @click="row.option_mouse_over_text_position = 'bottom-left'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'bottom-center'}" @click="row.option_mouse_over_text_position = 'bottom-center'"></div>
                                            <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'bottom-right'}" @click="row.option_mouse_over_text_position = 'bottom-right'"></div>
                                        </div>
                                    </div>

                                    <div style="display:flex; align-items: center; gap: 10px;">
                                        <span style="margin: 0; font-weight: 600; font-size: 14px;">문구</span>
                                        <input type="text" class="form-control" style="flex: 1" v-model="row.option_mouse_over_text">
                                    </div>

                                    <div style="display:flex; align-items: center; gap: 10px;">
                                        <span style="margin: 0; font-weight: 600; font-size: 14px;">전환 애니메이션</span>
                                        <select class="form-control input-sm" style="width: 80px; display: inline-block; flex : 1" v-model="row.option_mouse_over_text_animation">
                                            <option value="">없음</option>
                                            <option value="페이드">페이드</option>
                                            <option value="슬라이드">슬라이드</option>
                                            <option value="플립">플립</option>
                                        </select>
                                    </div>
                                </div>
                            </template>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div class="modal-footer" style="position:sticky;right:0;bottom:0;">
            <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
            <button type="button" class="btn btn-orange save" @click="dataPost()">저장</button>
        </div>

        <design-widget-image-free-modal v-model="modal" @update:modelValue="value => $emit('update:modelValue', value)" :widget_type></design-widget-image-free-modal>
        <design-widget-image-storage-modal v-model="modal2" @update:modelValue="value => $emit('update:modelValue', value)" :widget_type></design-widget-image-storage-modal>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-image-modal",object : {
            template: "#design-widget-image-modal-template",
            props: {
                primary : {type : String, default : ""},
                widget_code : {type : String, default : "1"},
                site_code : {type : String, default : "st68f18a193702e"},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-image-modal",
                    component_idx: "",

                    modal : this.lib.createModalObject(),
                    modal2 : this.lib.createModalObject(),

                    sessions : {},

                    table : null,
                    widget_type : "image",
                    row: {
                        widget_code : this.widget_code,
                        user_idx : "",
                        upload_file : null,
                        file_src : "",
                        box_shadow_use : false,
                        box_shadow_opacity : "20",
                        filter_use : false,
                        filter_brightness : "50",
                        filter_overlay_color : "#00000030",
                        border_radius_use : false,
                        border_radius_px : "6",
                        circle_use : false,
                        border_use : false,
                        border_width : "1",
                        border_color : "#000000",
                        height : "400",
                        width : "0",
                        mobile_height : "",
                        mobile_width : "",
                        image_attribute : "full",
                        padding_top : "0",
                        padding_right : "0",
                        padding_bottom : "0",
                        padding_left : "0",

                        option_text : "사용안함",
                        option_text_content : "더블 클릭하여 텍스트를 수정해주세요.",
                        option_text_position : "middle-center",
                        option_text_background : "transparent",
                        option_text_margin_top : "0",
                        option_text_margin_right : "0",
                        option_text_margin_bottom : "0",
                        option_text_margin_left : "0",
                        option_text_mobile_margin_top : "0",
                        option_text_mobile_margin_right : "0",
                        option_text_mobile_margin_bottom : "0",
                        option_text_mobile_margin_left : "0",

                        option_mouse_click : "동작없음",
                        option_mouse_click_url_type : "URL",
                        option_mouse_click_url : "",
                        option_mouse_click_target : "_self",

                        option_scroll : "효과없음",
                        option_scroll_direction : "center",
                        option_scroll_duration : "1000",
                        option_scroll_delay : "0",


                        // 페이드~ 플립
                        option_mouse_over : "효과없음",
                        option_mouse_over_direction : "center",
                        option_mouse_over_duration : "1000",
                        option_mouse_over_delay : "0",
                        //테두리
                        option_mouse_over_border_color : "#000000",
                        option_mouse_over_border_width : "1",
                        //그림자
                        option_mouse_over_box_shadow_opacity : "20",
                        //색 오버레이
                        option_mouse_over_background_color : "#00000030",
                        option_mouse_over_overlay_opacity: "",
                        //이미지 전환
                        hover_file : null,
                        option_mouse_over_image_name : "",
                        option_mouse_over_image_src : "",
                        option_mouse_over_image_animation : "",
                        //텍스트
                        option_mouse_over_text_color : "#ffffff",
                        option_mouse_over_text_background : "#0000008c",
                        option_mouse_over_font_size : "14",
                        option_mouse_over_text_position : "middle-center",
                        option_mouse_over_text : "",
                        option_mouse_over_text_animation : "",

                        alt_text : "",
                    },
                    rows : [],
                    menus : [],
                    menu_load : false,

                    editor : false,
                    viewportWidth : typeof window !== "undefined" ? window.innerWidth : 0,
                    previewDevice : (typeof document !== "undefined" && document.body && document.body.classList.contains('_mbv')) ? 'mobile' : 'pc',
                    previewViewportWidth : 0,
                    previewBaseWidth : 0,
                    previewStageHeight : 0,
                    previewMeasureFrame : null,
                    previewMeasureTimeouts : [],
                    previewResizeObserver : null,
                    previewObservedViewport : null,
                    previewObservedTarget : null,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                if(!this.site_code) {
                    await this.lib.alert("잘못된 접근입니다.")
                    window.history.back();
                    return false;
                }
                this.sessions = await this.session.all();
                this.table = await this.api.table("jd_widget_image");
                this.table.where('widget_code',this.widget_code);
                let row = await this.table.get();
                if(row.length > 0) this.row = row[0];

                this.normalizeTextMobileMargins();
                this.migrateOverlayColors();

                this.row.user_idx = this.sessions.mem_idx;
                // await this.table.get(this.rows,{paging : 10})

                this.handleResize = () => {
                    this.viewportWidth = window.innerWidth;
                    this.queuePreviewMetrics();
                };
                window.addEventListener("resize", this.handleResize);

                this.load = true;

                this.$nextTick(async () => {
                    this.queuePreviewMetrics();
                    [120, 350, 800].forEach((delay) => {
                        const timeout = setTimeout(() => {
                            this.queuePreviewMetrics();
                        }, delay);
                        this.previewMeasureTimeouts.push(timeout);
                    });

                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                    //해당부분은 시간이 좀 걸리길래 비동기로 변경
                    this.api.table("menu").where("site_code",this.site_code).get(this.menus);
                });
            },
            updated() {
            },
            beforeUnmount() {
                this.destroyPreviewMetrics();
            },
            beforeDestroy() {
                this.destroyPreviewMetrics();
            },
            methods: {
                destroyPreviewMetrics() {
                    if(this.handleResize) {
                        window.removeEventListener("resize", this.handleResize);
                    }

                    if(this.previewMeasureFrame) {
                        const cancelFrame = window.cancelAnimationFrame || clearTimeout;
                        cancelFrame(this.previewMeasureFrame);
                        this.previewMeasureFrame = null;
                    }

                    this.previewMeasureTimeouts.forEach((timeout) => {
                        clearTimeout(timeout);
                    });
                    this.previewMeasureTimeouts = [];

                    if(this.previewResizeObserver) {
                        this.previewResizeObserver.disconnect();
                        this.previewResizeObserver = null;
                        this.previewObservedViewport = null;
                        this.previewObservedTarget = null;
                    }
                },
                queuePreviewMetrics() {
                    if(!this.load) return;

                    if(this.previewMeasureFrame) {
                        const cancelFrame = window.cancelAnimationFrame || clearTimeout;
                        cancelFrame(this.previewMeasureFrame);
                    }

                    const requestFrame = window.requestAnimationFrame || function(callback) {
                        return setTimeout(callback, 16);
                    };

                    this.previewMeasureFrame = requestFrame(() => {
                        this.previewMeasureFrame = null;
                        this.updatePreviewMetrics();
                    });
                },
                updatePreviewMetrics() {
                    if(!this.$refs.previewViewport) return;

                    this.observePreviewTargets();

                    const viewportWidth = Math.round(this.$refs.previewViewport.clientWidth || 0);
                    const baseWidth = Math.round(this.getPreviewBaseWidth() || viewportWidth || 0);

                    if(viewportWidth && Math.abs(this.previewViewportWidth - viewportWidth) > 1) {
                        this.previewViewportWidth = viewportWidth;
                    }

                    if(baseWidth && Math.abs(this.previewBaseWidth - baseWidth) > 1) {
                        this.previewBaseWidth = baseWidth;
                    }

                    this.$nextTick(() => {
                        const stage = this.$refs.previewStage;
                        const stageHeight = stage ? Math.round(stage.scrollHeight || stage.offsetHeight || 0) : 0;
                        const fallbackHeight = this.getFallbackPreviewHeight();
                        const nextHeight = stageHeight || fallbackHeight;

                        if(nextHeight && Math.abs(this.previewStageHeight - nextHeight) > 1) {
                            this.previewStageHeight = nextHeight;
                        }
                    });
                },
                observePreviewTargets() {
                    if(typeof ResizeObserver === 'undefined') return;

                    if(!this.previewResizeObserver) {
                        this.previewResizeObserver = new ResizeObserver(() => {
                            this.queuePreviewMetrics();
                        });
                    }

                    const viewport = this.$refs.previewViewport || null;
                    const target = this.getLivePreviewElement();

                    if(viewport !== this.previewObservedViewport) {
                        if(this.previewObservedViewport) this.previewResizeObserver.unobserve(this.previewObservedViewport);
                        if(viewport) this.previewResizeObserver.observe(viewport);
                        this.previewObservedViewport = viewport;
                    }

                    if(target !== this.previewObservedTarget) {
                        if(this.previewObservedTarget) this.previewResizeObserver.unobserve(this.previewObservedTarget);
                        if(target) this.previewResizeObserver.observe(target);
                        this.previewObservedTarget = target;
                    }
                },
                getLivePreviewElement() {
                    if(!this.widget_code || typeof document === 'undefined') return null;

                    const widget = document.getElementById(this.widget_code);
                    if(!widget) return null;

                    const paddingFrame = widget.querySelector('.jd-image-padding-frame');
                    if(paddingFrame) return paddingFrame;

                    const image = widget.querySelector('.jd-image');
                    if(image && image.parentElement) return image.parentElement;

                    return widget;
                },
                getLivePreviewWidth() {
                    const target = this.getLivePreviewElement();
                    if(!target) return 0;

                    const rect = target.getBoundingClientRect();
                    return rect && rect.width ? rect.width : 0;
                },
                getPreviewBaseWidth() {
                    const liveWidth = this.getLivePreviewWidth();
                    const isBodyMobile = typeof document !== 'undefined'
                        && document.body
                        && document.body.classList.contains('_mbv');

                    if(this.previewDevice == 'mobile') {
                        if(isBodyMobile && liveWidth) return liveWidth;
                        if(this.row.image_attribute == 'full') return this.getEstimatedMobilePreviewWidth();
                        return this.getFallbackPreviewWidth();
                    }

                    if(!isBodyMobile && liveWidth) return liveWidth;
                    if(this.row.image_attribute == 'full') return this.getEstimatedPcPreviewWidth();

                    return this.getFallbackPreviewWidth();
                },
                getEstimatedPcPreviewWidth() {
                    if(typeof document === 'undefined') return 0;

                    const viewportWidth = document.documentElement ? document.documentElement.clientWidth : window.innerWidth;
                    return Math.max(0, viewportWidth - 180);
                },
                getEstimatedMobilePreviewWidth() {
                    if(typeof document === 'undefined') return 398;

                    const viewportWidth = document.documentElement ? document.documentElement.clientWidth : window.innerWidth;
                    return Math.max(0, Math.min(562, viewportWidth) - 164);
                },
                getFallbackPreviewWidth() {
                    const height = this.getFallbackPreviewHeight();
                    const width = this.isMobileView()
                        ? (this.row.mobile_width || this.row.width)
                        : this.row.width;
                    const parsedWidth = parseInt(width, 10) || 0;

                    if(this.row.image_attribute == 'self' && parsedWidth) return parsedWidth;

                    if(this.row.image_attribute == 'original') {
                        const fileWidth = parseInt(this.row.file_width, 10) || 0;
                        const fileHeight = parseInt(this.row.file_height, 10) || 0;

                        if(fileWidth && fileHeight && height) {
                            return Math.round(fileWidth * height / fileHeight);
                        }

                        if(fileWidth) return fileWidth;
                    }

                    if(this.row.image_attribute == 'full') {
                        const fileWidth = parseInt(this.row.file_width, 10) || 0;
                        if(parsedWidth) return parsedWidth;
                        if(fileWidth) return fileWidth;
                    }

                    return parsedWidth;
                },
                getFallbackPreviewHeight() {
                    const height = this.isMobileView()
                        ? (this.row.mobile_height || this.row.height)
                        : this.row.height;

                    return parseInt(height, 10) || 0;
                },
                resetPreviewMetrics() {
                    this.previewViewportWidth = 0;
                    this.previewBaseWidth = 0;
                    this.previewStageHeight = 0;
                },
                setPreviewDevice(device) {
                    if(!['pc', 'mobile'].includes(device) || this.previewDevice == device) return;

                    this.previewDevice = device;
                    this.resetPreviewMetrics();
                    this.$nextTick(() => {
                        this.queuePreviewMetrics();
                    });
                },
                getPreviewScale() {
                    const baseWidth = this.previewBaseWidth || this.getPreviewBaseWidth();
                    const viewportWidth = this.previewViewportWidth
                        || (this.$refs.previewViewport ? this.$refs.previewViewport.clientWidth : 0);

                    if(!baseWidth || !viewportWidth) return 1;

                    return Math.min(1, viewportWidth / baseWidth);
                },
                getPreviewViewportStyle() {
                    const scale = this.getPreviewScale();
                    const baseHeight = this.previewStageHeight || this.getFallbackPreviewHeight();
                    let style = {};

                    if(baseHeight) {
                        style.height = `${Math.ceil(baseHeight * scale)}px`;
                    }

                    return style;
                },
                getPreviewScaleStageStyle() {
                    const baseWidth = this.previewBaseWidth || this.getPreviewBaseWidth();
                    const scale = this.getPreviewScale();
                    const viewportWidth = this.previewViewportWidth
                        || (this.$refs.previewViewport ? this.$refs.previewViewport.clientWidth : 0);
                    const visibleWidth = baseWidth * scale;
                    let style = {
                        transform: `scale(${scale})`,
                        transformOrigin: 'top left',
                        textAlign: 'center',
                    };

                    if(!this.previewViewportWidth || !this.previewBaseWidth) {
                        style.visibility = 'hidden';
                    } else {
                        style.visibility = 'visible';
                    }

                    if(baseWidth) {
                        style.width = `${baseWidth}px`;
                    }

                    if(this.previewDevice == 'mobile' && viewportWidth && visibleWidth < viewportWidth) {
                        style.marginLeft = `${Math.floor((viewportWidth - visibleWidth) / 2)}px`;
                    } else {
                        style.marginLeft = '0';
                    }

                    return style;
                },
                summernoteColorEvent(color) {
                    this.row.option_text_background = color;
                },
                summernoteCustomEvent() {
                    this.editor = false;
                },
                async dataPost() {
                    window.design.loadingOn()
                    if(this.row.upload_file) {
                        let res = await this.lib.ajax("file_save",{
                            "user_idx" : this.row.user_idx,
                            "upload_file" : this.row.upload_file,
                            "width" : this.row.file_width,
                            "height" : this.row.file_height,
                            "memo" : "jd_widget_image",
                            "widget_code" : this.row.widget_code,
                            "site_code" : this.site_code,
                        })

                        let file = res['file'];

                        this.row.upload_file = null;
                        this.row.file_src = file.src;
                    }

                    if(this.row.hover_file) {
                        let res = await this.lib.ajax("file_save",{
                            "user_idx" : this.row.user_idx,
                            "hover_file" : this.row.hover_file,
                            "memo" : "jd_widget_image_hover"
                        })

                        let file = res['file'];

                        this.row.hover_file = null;
                        this.row.option_mouse_over_image_name = file.name;
                        this.row.option_mouse_over_image_src = file.src;
                    }

                    await this.table.post(this.row,{return : true});
                    window.design.loadingOff()

                    window.JAYDREAM_VUE_GLOBAL[this.widget_code].reload = true;

                    $(this.$el).closest('.modal').modal('hide');
                },
                changeEvent() {
                    this.row.width = this.row.upload_file.width;

                    this.row.file_width = this.row.upload_file.width;
                    this.row.file_height = this.row.upload_file.height;
                },
                changeEvent2() {
                    // this.row.option_mouse_over_image_name = this.hover_file.name;
                },
                migrateOverlayColors() {
                    if(!this.row.filter_overlay_color) {
                        this.row.filter_overlay_color = this.getLegacyFilterOverlayColor();
                    }

                    this.row.option_mouse_over_background_color = this.normalizeAlphaColor(
                        this.row.option_mouse_over_background_color,
                        '#00000030',
                        this.row.option_mouse_over_overlay_opacity
                    );
                    this.row.option_mouse_over_overlay_opacity = '';
                },
                normalizeTextMobileMargins() {
                    const defaults = {
                        option_text_mobile_margin_top: "0",
                        option_text_mobile_margin_right: "0",
                        option_text_mobile_margin_bottom: "0",
                        option_text_mobile_margin_left: "0",
                    };

                    Object.keys(defaults).forEach((key) => {
                        if(this.row[key] === undefined || this.row[key] === null || this.row[key] === '') {
                            this.row[key] = defaults[key];
                        }
                    });
                },
                getTextMargins() {
                    const prefix = this.isMobileView() ? 'option_text_mobile_margin_' : 'option_text_margin_';

                    return {
                        top: parseInt(this.row[prefix + 'top'], 10) || 0,
                        right: parseInt(this.row[prefix + 'right'], 10) || 0,
                        bottom: parseInt(this.row[prefix + 'bottom'], 10) || 0,
                        left: parseInt(this.row[prefix + 'left'], 10) || 0,
                    };
                },
                normalizeAlphaColor(color, defaultColor = '#00000030', legacyOpacity = null) {
                    color = String(color || '').trim();
                    defaultColor = String(defaultColor || '#00000030').trim();

                    if(/^#[0-9a-f]{6}$/i.test(color)) {
                        if(legacyOpacity !== null && legacyOpacity !== '' && legacyOpacity !== undefined) {
                            return color + this.opacityToHex(legacyOpacity);
                        }

                        return color + 'ff';
                    }

                    if(/^#[0-9a-f]{8}$/i.test(color)) {
                        if(color.toLowerCase() === '#ffffff00' && legacyOpacity !== null && legacyOpacity !== '' && legacyOpacity !== undefined) {
                            return color.slice(0, 7) + this.opacityToHex(legacyOpacity);
                        }

                        return color;
                    }

                    if(/^#[0-9a-f]{6}$/i.test(defaultColor)) return defaultColor + 'ff';
                    if(/^#[0-9a-f]{8}$/i.test(defaultColor)) return defaultColor;

                    return '#00000030';
                },
                opacityToHex(opacity) {
                    const percent = Math.max(0, Math.min(100, parseInt(opacity, 10) || 0));
                    return ('0' + Math.round(percent * 255 / 100).toString(16)).slice(-2);
                },
                hexAlphaToRgba(color) {
                    const normalizedColor = this.normalizeAlphaColor(color);
                    const r = parseInt(normalizedColor.slice(1, 3), 16);
                    const g = parseInt(normalizedColor.slice(3, 5), 16);
                    const b = parseInt(normalizedColor.slice(5, 7), 16);
                    const a = parseInt(normalizedColor.slice(7, 9), 16) / 255;

                    return `rgba(${r}, ${g}, ${b}, ${a})`;
                },
                getLegacyFilterOverlayColor() {
                    const opacity = this.row.filter_brightness !== undefined && this.row.filter_brightness !== ''
                        ? this.row.filter_brightness
                        : 19;
                    return '#000000' + this.opacityToHex(opacity);
                },
                getImageOverlayStyle() {
                    let style = {
                        position: 'absolute',
                        top: '0',
                        left: '0',
                        width: '100%',
                        height: '100%',
                        pointerEvents: 'none',
                        backgroundColor: this.hexAlphaToRgba(this.row.filter_overlay_color || this.getLegacyFilterOverlayColor())
                    };

                    if(this.row.circle_use) {
                        style.borderRadius = '50%';
                    } else if(this.row.border_radius_use) {
                        style.borderRadius = `${this.row.border_radius_px}px`;
                    }

                    return style;
                },
                getPaddingStyle() {
                    let style = {
                        boxSizing: 'border-box',
                        maxWidth: '100%',
                    };

                    if (this.row.padding_top) {
                        style.paddingTop = `${this.row.padding_top}px`;
                    }
                    if (this.row.padding_right) {
                        style.paddingRight = `${this.row.padding_right}px`;
                    }
                    if (this.row.padding_bottom) {
                        style.paddingBottom = `${this.row.padding_bottom}px`;
                    }
                    if (this.row.padding_left) {
                        style.paddingLeft = `${this.row.padding_left}px`;
                    }

                    return style;
                },
                getPreviewOuterStyle() {
                    let style = this.getPaddingStyle();
                    style.display = 'inline-block';
                    style.maxWidth = '100%';

                    if(this.row.image_attribute == 'full') {
                        style.display = 'block';
                        style.width = '100%';
                    }

                    return style;
                },
                getPreviewDivStyle() {
                    let style = {
                        position: 'relative',
                        display: 'inline-block',
                        overflow: 'hidden',
                        maxWidth: '100%',
                        boxSizing: 'border-box',
                    };

                    // 그림자
                    if(this.row.box_shadow_use) {
                        style.boxShadow = `0px 4px 8px rgba(0, 0, 0, ${this.row.box_shadow_opacity / 100})`;
                    }

                    // 모서리 둥글게
                    if (this.row.border_radius_use) {
                        style.borderRadius = `${this.row.border_radius_px}px`;
                    }

                    // 원형 이미지
                    if (this.row.circle_use) {
                        style.borderRadius = '50%';
                    }

                    // 테두리
                    if (this.row.border_use) {
                        style.border = `${this.row.border_width}px solid ${this.row.border_color}`;
                    }

                    if(this.row.image_attribute == 'full') {
                        style.display = 'block';
                        style.width = '100%';
                    }

                    return style;
                },
                getTextStyle() {
                    let style = {};

                    // style.padding = "10px";
                    style.fontSize = "16px";
                    style.background = this.row.option_text_background;
                    // style.width = this.row.width + "px";
                    style.maxWidth = "100%";

                    const margins = this.getTextMargins();
                    const marginTop = margins.top;
                    const marginRight = margins.right;
                    const marginBottom = margins.bottom;
                    const marginLeft = margins.left;

                    style.marginTop = marginTop + "px";
                    style.marginRight = marginRight + "px";
                    style.marginBottom = marginBottom + "px";
                    style.marginLeft = marginLeft + "px";

                    if(this.row.option_text == "이미지 겹침") {
                        style.position = 'absolute';
                        style.left = `${marginLeft}px`;
                        style.right = `${marginRight}px`;
                        style.width = 'auto';
                        style.maxWidth = 'none';
                        style.boxSizing = 'border-box';
                        style.margin = '0';

                        // position에 따른 스타일 적용
                        const position = this.row.option_text_position || 'middle-center';
                        const verticalPosition = position.split('-')[0];
                        const horizontalPosition = position.split('-')[1];

                        if(horizontalPosition === 'center') {
                            style.textAlign = 'center';
                        } else if(horizontalPosition === 'right') {
                            style.textAlign = 'right';
                        } else {
                            style.textAlign = 'left';
                        }

                        if(verticalPosition === 'top') {
                            style.top = `${marginTop}px`;
                        } else if(verticalPosition === 'middle') {
                            const verticalOffset = marginTop - marginBottom;
                            style.top = '50%';
                            style.transform = verticalOffset
                                ? `translateY(calc(-50% ${verticalOffset > 0 ? '+' : '-'} ${Math.abs(verticalOffset)}px))`
                                : 'translateY(-50%)';
                        } else if(verticalPosition === 'bottom') {
                            style.bottom = `${marginBottom}px`;
                        }
                    }

                    return style;
                },
                isMobileView() {
                    return this.previewDevice == 'mobile';
                },
                getStyle() {
                    let style = {};
                    const isMobileView = this.isMobileView();
                    const height = isMobileView ? (this.row.mobile_height || this.row.height) : this.row.height;
                    const width = isMobileView ? (this.row.mobile_width || this.row.width) : this.row.width;

                    // 모서리 둥글게
                    if (this.row.border_radius_use) {
                        style.borderRadius = `${this.row.border_radius_px}px`;
                    }

                    // 원형 이미지
                    if (this.row.circle_use) {
                        style.borderRadius = '50%';
                        style.aspectRatio = '1 / 1';  // 정사각형 비율
                        style.objectFit = 'cover';     // 이미지 가운데 중심으로 채움
                    }

                    // 이미지 사이즈
                    if(this.row.image_attribute == 'full') {
                        style.height = `${height}px`;
                        style.width = '100%';
                        style.objectFit = 'cover';
                    }
                    if(this.row.image_attribute == 'original') {
                        style.height = `${height}px`;
                        if (!this.row.circle_use) style.objectFit = 'contain';
                    }
                    if(this.row.image_attribute == 'self') {
                        style.height = `${height}px`;
                        if (!this.row.circle_use) style.width = `${width}px`;
                        style.objectFit = 'cover';
                    }

                    return style;
                },
            },
            computed: {

            },
            watch: {
                row: {
                    handler() {
                        if(this.load) this.queuePreviewMetrics();
                    },
                    deep: true,
                },
                async "row.image_attribute"(value, old_value) {
                    if(value == "full") this.row.circle_use = false;
                },

                async "row.border_radius_use"(value, old_value) {
                    if(value) this.row.circle_use = false;
                },

                async "row.circle_use"(value, old_value) {
                    if(value) this.row.border_radius_use = false;
                }
            }
        }});
</script>

<style>
    .jd-image-modal-title {
        font-weight: bold;
        margin-bottom: 15px;
        font-size: 16px;
    }

    .jd-image-modal-content-text {
        display: flex;
        align-items: center;
        gap: 10px;
        margin-bottom: 0;
        font-size: 14px;
    }

    .jd-image-size-grid {
        display: flex;
        flex-direction: column;
        gap: 10px;
        margin-bottom: 10px;
    }

    .jd-image-size-row {
        display: flex;
        gap: 28px;
        flex-wrap: wrap;
    }

    .jd-image-size-field {
        display: flex;
        align-items: center;
        gap: 6px;
    }

    .jd-image-size-input {
        width: 80px;
    }

    .jd-image-upload-header {
        margin-bottom: 12px;
    }

    .jd-image-upload-header-main {
        display: flex;
        align-items: flex-end;
        justify-content: space-between;
        gap: 12px;
    }

    .jd-image-upload-header .button-group {
        margin-top: 8px;
    }

    .jd-preview-device-toggle {
        display: inline-flex;
        flex: 0 0 auto;
        margin-bottom: 2px;
        border: 1px solid #f47452;
        border-radius: 4px;
        overflow: hidden;
        background: #fff;
    }

    .jd-preview-device-toggle button {
        min-width: 70px;
        height: 32px;
        border: 0;
        border-right: 1px solid #f47452;
        background: #fff;
        color: #f47452;
        font-size: 13px;
        font-weight: 600;
        line-height: 32px;
        padding: 0 10px;
    }

    .jd-preview-device-toggle button:last-child {
        border-right: 0;
    }

    .jd-preview-device-toggle button.active {
        background: #f47452;
        color: #fff;
    }

    .jd-preview-device-toggle button i {
        margin-right: 4px;
    }

    .jd-image-preview-box-wide {
        width: 100%;
        min-height: 260px;
        margin-bottom: 10px;
        overflow: hidden;
    }

    .jd-image-preview-box-wide.has-image {
        display: block;
        min-height: 0;
        text-align: left;
    }

    .jd-image-preview-scale-stage {
        flex: 0 0 auto;
        will-change: transform;
    }

    .jd-image-preview-scale-stage .jd-preview-image {
        max-height: none;
    }

    .jd-image-text-editor-wrap {
        margin-top: 10px;
    }

    .jd-image-basic-grid {
        display: grid;
        grid-template-columns: repeat(2, minmax(260px, 1fr));
        gap: 8px 24px;
        align-items: start;
    }

    .jd-image-alt-setting {
        display: flex;
        flex-direction: column;
        gap: 6px;
    }

    .jd-text-section-row {
        display: flex;
        align-items: stretch;
        min-height: 170px;
    }

    .jd-text-option-list {
        flex: 0 0 360px;
        max-width: 360px;
        border-right: 1px solid #ddd;
        padding-right: 14px;
        display: flex;
        gap: 10px;
        align-items: flex-start;
        flex-wrap: wrap;
        align-content: flex-start;
    }

    .jd-text-setting-panel {
        flex: 1 1 auto;
        min-width: 0;
        padding-left: 16px;
        display: flex;
        align-items: center;
    }

    .jd-text-overlay-setting {
        display: flex;
        align-items: flex-start;
        gap: 28px;
        flex-wrap: nowrap;
        width: 100%;
    }

    .jd-text-position-setting {
        flex: 0 0 auto;
    }

    .jd-text-help {
        color: #999;
        font-size: 12px;
        margin: 0 0 10px;
    }

    .jd-text-margin-table {
        display: grid;
        grid-template-columns: 76px repeat(4, 58px);
        gap: 8px 10px;
        align-items: center;
    }

    .jd-text-margin-table-standalone {
        justify-content: flex-start;
    }

    .jd-text-margin-table-header {
        display: contents;
    }

    .jd-text-margin-table-header span {
        font-weight: bold;
        font-size: 13px;
        text-align: center;
    }

    .jd-text-margin-table-row {
        display: contents;
    }

    .jd-text-margin-table-label {
        font-weight: bold;
        white-space: nowrap;
    }

    .jd-text-margin-table input {
        width: 58px;
        text-align: center;
    }

    @media (max-width: 980px) {
        .jd-text-section-row {
            flex-direction: column;
        }

        .jd-text-option-list {
            flex: 0 0 auto;
            max-width: none;
            border-right: none;
            border-bottom: 1px solid #ddd;
            padding-right: 0;
            padding-bottom: 12px;
            margin-bottom: 12px;
        }

        .jd-text-setting-panel {
            padding-left: 0;
        }
    }

    .number-input-wrapper {
        position: relative;
        width: 80px;
    }

    .number-input-wrapper input {
        width: 100%;
        padding-left: 24px;   /* 왼쪽 아이콘 공간 */
        padding-right: 24px;  /* 오른쪽 스테퍼 공간 */
        text-align: center;
        font-size: 14px;
    }

    .input-icon-left {
        position: absolute;
        left: 6px;
        top: 50%;
        transform: translateY(-50%);
        width: 14px;
        height: 14px;
        pointer-events: none;
    }

    .number-stepper {
        position: absolute;
        right: 4px;
        top: 50%;
        transform: translateY(-50%);
        display: flex;
        flex-direction: column;
        gap: 0;
    }

    .number-stepper button {
        border: none;
        background: none;
        padding: 0;
        margin: 0;
        cursor: pointer;
        line-height: 1;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 14px;
        height: 10px;
    }

    .number-stepper button:hover svg path {
        fill: #333;
    }

    .number-stepper button:active svg path {
        fill: #999;
    }

    .number-stepper svg {
        display: block;
    }

    .panel-default > .panel-heading {
        background-image: linear-gradient(to bottom, #ffffff 0%, #ffffff 100%) !important;
    }
</style>
<script type="text/x-template" id="design-widget-image-storage-modal-template">
    <div v-if="load">
        <external-bs-modal v-model="modelValue" @update:modelValue="value => $emit('update:modelValue', value)">
            <template v-slot:header>
                <h5 class="modal-title" id="exampleModalLabel">이미지 보관함</h5>
            </template>

            <!-- body -->
            <template v-slot:default>
                <div>
                    <div v-if="loading">이미지 불러오는중...</div>
                    <template v-else>
                        <div v-if="rows.length == 0">이미지가 없습니다.</div>
                        <div v-else>
                            <div class="image-grid-flex">
                                <template v-for="item,index in rows">
                                    <div v-if="index >= (page - 1) * limit && index < page * limit" class="image-item" @click="choiceImage(item)" :class="activeImage(item)">
                                        <img :src="item.src">
                                        <button class="image-delete-btn" @click.stop="deleteImage(item)">
                                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                                <polyline points="3 6 5 6 21 6"></polyline>
                                                <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                                                <line x1="10" y1="11" x2="10" y2="17"></line>
                                                <line x1="14" y1="11" x2="14" y2="17"></line>
                                            </svg>
                                        </button>
                                    </div>
                                </template>
                            </div>

                            <nav aria-label="...">
                                <ul class="pagination pagination-sm">
                                    <template v-for="num in totalPage">
                                        <li class="page-item" :class="{active : num === page}" aria-current="page" @click="page = num;">
                                            <span class="page-link">{{num}}</span>
                                        </li>
                                    </template>
                                </ul>
                            </nav>
                        </div>
                    </template>

                </div>
            </template>


            <template v-slot:footer>
                <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
                <button type="button" class="btn btn-orange save" @click="saveImage()">저장</button>
            </template>
        </external-bs-modal>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-image-storage-modal",object : {
            template: "#design-widget-image-storage-modal-template",
            props: {
                modelValue : {type: Object, default: {}},
                widget_type : {type: String, default: ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-image-storage-modal",
                    component_idx: "",
                    loading : false,

                    temp : {
                        $table : "",
                    },
                    row: {},
                    rows : [],

                    limit : 9,
                    page : 1,

                    select_item : null,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
            },
            async mounted() {
                this.table = await this.api.table("jd_file");

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {
                async deleteImage(item) {
                    if(!await this.lib.confirm("삭제시 연결된 이미지가 있을경우 엑박이 뜹니다 삭제 하시겠습니까?")) return false;

                    await this.api.table("jd_file").delete(item,{return : true});

                    await this.getImages();
                },
                async getImages() {
                    this.loading = true;

                    await this.table.where("user_idx",this.modelValue.row.user_idx).get(this.rows);

                    this.loading = false;

                },
                saveImage() {
                    if(this.widget_type === "image") {
                        this.modelValue.row.file_src = this.select_item.src;
                        this.modelValue.row.file_width = this.select_item.width;
                        this.modelValue.row.file_height = this.select_item.height;

                        // height는 기획자가 고정 400으로 원함
                        this.modelValue.row.width = this.select_item.width;
                    }else if(this.widget_type === "image_hover") {
                        this.modelValue.row.option_mouse_over_image_src = this.select_item.src;
                        this.modelValue.row.option_mouse_over_image_name = this.select_item.name;
                    }

                    this.$closeModal(this.modelValue);

                },
                activeImage(item) {
                    if(this.widget_type === "image" || this.widget_type === "image_hover") {
                        if(item.src === this.select_item?.src) return "active";
                    }

                    return false;
                },
                choiceImage(item) {
                    if(this.widget_type === "image" ||  this.widget_type === "image_hover") {
                        this.select_item = item;
                    }
                },
            },
            computed: {
                totalPage() {
                    return this.rows.length % this.limit === 0 ? this.rows.length / this.limit : Math.floor(this.rows.length / this.limit) + 1;
                }
            },
            watch: {
                async "modelValue.status"(value, old_value) {
                    const $modal = $("#widget_setting_jd");

                    if(!value) {
                        this.file_idx = "";
                        this.rows = [];

                        $modal.data('bs.modal').options.backdrop = true;
                        $modal.data('bs.modal').options.keyboard = true;


                        return false;
                    }


                    $modal.data('bs.modal').options.backdrop = 'static';
                    $modal.data('bs.modal').options.keyboard = false;

                    await this.getImages();
                }
            }
        }});
</script>

<style>
    .image-grid-flex {
        display: grid;
        grid-template-columns: repeat(3, 1fr); /* 4열 */
        padding: 10px 0;
    }

    .image-item {
        cursor: pointer;
        border-radius: 4px;
        overflow: hidden;
        border: 1px solid #ddd;
        transition: all 0.2s;
        aspect-ratio: 1 / 1; /* 정사각형 비율 */
        position: relative; /* 삭제 버튼 위치를 위해 추가 */
    }

    .image-item img {
        width: 100%;
        height: 100%;
        object-fit: cover; /* 이미지를 정사각형에 꽉 차게 */
        display: block;
        transition: all 0.2s;
    }

    .image-item:hover {
        border-color: #337ab7;
        box-shadow: 0 2px 8px rgba(0,0,0,0.15);
        transform: scale(1.05);
    }

    .image-item.active {
        outline: 3px solid red;
        outline-offset: -3px;
    }

    /* 삭제 버튼 스타일 */
    .image-delete-btn {
        position: absolute;
        top: 4px;
        right: 4px;
        width: 28px;
        height: 28px;
        background-color: rgba(255, 255, 255, 0.9);
        border: 1px solid #ddd;
        border-radius: 4px;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
        opacity: 0;
        transition: all 0.2s;
        padding: 0;
    }

    .image-delete-btn svg {
        color: #dc3545;
    }

    .image-item:hover .image-delete-btn {
        opacity: 1;
    }

    .image-delete-btn:hover {
        background-color: #dc3545;
        border-color: #dc3545;
    }

    .image-delete-btn:hover svg {
        color: white;
    }
</style><script type="text/x-template" id="design-widget-image-template">
    <div v-if="load">
        <template v-if="row.idx">
            <div style="text-align: center;" ref="imageElement" @mouseenter="hoverEvent(); parent_div_hover = true;" @mouseleave="leaveEvent(); parent_div_hover = false;">
                <div class="jd-image-padding-frame" :style="getOuterStyle()" @mouseenter="hover = true;" @mouseleave="hover = false;">
                    <div class="jd-image-effect-frame" :style="getDivStyle()">
                    <img :src="getSrc()" class="jd-image" :style="getStyle()"
                         @click="getClick()" @load="onImageLoad" :title="row.alt_text || ''">

                    <div v-if="row.filter_use" :style="getImageOverlayStyle()"></div>

                    <template v-if="row.option_text != '사용안함'">
                        <div :style="getTextStyle()">
                            <div v-html="row.option_text_content" class="jd-image-text-content"></div>
                        </div>
                    </template>

                    <!-- 색오버레이용 레이어 추가 -->
                    <div v-if="row.option_mouse_over == '색 오버레이' && hover"
                         :style="getOverlayStyle()"
                         style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;">
                    </div>

                    <!-- 호버 텍스트를 wrapper로 감싸기 -->
                    <template v-if="row.option_mouse_over == '텍스트' && hover">
                        <div :style="getHoverTextPositionStyle()">
                            <div :style="getHoverTextAnimationStyle()">
                                {{row.option_mouse_over_text}}
                            </div>
                        </div>
                    </template>
                    </div>
                </div>
            </div>
        </template>

        <template v-else>
            <div class="jd-imageupload-box jd-widget-placeholder">
                <img class="jd-widget-placeholder-icon" src="/file/icon/widget-add/image.svg" alt="">
                <p class="jd-imageupload-text">더블 클릭하여 이미지를 추가하세요.</p>
            </div>
        </template>

        <div v-if="viewImage" class="widget-image-overlay" @click="viewImage = ''">
            <img :src="viewImage" @click.stop>
            <button class="widget-overlay-close" @click="viewImage = ''">&times;</button>
        </div>

    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-image",object : {
            template: "#design-widget-image-template",
            props: {
                primary : {type : String, default : ""},
                widget_code : {type : String, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-image",
                    component_idx: "",

                    observer: null,
                    scrollEvent : true,

                    table : null,
                    row: {},
                    rows : [],

                    hover : false,
                    parent_div_hover : false,

                    renderedWidth: null,
                    viewportWidth: typeof window !== "undefined" ? window.innerWidth : 0,

                    modal: {
                        id : "", // modal의 id값을 설정합니다 빈값이라면 고유값을 랜덤으로 생성해 지정합니다
                        class_1: "", // modal fade 부분에 클래스를 추가합니다 ex) "one_class two_class"
                        class_2: "", // modal-dialog 부분에 클래스를 추가합니다
                        status: false,
                        table : "",
                        primary : "",
                        row : null,
                    },

                    viewImage : "",
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_image");
                this.table.where('widget_code',this.widget_code);
                let row = await this.table.get();
                if(row.length > 0) this.row = row[0];

                this.handleResize = () => {
                    this.viewportWidth = window.innerWidth;
                };
                window.addEventListener("resize", this.handleResize);

                this.load = true;

                this.$nextTick(async () => {
                    if(!this.row.file_src) $('.firewall').addClass('widget_setting_btn');


                    this.initScrollObserver();

                    if(window.design) {
                        window.design.menu.grid.reset();
                    }
                });
            },
            updated() {

            },
            beforeUnmount() {
                window.removeEventListener("resize", this.handleResize);
            },
            methods: {
                isMobileView() {
                    return document.body.classList.contains('_mbv')
                        || this.viewportWidth <= 768
                        || this.lib.isMobile('device');
                },
                onImageLoad(event) {
                    if(this.row.image_attribute == 'original' && !this.renderedWidth) {
                        this.renderedWidth = event.target.offsetWidth;
                    }
                },
                getSrc() {
                    if(this.row.option_mouse_over == '이미지 전환' && this.hover) {
                        return this.row.option_mouse_over_image_src;
                    }

                    return this.row.file_src
                },
                leaveEvent() {
                    if(['페이드','줌인','줌아웃','플립'].includes(this.row.option_mouse_over)) {
                        this.scrollEvent = false;
                    }
                },
                hoverEvent() {
                    if(['페이드','줌인','줌아웃','플립'].includes(this.row.option_mouse_over)) {
                        this.scrollEvent = true;
                    }
                },
                getHoverTextPositionStyle() {
                    let style = {};
                    style.position = 'absolute';
                    style.color = this.row.option_mouse_over_text_color;
                    style.background = this.row.option_mouse_over_text_background;
                    style.fontSize = `${this.row.option_mouse_over_font_size}px`;

                    const position = this.row.option_mouse_over_text_position;

                    if(position === 'top-left') {
                        style.top = '0';
                        style.left = '0';
                    } else if(position === 'top-center') {
                        style.top = '0';
                        style.left = '50%';
                        style.transform = 'translateX(-50%)';
                    } else if(position === 'top-right') {
                        style.top = '0';
                        style.right = '0';
                    } else if(position === 'middle-left') {
                        style.top = '50%';
                        style.left = '0';
                        style.transform = 'translateY(-50%)';
                    } else if(position === 'middle-center') {
                        style.top = '50%';
                        style.left = '50%';
                        style.transform = 'translate(-50%, -50%)';
                    } else if(position === 'middle-right') {
                        style.top = '50%';
                        style.right = '0';
                        style.transform = 'translateY(-50%)';
                    } else if(position === 'bottom-left') {
                        style.bottom = '0';
                        style.left = '0';
                    } else if(position === 'bottom-center') {
                        style.bottom = '0';
                        style.left = '50%';
                        style.transform = 'translateX(-50%)';
                    } else if(position === 'bottom-right') {
                        style.bottom = '0';
                        style.right = '0';
                    }

                    return style;
                },

                getHoverTextAnimationStyle() {
                    let style = {};

                    if(this.row.option_mouse_over_text_animation) {
                        const animation = this.row.option_mouse_over_text_animation;
                        const duration = 500;

                        if(animation === '페이드') {
                            style.animation = `hover-text-fade ${duration}ms ease both`;
                        } else if(animation === '슬라이드') {
                            style.animation = `hover-text-slide ${duration}ms ease both`;
                        } else if(animation === '플립') {
                            style.animation = `hover-text-flip ${duration}ms ease both`;
                        }
                    }

                    return style;
                },
                normalizeAlphaColor(color, defaultColor = '#00000030', legacyOpacity = null) {
                    color = String(color || '').trim();
                    defaultColor = String(defaultColor || '#00000030').trim();

                    if(/^#[0-9a-f]{6}$/i.test(color)) {
                        if(legacyOpacity !== null && legacyOpacity !== '' && legacyOpacity !== undefined) {
                            return color + this.opacityToHex(legacyOpacity);
                        }

                        return color + 'ff';
                    }

                    if(/^#[0-9a-f]{8}$/i.test(color)) {
                        if(color.toLowerCase() === '#ffffff00' && legacyOpacity !== null && legacyOpacity !== '' && legacyOpacity !== undefined) {
                            return color.slice(0, 7) + this.opacityToHex(legacyOpacity);
                        }

                        return color;
                    }

                    if(/^#[0-9a-f]{6}$/i.test(defaultColor)) return defaultColor + 'ff';
                    if(/^#[0-9a-f]{8}$/i.test(defaultColor)) return defaultColor;

                    return '#00000030';
                },
                opacityToHex(opacity) {
                    const percent = Math.max(0, Math.min(100, parseInt(opacity, 10) || 0));
                    return ('0' + Math.round(percent * 255 / 100).toString(16)).slice(-2);
                },
                hexAlphaToRgba(color) {
                    const normalizedColor = this.normalizeAlphaColor(color);
                    const r = parseInt(normalizedColor.slice(1, 3), 16);
                    const g = parseInt(normalizedColor.slice(3, 5), 16);
                    const b = parseInt(normalizedColor.slice(5, 7), 16);
                    const a = parseInt(normalizedColor.slice(7, 9), 16) / 255;

                    return `rgba(${r}, ${g}, ${b}, ${a})`;
                },
                getLegacyFilterOverlayColor() {
                    const opacity = this.row.filter_brightness !== undefined && this.row.filter_brightness !== ''
                        ? this.row.filter_brightness
                        : 19;
                    return '#000000' + this.opacityToHex(opacity);
                },
                getImageOverlayStyle() {
                    let style = {
                        position: 'absolute',
                        top: '0',
                        left: '0',
                        width: '100%',
                        height: '100%',
                        pointerEvents: 'none',
                        backgroundColor: this.hexAlphaToRgba(this.row.filter_overlay_color || this.getLegacyFilterOverlayColor())
                    };

                    if(this.row.circle_use) {
                        style.borderRadius = '50%';
                    } else if(this.row.border_radius_use) {
                        style.borderRadius = `${this.row.border_radius_px}px`;
                    }

                    return style;
                },
                getOverlayStyle() {
                    return {
                        backgroundColor: this.hexAlphaToRgba(this.normalizeAlphaColor(
                            this.row.option_mouse_over_background_color,
                            '#00000030',
                            this.row.option_mouse_over_overlay_opacity
                        ))
                    };
                },
                hoverStyle(style) {
                    if(!this.hover) return false;
                    if(this.row.option_mouse_over == '테두리') {
                        style.outline = `${this.row.option_mouse_over_border_width}px solid ${this.row.option_mouse_over_border_color}`;
                        style.outlineOffset = `-${this.row.option_mouse_over_border_width}px`;
                    }

                    if(this.row.option_mouse_over == '그림자') {
                        style.boxShadow = `0px 4px 8px rgba(0, 0, 0, ${this.row.option_mouse_over_box_shadow_opacity / 100})`;
                    }


                    return style;
                },
                initScrollObserver() {
                    let component = this;
                    let time = parseInt(this.row.option_scroll_delay) + parseInt(this.row.option_scroll_duration);
                    this.observer = new IntersectionObserver((entries) => {
                        entries.forEach(entry => {
                            if (entry.isIntersecting) {
                                this.scrollEvent = true;
                                setTimeout(() => {
                                    component.scrollEvent = false;
                                }, time);
                            } else if (!entry.isIntersecting) {
                                this.scrollEvent = false;
                            }
                        });
                    }, {
                        threshold: 0.1  // 이미지의 10%가 보이면 트리거
                    });

                    if(this.$refs.imageElement) {
                        this.observer.observe(this.$refs.imageElement);
                    }
                },
                async getClick() {
                    if(this.row.option_mouse_click == '링크 이동') {
                        this.lib.href(this.row.option_mouse_click_url,this.row.option_mouse_click_target);
                    }

                    if(this.row.option_mouse_click == '크게 보기') {
                        this.viewImage = this.row.file_src;
                    }

                    return false;
                },
                getTextStyle() {
                    let style = {};

                    // style.padding = "10px";
                    style.fontSize = "16px";
                    style.background = this.row.option_text_background;
                    // style.width = this.row.width + "px";
                    style.maxWidth = "100%";

                    const margins = this.getTextMargins();
                    const marginTop = margins.top;
                    const marginRight = margins.right;
                    const marginBottom = margins.bottom;
                    const marginLeft = margins.left;

                    style.marginTop = marginTop + "px";
                    style.marginRight = marginRight + "px";
                    style.marginBottom = marginBottom + "px";
                    style.marginLeft = marginLeft + "px";

                    if(this.row.option_text == "이미지 겹침") {
                        style.position = 'absolute';
                        style.left = `${marginLeft}px`;
                        style.right = `${marginRight}px`;
                        style.width = 'auto';
                        style.maxWidth = 'none';
                        style.boxSizing = 'border-box';
                        style.margin = '0';

                        // position에 따른 스타일 적용
                        const position = this.row.option_text_position || 'middle-center';
                        const verticalPosition = position.split('-')[0];
                        const horizontalPosition = position.split('-')[1];

                        if(horizontalPosition === 'center') {
                            style.textAlign = 'center';
                        } else if(horizontalPosition === 'right') {
                            style.textAlign = 'right';
                        } else {
                            style.textAlign = 'left';
                        }

                        if(verticalPosition === 'top') {
                            style.top = `${marginTop}px`;
                        } else if(verticalPosition === 'middle') {
                            const verticalOffset = marginTop - marginBottom;
                            style.top = '50%';
                            style.transform = verticalOffset
                                ? `translateY(calc(-50% ${verticalOffset > 0 ? '+' : '-'} ${Math.abs(verticalOffset)}px))`
                                : 'translateY(-50%)';
                        } else if(verticalPosition === 'bottom') {
                            style.bottom = `${marginBottom}px`;
                        }
                    }

                    return style;
                },
                getTextMargins() {
                    const prefix = this.isMobileView() ? 'option_text_mobile_margin_' : 'option_text_margin_';

                    return {
                        top: parseInt(this.row[prefix + 'top'], 10) || 0,
                        right: parseInt(this.row[prefix + 'right'], 10) || 0,
                        bottom: parseInt(this.row[prefix + 'bottom'], 10) || 0,
                        left: parseInt(this.row[prefix + 'left'], 10) || 0,
                    };
                },
                getPaddingStyle() {
                    let style = {};
                    style.boxSizing = 'border-box';
                    style.maxWidth = '100%';

                    if (this.row.padding_top) {
                        style.paddingTop = `${this.row.padding_top}px`;
                    }
                    if (this.row.padding_right) {
                        style.paddingRight = `${this.row.padding_right}px`;
                    }
                    if (this.row.padding_bottom) {
                        style.paddingBottom = `${this.row.padding_bottom}px`;
                    }
                    if (this.row.padding_left) {
                        style.paddingLeft = `${this.row.padding_left}px`;
                    }

                    return style;
                },
                getOuterStyle() {
                    let style = this.getPaddingStyle();
                    style.display = 'inline-block';
                    style.maxWidth = '100%';

                    if(this.row.option_mouse_click != "동작없음") {
                        style.cursor = 'pointer';
                    }

                    if(this.row.image_attribute == 'full') {
                        style.display = "block";
                        style.width = "100%";
                    }

                    if(this.row.option_scroll !== '효과없음' && this.scrollEvent) {
                        const effect = this.row.option_scroll;
                        const direction = this.row.option_scroll_direction || 'up';
                        const duration = parseInt(this.row.option_scroll_duration) || 300;  // 숫자로 변환
                        const delay = parseInt(this.row.option_scroll_delay) || 0;          // 숫자로 변환

                        const animationName = effect === '페이드' ? 'fade' :
                            effect === '줌인' ? 'zoom-in' :
                                effect === '줌아웃' ? 'zoom-out' :
                                    effect === '플립' ? 'flip' : 'fade';

                        style.animation = `${animationName}-${direction} ${duration}ms ease ${delay}ms both`;
                    }

                    if(['페이드','줌인','줌아웃','플립'].includes(this.row.option_mouse_over) && this.scrollEvent && this.parent_div_hover) {
                        const effect = this.row.option_mouse_over;
                        const direction = this.row.option_mouse_over_direction || 'up';
                        const duration = parseInt(this.row.option_mouse_over_duration) || 300;  // 숫자로 변환
                        const delay = parseInt(this.row.option_mouse_over_delay) || 0;          // 숫자로 변환

                        const animationName = effect === '페이드' ? 'fade' :
                            effect === '줌인' ? 'zoom-in' :
                                effect === '줌아웃' ? 'zoom-out' :
                                    effect === '플립' ? 'flip' : 'fade';

                        style.animation = `${animationName}-${direction} ${duration}ms ease ${delay}ms both`;
                    }

                    return style;
                },
                getDivStyle() {
                    let style = {};
                    style.position = 'relative';
                    style.display = 'inline-block';
                    style.overflow = 'hidden';
                    style.maxWidth = '100%';
                    style.boxSizing = 'border-box';

                    // 그림자
                    if(this.row.box_shadow_use) {
                        style.boxShadow = `0px 4px 8px rgba(0, 0, 0, ${this.row.box_shadow_opacity / 100})`;
                    }

                    // 모서리 둥글게
                    if (this.row.border_radius_use) {
                        style.borderRadius = `${this.row.border_radius_px}px`;
                    }

                    // 원형 이미지
                    if (this.row.circle_use) {
                        style.borderRadius = '50%';
                    }

                    // 테두리
                    if (this.row.border_use) {
                        style.border = `${this.row.border_width}px solid ${this.row.border_color}`;
                    }

                    if(this.row.image_attribute == 'full') {
                        style.display = "block";
                        style.width = "100%";
                    }

                    this.hoverStyle(style);

                    return style;
                },
                getStyle() {
                    let style = {};
                    const isMobileView = this.isMobileView();
                    const height = isMobileView ? (this.row.mobile_height || this.row.height) : this.row.height;
                    const width = isMobileView ? (this.row.mobile_width || this.row.width) : this.row.width;

                    // 모서리 둥글게
                    if (this.row.border_radius_use) {
                        style.borderRadius = `${this.row.border_radius_px}px`;
                    }

                    // 원형 이미지
                    if (this.row.circle_use) {
                        style.borderRadius = '50%';
                        style.aspectRatio = '1 / 1';  // 정사각형 비율
                        style.objectFit = 'cover';     // 이미지 가운데 중심으로 채움
                    }

                    //이미지 사이즈
                    if(this.row.image_attribute == 'full') {
                        style.height = `${height}px`;
                        style.width = '100%';
                        style.objectFit = 'cover';
                    }
                    if(this.row.image_attribute == 'original') {
                        style.height = `${height}px`;
                        if (!this.row.circle_use) style.objectFit = 'contain';

                    }
                    if(this.row.image_attribute == 'self') {
                        style.height = `${height}px`;
                        if (!this.row.circle_use) style.width = `${width}px`;
                        style.objectFit = 'cover';
                    }

                    if(this.row.option_mouse_over == '이미지 전환' && this.hover) {

                        if(this.row.image_attribute == 'original') style.width = `${this.renderedWidth}px`;
                        if(this.row.image_attribute == 'full') style.width = "100%";
                        if(this.row.image_attribute == 'self') style.width = `${width}px`;
                        style.objectFit = 'cover';
                        style.height = `${height}px`;
                        let animation = this.row.option_mouse_over_image_animation;
                        let duration = 500;
                        if(animation === '페이드') {
                            style.animation = `hover-text-fade ${duration}ms ease both`;
                        } else if(animation === '슬라이드') {
                            style.animation = `hover-text-slide ${duration}ms ease both`;
                        } else if(animation === '플립') {
                            style.animation = `hover-text-flip ${duration}ms ease both`;
                        }
                    }

                    return style;
                },
            },
            computed: {
                appState() {
                    return window.JAYDREAM_VUE_GLOBAL[this.$app_name];
                }
            },
            watch: {
                appState: {
                    async handler(newVal) {
                        if(newVal.reload) {
                            let row = await this.table.get();
                            if(row.length > 0) this.row = row[0];
                            this.appState.reload = false;
                        }
                    },
                    deep: true
                }
            }
        }});
</script>

<style>
    .jd-image {
        max-width: 100%;
        margin: 0 auto;
    }


    /* 키프레임들 */
    @keyframes fade-up { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
    @keyframes fade-down { from { opacity: 0; transform: translateY(-30px); } to { opacity: 1; transform: translateY(0); } }
    @keyframes fade-left { from { opacity: 0; transform: translateX(30px); } to { opacity: 1; transform: translateX(0); } }
    @keyframes fade-right { from { opacity: 0; transform: translateX(-30px); } to { opacity: 1; transform: translateX(0); } }
    @keyframes fade-center { from { opacity: 0; } to { opacity: 1; } }

    @keyframes zoom-in-up { from { opacity: 0; transform: scale(0.8) translateY(30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-in-down { from { opacity: 0; transform: scale(0.8) translateY(-30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-in-left { from { opacity: 0; transform: scale(0.8) translateX(30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-in-right { from { opacity: 0; transform: scale(0.8) translateX(-30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-in-center { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }

    @keyframes zoom-out-up { from { opacity: 0; transform: scale(1.2) translateY(30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-out-down { from { opacity: 0; transform: scale(1.2) translateY(-30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-out-left { from { opacity: 0; transform: scale(1.2) translateX(30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-out-right { from { opacity: 0; transform: scale(1.2) translateX(-30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-out-center { from { opacity: 0; transform: scale(1.2); } to { opacity: 1; transform: scale(1); } }

    @keyframes flip-up { from { opacity: 0; transform: perspective(400px) rotateX(90deg); } to { opacity: 1; transform: perspective(400px) rotateX(0); } }
    @keyframes flip-down { from { opacity: 0; transform: perspective(400px) rotateX(-90deg); } to { opacity: 1; transform: perspective(400px) rotateX(0); } }
    @keyframes flip-left { from { opacity: 0; transform: perspective(400px) rotateY(90deg); } to { opacity: 1; transform: perspective(400px) rotateY(0); } }
    @keyframes flip-right { from { opacity: 0; transform: perspective(400px) rotateY(-90deg); } to { opacity: 1; transform: perspective(400px) rotateY(0); } }

    /* 호버 텍스트 애니메이션 */
    @keyframes hover-text-fade {
        from { opacity: 0; }
        to { opacity: 1; }
    }

    @keyframes hover-text-slide {
        from {
            opacity: 0;
            transform: translateY(20px);
        }
        to {
            opacity: 1;
            transform: translateY(0);
        }
    }

    @keyframes hover-text-flip {
        from {
            opacity: 0;
            transform: perspective(400px) rotateX(90deg);
        }
        to {
            opacity: 1;
            transform: perspective(400px) rotateX(0);
        }
    }

    /* 이미지 오버레이 */
    .widget-image-overlay {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.9);
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 910000;
        cursor: pointer;
    }

    .widget-image-overlay img {
        max-width: 90%;
        max-height: 90%;
        object-fit: contain;
        cursor: default;
    }

    .widget-overlay-close {
        position: absolute;
        top: 20px;
        right: 30px;
        font-size: 40px;
        color: white;
        background: none;
        border: none;
        cursor: pointer;
        padding: 0;
        width: 40px;
        height: 40px;
        line-height: 1;
    }

    .widget-overlay-close:hover {
        color: #ccc;
    }
</style>
<script type="text/x-template" id="design-widget-map-google-template">
    <div v-if="load">
        <div v-html="modelValue.google_iframe"></div>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-map-google",object : {
            template: "#design-widget-map-google-template",
            props: {
                primary : {type : String, default : ""},
                modelValue : {type : Object, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-map-google",
                    component_idx: "",

                    table : null,
                    row: {},
                    rows : [],
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                // this.table = await this.api.table("exam");
                // await this.table.get(this.rows,{paging : 10})

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {

            },
            computed: {

            },
            watch: {

            }
        }});
</script><script type="text/x-template" id="design-widget-map-kakao-template">
    <div v-if="load">
        <iframe
            class="design-widget-map-kakao-frame"
            :src="iframeSrc"
            :style="getStyle()"
            frameborder="0"
            allowfullscreen
            loading="lazy">
        </iframe>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-map-kakao",object : {
            template: "#design-widget-map-kakao-template",
            props: {
                primary : {type : String, default : ""},
                modelValue : {type : Object, default : ""},
                widget_code : {type : String, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-map-kakao",
                    component_idx: "",
                    kakaoMapEmbedOrigin : "https:\/\/isweb.co.kr",
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.load = true;
            },
            updated() {

            },
            methods: {
                getStyle() {
                    let style = {
                        width: "100%",
                        border: "0",
                    };

                    if(this.lib.isMobile()) {
                        style.height = (this.row.mobile_height || this.row.pc_height || 320) + "px";
                    } else {
                        style.height = (this.row.pc_height || 450) + "px";
                    }

                    return style;
                }
            },
            computed: {
                row() {
                    return this.modelValue || {};
                },
                iframeSrc() {
                    const code = this.row.widget_code || this.widget_code || "";
                    const origin = this.kakaoMapEmbedOrigin || window.location.origin;
                    return origin.replace(/\/$/, "") + "/JayDream/kakaoMapEmbed.php?widget_code=" + encodeURIComponent(code);
                }
            },
            watch: {

            }
        }});
</script>

<style>
    .design-widget-map-kakao-frame {
        display: block;
        width: 100%;
        border: 0;
        background-color: #fff;
    }
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/theme/material-darker.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/htmlmixed/htmlmixed.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/xml/xml.min.js"></script>
<script type="text/x-template" id="design-widget-map-modal-google-template">
    <div v-if="load">
        <div class="design-widget-map-modal-google-guide-container">
            <div class="design-widget-map-modal-google-guide-header">
                <h4 class="design-widget-map-modal-google-guide-title">
                    <span class="design-widget-map-modal-google-guide-icon">ⓘ</span>
                    구글 지도 삽입 방법
                </h4>
            </div>

            <div class="design-widget-map-modal-google-steps">
                <div class="design-widget-map-modal-google-step">
                    <div class="design-widget-map-modal-google-step-icon-box">
                        <div class="design-widget-map-modal-google-step-icon">
                            <img src="/file/icon/map/step-1.svg">
                        </div>
                    </div>
                    <div class="design-widget-map-modal-google-step-label">
                        구글 지도 접속 <br>
                        <a href="https://www.google.com/maps" class="btn btn-link" target="_blank">&lt;바로가기&gt;</a>
                    </div>
                </div>

                <div class="design-widget-map-modal-google-step-arrow">›</div>

                <div class="design-widget-map-modal-google-step">
                    <div class="design-widget-map-modal-google-step-icon-box">
                        <div class="design-widget-map-modal-google-step-icon">
                            <img src="/file/icon/map/step-2.svg">
                        </div>
                    </div>
                    <div class="design-widget-map-modal-google-step-label">삽입할 주소 검색</div>
                </div>

                <div class="design-widget-map-modal-google-step-arrow">›</div>

                <div class="design-widget-map-modal-google-step">
                    <div class="design-widget-map-modal-google-step-icon-box">
                        <div class="design-widget-map-modal-google-step-icon">
                            <img src="/file/icon/map/step-3.svg">
                        </div>
                    </div>
                    <div class="design-widget-map-modal-google-step-label">
                        주소창 내<br>
                        [공유]버튼 클릭
                    </div>
                </div>

                <div class="design-widget-map-modal-google-step-arrow">›</div>

                <div class="design-widget-map-modal-google-step">
                    <div class="design-widget-map-modal-google-step-icon-box">
                        <div class="design-widget-map-modal-google-step-icon">
                            <img src="/file/icon/map/step-4.svg">
                        </div>
                    </div>
                    <div class="design-widget-map-modal-google-step-label">
                        우측의 [지도 퍼가기]<br>
                        버튼 클릭
                    </div>
                </div>

                <div class="design-widget-map-modal-google-step-arrow">›</div>

                <div class="design-widget-map-modal-google-step">
                    <div class="design-widget-map-modal-google-step-icon-box">
                        <div class="design-widget-map-modal-google-step-icon">
                            <img src="/file/icon/map/step-5.svg">
                        </div>
                    </div>
                    <div class="design-widget-map-modal-google-step-label">
                        [HTML 복사]<br>
                        버튼 클릭
                    </div>
                </div>

                <div class="design-widget-map-modal-google-step-arrow">›</div>

                <div class="design-widget-map-modal-google-step">
                    <div class="design-widget-map-modal-google-step-icon-box">
                        <div class="design-widget-map-modal-google-step-icon">
                            <img src="/file/icon/map/step-6.svg">
                        </div>
                    </div>
                    <div class="design-widget-map-modal-google-step-label">
                        복사한 링크<br>
                        붙여넣기
                    </div>
                </div>
            </div>

            <div class="design-widget-map-modal-google-note">
                ※ &lt;iframe src= 로 시작하는 링크를 붙여넣기 해주셔야 지도 불러오기가 정상적으로 진행됩니다.(일반 공유 링크는 삽입 불가)
            </div>

            <div class="design-widget-map-modal-google-steps" style="width: 50%">
                <div class="design-widget-map-modal-google-step">
                    <img src="/file/icon/map/step-7.png">
                    <div class="design-widget-map-modal-google-step-label">기본값</div>
                </div>

                <div class="design-widget-map-modal-google-step-arrow">›</div>

                <div class="design-widget-map-modal-google-step">
                    <img src="/file/icon/map/step-8.png">
                    <div class="design-widget-map-modal-google-step-label">위젯 영역 채우기</div>
                </div>
            </div>

            <div class="design-widget-map-modal-google-note">
                ※ 지도의 크기를 조정하시려면, 퍼온 링크 내에서 width(너비), height(높이) 픽셀값을 각각 수정해 주세요.(*채우기: width=”100%” 입력)
            </div>
        </div>

        <div class="design-widget-map-modal-google-code-section">
            <label class="design-widget-map-modal-google-code-label">지도 링크 입력</label>
            <textarea
                    class="form-control design-widget-map-modal-google-code-input"
                    rows="6"></textarea>
        </div>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-map-modal-google",object : {
            template: "#design-widget-map-modal-google-template",
            props: {
                primary : {type : String, default : ""},
                modelValue : {type : Object, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-map-modal-google",
                    component_idx: "",

                    table : null,
                    rows : [],
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                // this.table = await this.api.table("exam");
                // await this.table.get(this.rows,{paging : 10})

                this.load = true;
                let component = this;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                    var editor = CodeMirror.fromTextArea(document.querySelector('.design-widget-map-modal-google-code-input'), {
                        mode: 'htmlmixed',
                        theme: 'material-darker',
                        lineNumbers: true,
                        lineWrapping: true
                    });

                    if (component.row.google_iframe) {
                        editor.setValue(component.row.google_iframe);
                    }

                    editor.on('change', function() {
                        component.row.google_iframe = editor.getValue();
                    });
                });
            },
            updated() {

            },
            methods: {

            },
            computed: {
                row: {
                    get() {
                        return this.modelValue;
                    },
                    set(val) {
                        this.$emit('update:modelValue', val);
                    }
                }
            },
            watch: {

            }
        }});
</script>

<style>
    .design-widget-map-modal-google-guide-container {
        background-color: #f8f8f8;
        border: 1px solid #e0e0e0;
        border-radius: 8px;
        padding: 20px;
        margin: 20px 0;
    }

    .design-widget-map-modal-google-guide-header {
        display: flex;
        align-items: center;
        gap: 10px;
        margin-bottom: 20px;
    }

    .design-widget-map-modal-google-guide-title {
        margin: 0;
        font-size: 16px;
        font-weight: 600;
        display: inline-flex;
        align-items: center;
        gap: 5px;
    }

    .design-widget-map-modal-google-guide-icon {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 20px;
        height: 20px;
        background-color: #333;
        color: white;
        border-radius: 50%;
        font-size: 12px;
    }

    .design-widget-map-modal-google-steps {
        display: flex;
        align-items: center;
        justify-content: space-between;
        flex-wrap: nowrap;
        overflow-x: auto;
        padding: 10px 0;
    }

    .design-widget-map-modal-google-step {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 10px;
        min-width: 100px;
    }

    .design-widget-map-modal-google-step-icon-box {
        background-color: white;
        border: 1px solid #e0e0e0;
        border-radius: 8px;
        padding: 15px;
        width: 80px;
        height: 80px;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .design-widget-map-modal-google-step-icon {
        width: 50px;
        height: 50px;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .design-widget-map-modal-google-step-icon img {
        max-width: 100%;
        max-height: 100%;
        object-fit: contain;
    }

    .design-widget-map-modal-google-step-label {
        font-size: 13px;
        text-align: center;
        line-height: 1.4;
        color: #333;
    }

    .design-widget-map-modal-google-step-arrow {
        font-size: 24px;
        color: #999;
        margin: 0 5px;
        flex-shrink: 0;
    }

    .design-widget-map-modal-google-note {
        margin-top: 15px;
        font-size: 12px;
        color: #666;
        line-height: 1.5;
    }

    /* 반응형 */
    @media (max-width: 768px) {
        .design-widget-map-modal-google-steps {
            overflow-x: auto;
            padding-bottom: 15px;
        }

        .design-widget-map-modal-google-step {
            min-width: 90px;
        }

        .design-widget-map-modal-google-step-icon-box {
            width: 70px;
            height: 70px;
            padding: 10px;
        }
    }
</style><script type="text/x-template" id="design-widget-map-modal-kakao-template">
    <div v-if="load">
        <div class="design-widget-map-form-container">
            <form>
                <div class="design-widget-map-form-row">
                    <div class="row">
                        <div class="col-sm-2 design-widget-map-label-col">
                            <label>주소 검색</label>
                        </div>
                        <div class="col-sm-10">
                            <button type="button" class="btn design-widget-map-btn-search" @click="modal.status = true">주소 검색</button>
                        </div>
                    </div>
                </div>

                <div class="design-widget-map-form-row">
                    <div class="row">
                        <div class="col-sm-2 design-widget-map-label-col">
                            <label>지번 주소</label>
                        </div>
                        <div class="col-sm-5">
                            <input type="text" class="form-control" readonly v-model="row.jibun">
                        </div>
                        <div class="col-sm-5">
                            <input type="text" class="form-control" v-model="row.jibun2" placeholder="상세 주소 입력">
                        </div>
                    </div>
                </div>

                <div class="design-widget-map-form-row">
                    <div class="row">
                        <div class="col-sm-2 design-widget-map-label-col">
                            <label>도로명 주소</label>
                        </div>
                        <div class="col-sm-5">
                            <input type="text" class="form-control" readonly v-model="row.address">
                        </div>
                        <div class="col-sm-5">
                            <input type="text" class="form-control" v-model="row.address2" placeholder="상세 주소 입력">
                        </div>
                    </div>
                </div>

                <div class="design-widget-map-form-row">
                    <div class="row">
                        <div class="col-sm-2 design-widget-map-label-col">
                            <label>지도 높이</label>
                        </div>
                        <div class="col-sm-10">
                            <div class="design-widget-map-height-group">
                                <input type="text" class="form-control design-widget-map-input-small" v-model="row.pc_height" v-number>
                                <span class="design-widget-map-px-label">px</span>

                                <label class="design-widget-map-label-inline">모바일 지도 높이</label>
                                <input type="text" class="form-control design-widget-map-input-small" v-model="row.mobile_height" v-number>
                                <span class="design-widget-map-px-label">px</span>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="design-widget-map-form-row">
                    <div class="row">
                        <div class="col-sm-2 design-widget-map-label-col">
                            <label>상호명(선택)</label>
                        </div>
                        <div class="col-sm-10">
                            <input type="text" class="form-control" v-model="row.title" placeholder="지도에 표시할 상호명을 입력해주세요">
                        </div>
                    </div>
                </div>

                <div class="design-widget-map-form-row">
                    <div class="row">
                        <div class="col-sm-2 design-widget-map-label-col">
                            <label>전화번호(선택)</label>
                        </div>
                        <div class="col-sm-10">
                            <div class="design-widget-map-phone-group">
                                <input type="text" class="form-control" v-model="row.phone1" placeholder="02">
                                <span class="design-widget-map-phone-separator">-</span>
                                <input type="text" class="form-control" v-model="row.phone2" placeholder="1234">
                                <span class="design-widget-map-phone-separator">-</span>
                                <input type="text" class="form-control" v-model="row.phone3" placeholder="5678">
                            </div>
                        </div>
                    </div>
                </div>

                <div class="design-widget-map-form-row">
                    <div class="row">
                        <div class="col-sm-2 design-widget-map-label-col">
                            <label>미리보기</label>
                        </div>
                        <div class="col-sm-10">
                            <div class="design-widget-map-preview-actions">
                                <button type="button" class="btn btn-default" @click="updatePreview">미리보기 업데이트</button>
                            </div>
                            <iframe
                                class="design-widget-map-preview-frame"
                                :key="iframeSrc"
                                :src="iframeSrc"
                                :style="previewFrameStyle"
                                frameborder="0"
                                allowfullscreen
                                loading="lazy">
                            </iframe>
                        </div>
                    </div>
                </div>
            </form>
        </div>

        <external-bs-modal v-model="modal">
            <template v-slot:header>
                <h5 class="modal-title">주소 검색</h5>
            </template>

            <template v-slot:default>
                <external-daum-postcode v-model="row" @close="modal.status = false;"></external-daum-postcode>
            </template>

            <template v-slot:footer>
                <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
            </template>
        </external-bs-modal>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-map-modal-kakao",object : {
            template: "#design-widget-map-modal-kakao-template",
            props: {
                primary : {type : String, default : ""},
                modelValue : {type: Object, default: {}},
                widget_code : {type : String, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-map-modal-kakao",
                    component_idx: "",
                    modal : this.lib.createModalObject(),
                    kakaoMapEmbedOrigin : "https:\/\/isweb.co.kr",
                    handleIframeMessageListener : null,
                    previewRow : {
                        widget_code : "",
                        address : "",
                        address2 : "",
                        jibun : "",
                        jibun2 : "",
                        title : "",
                        phone1 : "",
                        phone2 : "",
                        phone3 : "",
                        z : "",
                        pc_height : 360,
                        mobile_height : 320,
                    },
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
                this.handleIframeMessageListener = (event) => this.handleIframeMessage(event);
            },
            async mounted() {
                window.addEventListener("message", this.handleIframeMessageListener);
                this.updatePreview();
                this.load = true;
            },
            beforeUnmount() {
                window.removeEventListener("message", this.handleIframeMessageListener);
            },
            updated() {

            },
            methods: {
                updatePreview() {
                    this.previewRow = {
                        widget_code : this.widget_code || this.row.widget_code || "",
                        address : this.row.address || "",
                        address2 : this.row.address2 || "",
                        jibun : this.row.jibun || "",
                        jibun2 : this.row.jibun2 || "",
                        title : this.row.title || "",
                        phone1 : this.row.phone1 || "",
                        phone2 : this.row.phone2 || "",
                        phone3 : this.row.phone3 || "",
                        z : this.row.z || "",
                        pc_height : this.row.pc_height || 360,
                        mobile_height : this.row.mobile_height || this.row.pc_height || 320,
                    };
                },
                updateAddressPreview() {
                    this.previewRow = Object.assign({}, this.previewRow, {
                        widget_code : this.widget_code || this.row.widget_code || "",
                        address : this.row.address || "",
                        jibun : this.row.jibun || "",
                        z : 3,
                    });
                    this.row.z = 3;
                },
                handleIframeMessage(event) {
                    if(event.origin !== this.iframeOrigin) {
                        return;
                    }

                    const data = event.data || {};
                    if(data.type !== "jd-kakao-map-level") {
                        return;
                    }

                    const widgetCode = this.widget_code || this.row.widget_code || "";
                    if(data.widget_code && widgetCode && data.widget_code !== widgetCode) {
                        return;
                    }

                    const level = Number(data.z);
                    if(!level) {
                        return;
                    }

                    this.row.z = level;
                }
            },
            computed: {
                row: {
                    get() {
                        return this.modelValue;
                    },
                    set(val) {
                        this.$emit('update:modelValue', val);
                    }
                },
                iframeSrc() {
                    const params = new URLSearchParams();
                    params.set("preview", "1");
                    params.set("widget_code", this.previewRow.widget_code || "");

                    [
                        "address",
                        "address2",
                        "jibun",
                        "jibun2",
                        "title",
                        "phone1",
                        "phone2",
                        "phone3",
                        "z",
                    ].forEach((field) => {
                        params.set(field, this.previewRow[field] != null ? this.previewRow[field] : "");
                    });

                    const origin = this.kakaoMapEmbedOrigin || window.location.origin;
                    return origin.replace(/\/$/, "") + "/JayDream/kakaoMapEmbed.php?" + params.toString();
                },
                iframeOrigin() {
                    try {
                        return new URL(this.iframeSrc, window.location.href).origin;
                    } catch (e) {
                        return window.location.origin;
                    }
                },
                previewFrameStyle() {
                    let height = this.previewRow.pc_height || 360;

                    if(this.lib.isMobile()) {
                        height = this.previewRow.mobile_height || this.previewRow.pc_height || 320;
                    }

                    return {
                        width : "100%",
                        height : height + "px",
                        border : "1px solid #e5e5e5",
                        backgroundColor : "#f8f8f8",
                    };
                },
                addressSyncKey() {
                    return [
                        this.row.widget_code || this.widget_code || "",
                        this.row.address || "",
                        this.row.jibun || "",
                    ].join("||");
                }
            },
            watch: {
                addressSyncKey() {
                    this.updateAddressPreview();
                }
            }
        }});
</script>

<style>
    .design-widget-map-preview-actions {
        margin-bottom: 10px;
    }
</style>
<!-- 해당 지도 스크립트는 제이쿼리 충돌을 일으키므로 최상단에 올려야함 -->
<script type="text/x-template" id="design-widget-map-modal-template">
    <div v-if="load">
        <div class="design-widget-map-tab-container">
            <button type="button"
                    class="design-widget-map-tab-btn"
                    :class="{'active': row.map_type === 'kakao'}"
                    @click="row.map_type = 'kakao'">
                카카오 지도
            </button>
            <button type="button"
                    class="design-widget-map-tab-btn"
                    :class="{'active': row.map_type === 'google'}"
                    @click="row.map_type = 'google'">
                구글 지도
            </button>
        </div>

        <template v-if="row.map_type == 'kakao'">
            <design-widget-map-modal-kakao v-model="row" :widget_code></design-widget-map-modal-kakao>
        </template>

        <template v-if="row.map_type == 'google'">
            <design-widget-map-modal-google v-model="row" :widget_code></design-widget-map-modal-google>
        </template>

        <div class="modal-footer" style="position:sticky;right:0;bottom:0;">
            <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
            <button type="button" class="btn btn-orange save" @click="dataPost()">저장</button>
        </div>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-map-modal",object : {
            template: "#design-widget-map-modal-template",
            props: {
                primary : {type : String, default : ""},
                widget_code : {type : String, default : "1"},
                site_code : {type : String, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-map-modal",
                    component_idx: "",

                    table : null,
                    row: {
                        widget_code : this.widget_code,
                        map_type : "kakao",
                        x : 14129709.590359,
                        y : 4512313.7639686,
                        z : 19,

                        jibun : "",
                        jibun2 : "",
                        address : "",
                        address2 : "",

                        pc_height : 450,
                        mobile_height : 320,

                        title : "",
                        phone1 : "",
                        phone2 : "",
                        phone3 : "",

                        google_iframe : "",
                    },
                    rows : [],

                    vmap : null,
                    markerLayer : null,
                    zoom : null,
                    marker : {},
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_map");
                this.table.where('widget_code',this.widget_code);
                let row = await this.table.get();
                if(row.length > 0) this.row = row[0];
                if(this.row.map_type == 'vworld') this.row.map_type = 'kakao';
                // await this.table.get(this.rows,{paging : 10})
                this.load = true;
                let component = this;

                this.$nextTick(async () => {

                });
            },
            updated() {

            },
            methods: {
                async dataPost() {
                    if(this.row.map_type == 'google') {
                        const code = this.row.google_iframe.trim();

                        // 기본 형식 검증
                        const iframeRegex = /<iframe[^>]+src=["']https:\/\/www\.google\.com\/maps\/embed\?pb=[^"']+["'][^>]*><\/iframe>/i;

                        if(!code) {
                            await this.lib.alert("코드를 입력해주세요.");
                            return false;
                        }

                        if (!iframeRegex.test(code)) {
                            await this.lib.alert("구글 맵 형식의 코드가 아닙니다.");
                            return false;
                        }
                    }

                    if(this.row.map_type == 'kakao') {
                        if(!this.row.address && !this.row.jibun) {
                            await this.lib.alert("주소 검색을 해주세요.");
                            return false;
                        }
                    }
                    window.design.loadingOn()

                    await this.table.post(this.row,{return : true});

                    window.design.loadingOff()

                    window.JAYDREAM_VUE_GLOBAL[this.widget_code].reload = true;

                    $(this.$el).closest('.modal').modal('hide');
                },

            },
            computed: {

            },
            watch: {

            }
        }});
</script>

<style>

    .design-widget-map-height-group {
        display: flex;
        align-items: center;
        gap: 8px;
    }

    .design-widget-map-input-small {
        width: 80px;
        flex-shrink: 0;
    }

    .design-widget-map-px-label {
        color: #999;
        flex-shrink: 0;
    }

    .design-widget-map-label-inline {
        margin: 0;
        font-weight: normal;
        color: #333;
        flex-shrink: 0;
    }
    .design-widget-map-form-container {
        background-color: white;
        padding: 30px;
        border-radius: 4px;
        margin: 0 auto;
    }

    .design-widget-map-btn-search {
        background-color: #ff6b35;
        color: white;
        border: none;
        padding: 8px 20px;
        border-radius: 4px;
    }
    .design-widget-map-btn-search:hover {
        background-color: #e55a2b;
        color: white;
    }
    .design-widget-map-btn-default-tab {
        background-color: white;
        border: 1px solid #ddd;
        color: #666;
        padding: 8px 20px;
        margin-right: 5px;
        border-radius: 4px;
    }
    .design-widget-map-form-row {
        margin-bottom: 15px;
    }
    .design-widget-map-label-col {
        padding-top: 7px;
        font-weight: normal;
        color: #333;
    }
    .design-widget-map-px-label {
        padding-top: 7px;
        padding-left: 5px;
        color: #999;
    }
    .design-widget-map-help-text {
        color: #999;
        font-size: 12px;
        margin-left: 10px;
    }
    .design-widget-map-phone-group {
        display: flex;
        align-items: center;
        gap: 10px;
    }
    .design-widget-map-phone-group select,
    .design-widget-map-phone-group input {
        flex: 1;
    }
    .design-widget-map-phone-separator {
        color: #999;
    }

    .design-widget-map-jibun-btn {
        display: inline-block;
        background-color: #999;
        color: white;
        font-size: 12px;
        padding: 2px 8px;
        border-radius: 2px;
        border: none;
        cursor: pointer;
        font-weight: normal;
        line-height: 1.4;
        width : 48px;
    }

    .design-widget-map-jibun-btn:hover {
        background-color: #888;
    }

    .design-widget-map-doromyeong-btn {
        display: inline-block;
        background-color: #ff9999;
        color: white;
        font-size: 12px;
        padding: 2px 8px;
        border-radius: 2px;
        border: none;
        cursor: pointer;
        font-weight: normal;
        line-height: 1.4;
        margin-top: 5px;
        width : 48px;
    }

    .design-widget-map-doromyeong-btn:hover {
        background-color: #ff8888;
    }

    .design-widget-map-input-small {
        max-width: 80px;
    }

    .design-widget-map-px-label {
        padding-top: 7px;
        padding-left: 5px;
        padding-right: 5px;
        color: #999;
        width: auto;
        flex: 0 0 auto;
    }

    .vw-popup {
        left : -160px;
    }

    .vw-popup::before {
        left : 160px;
    }

    .vw-popup::after {
        left : 160px;
    }

    #areaInfoWrap {
        width: 320px !important;
    }

    #areaInfoWrap hr {
        margin-top: 5px !important;
        margin-bottom: 5px !important;
    }

    #areaInfoWrap * {
        font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, system-ui, sans-serif !important;
    }

    #vw-zoombar .ol-zoom-up,
    #vw-zoombar .ol-zoom-left,
    #vw-zoombar .ol-zoom-center,
    #vw-zoombar .ol-zoom-bottom,
    #vw-zoombar .ol-zoom-right,
    #vw-zoombar .ol-zoom-init,
    .vw-logo,
    .vw-control,
    .vw-notice {
        display: none !important;
    }

    .design-widget-map-tab-container {
        display: flex;
        margin: 2%;
        margin-bottom: 0;
        gap: 0;
    }

    .design-widget-map-tab-btn {
        flex: 1;
        padding: 12px 20px;
        background-color: #f5f5f5;
        border: none;
        border-bottom: 2px solid #ddd;
        color: #666;
        font-size: 14px;
        cursor: pointer;
        transition: all 0.3s;
    }

    .design-widget-map-tab-btn.active {
        background-color: #ff6b35;
        color: white;
        border-bottom-color: #ff6b35;
    }

    .design-widget-map-tab-btn:hover:not(.active) {
        background-color: #e8e8e8;
    }
</style>
<script type="text/x-template" id="design-widget-map-template">
    <div v-if="load">
        <template v-if="row.idx">
            <template v-if="row.map_type == 'google'">
                <design-widget-map-google v-model="row"></design-widget-map-google>
            </template>

            <template v-if="row.map_type == 'kakao'">
                <design-widget-map-kakao v-model="row" :widget_code="widget_code"></design-widget-map-kakao>
            </template>
        </template>

        <template v-else>
            <div class="jd-imageupload-box jd-widget-placeholder">
                <img class="jd-widget-placeholder-icon" src="/file/icon/widget-add/map.svg" alt="">
                <p class="jd-imageupload-text">더블 클릭하여 지도를 추가하세요.</p>
            </div>
        </template>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-map",object : {
            template: "#design-widget-map-template",
            props: {
                primary : {type : String, default : ""},
                widget_code : {type : String, default : "1"},
                site_code : {type : String, default : "st68f18a193702e"},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-map",
                    component_idx: "",

                    table : null,
                    row: {},
                    rows : [],


                    vmap : null,
                    zoom : null,
                    markerLayer : null,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                let component = this;
                this.table = await this.api.table("jd_widget_map");
                this.table.where('widget_code',this.widget_code);
                await this.table.get(this.row);
                // await this.table.get(this.rows,{paging : 10})

                this.load = true;

                this.$nextTick(async () => {
                    if(!this.row.idx) $(`#${this.widget_code} .firewall`).addClass('widget_setting_btn');
                });

            },
            updated() {

            },
            methods: {

            },
            computed: {
                appState() {
                    return window.JAYDREAM_VUE_GLOBAL[this.$app_name];
                }
            },
            watch: {
                appState: {
                    async handler(newVal) {
                        if(newVal.reload) {
                            this.row.idx = '';
                            await this.table.get(this.row);
                            this.appState.reload = false;
                        }
                    },
                    deep: true
                }
            }
        }});
</script>

<script type="text/x-template" id="design-widget-multi-modal-each-template">
    <div v-if="load">
        <external-bs-modal v-model="modal">
            <template v-slot:header>
                <h5 class="modal-title" id="exampleModalLabel">이미지 개별 설정</h5>
            </template>

            <!-- body -->
            <template v-slot:default>
                <div>
                    <div class="row">
                        <div class="col-md-12 text-center">
                            <img :src="row.src" style="max-width: 100%; max-height: 500px;">
                            <div class="jd-each-image-replace-actions">
                                <button type="button" class="btn btn-orange btn-sm" @click="$refs.replaceFileInput.click()">직접 업로드</button>
                                <button type="button" class="btn btn-orange btn-sm" @click="openReplaceFreeModal()">무료 이미지</button>
                                <button type="button" class="btn btn-orange btn-sm" @click="openReplaceStorageModal()">이미지 보관함</button>
                            </div>
                            <input class="hidden" type="file" accept="image/*" ref="replaceFileInput" @change="changeReplaceFile($event)">
                        </div>
                    </div>

                    <hr>

                    <div>
                        <div style="display: inline-flex; align-items: center; gap: 8px; font-size: 16px; position: relative;">
                            <span style="white-space: nowrap;">링크입력</span>
                            <img src="/file/icon/물음표.svg"
                                 style="cursor: pointer; width: 16px; height: 16px;"
                                 class="design-widget-multi-modal-each-help-icon"
                                 @mouseenter="$event.target.nextElementSibling.classList.add('active')"
                                 @mouseleave="$event.target.nextElementSibling.classList.remove('active')">
                            <div class="design-widget-multi-modal-each-help-tooltip">
                                ‘마우스 클릭 동작’ 항목에서
                                ‘링크 이동’ 설정 시 이동할 링크입니다.
                            </div>
                        </div>


                        <div style="display: flex; gap : 5px; align-items: center;">
                            <select class="form-control" style="flex : 2;" v-model="row.option_mouse_click_url_type">
                                <option value="URL">URL</option>
                                <option value="페이지">페이지</option>
                            </select>

                            <template v-if="row.option_mouse_click_url_type == 'URL'">
                                <input type="text" class="form-control" style="flex : 8;" v-model="row.option_mouse_click_url" placeholder="https://">
                            </template>

                            <template v-if="row.option_mouse_click_url_type == '페이지'">
                                <template v-if="menus.length == 0">데이터를 불러들이는 중입니다.</template>

                                <template v-else>
                                    <select class="form-control" style="flex : 8;" v-model="row.option_mouse_click_url">
                                        <option value="">선택해주세요.</option>
                                        <template v-for="item in menus">
                                            <option :value="item.url">{{item.name}}</option>
                                        </template>
                                    </select>
                                </template>
                            </template>
                        </div>


                        <div style="display: flex; margin-top: 10px; gap : 10px; align-items: center;">
                            <input type="radio" value="_self" v-model="row.option_mouse_click_target"> 기존 창에서 이동
                            <input type="radio" value="_blank" v-model="row.option_mouse_click_target"> 새 창으로 열기
                        </div>
                    </div>

                    <hr>

                    <div>
                        <div style="display: inline-flex; align-items: center; gap: 8px; font-size: 16px; position: relative;">
                            <span style="white-space: nowrap;">alt 태그</span>
                            <img src="/file/icon/물음표.svg"
                                 style="cursor: pointer; width: 16px; height: 16px; margin-bottom: 3px;"
                                 class="design-widget-multi-modal-each-help-icon"
                                 @mouseenter="$event.target.nextElementSibling.classList.add('active')"
                                 @mouseleave="$event.target.nextElementSibling.classList.remove('active')">
                            <div class="design-widget-multi-modal-each-help-tooltip">
                                이미지 내용을 글로 설명하는 텍스트입니다.
                                접근성 향상과 SEO(검색 노출)에 도움이 됩니다.
                            </div>
                        </div>

                        <input type="text" v-model="row.alt_text" class="form-control form-control-sm" placeholder="(선택)alt 태그를 입력해주세요.">
                    </div>

                    <hr>

                    <div>
                        <div style="display: inline-flex; align-items: center; gap: 8px; font-size: 16px; position: relative;">
                            <span style="white-space: nowrap;">텍스트 문구</span>
                            <img src="/file/icon/물음표.svg"
                                 style="cursor: pointer; width: 16px; height: 16px;"
                                 class="design-widget-multi-modal-each-help-icon"
                                 @mouseenter="$event.target.nextElementSibling.classList.add('active')"
                                 @mouseleave="$event.target.nextElementSibling.classList.remove('active')">
                            <div class="design-widget-multi-modal-each-help-tooltip">
                                ‘텍스트 노출’ 항목에서 ‘이미지 하단’ 또는
                                ‘이미지 겹침’ 설정 시 노출되는 문구입니다.
                            </div>
                        </div>

                        <external-summernote :row field="each_text_content" height="100"></external-summernote>
                    </div>

                    <hr>

                    <div>
                        <div style="display: inline-flex; align-items: center; gap: 8px; font-size: 16px; position: relative;">
                            <span style="white-space: nowrap;">마우스오버 문구</span>
                            <img src="/file/icon/물음표.svg"
                                 style="cursor: pointer; width: 16px; height: 16px;"
                                 class="design-widget-multi-modal-each-help-icon"
                                 @mouseenter="$event.target.nextElementSibling.classList.add('active')"
                                 @mouseleave="$event.target.nextElementSibling.classList.remove('active')">
                            <div class="design-widget-multi-modal-each-help-tooltip">
                                ‘마우스 오버 애니메이션’ 항목에서
                                ‘텍스트’ 설정 시 노출되는 문구입니다.
                            </div>
                        </div>

                        <external-summernote :row field="each_over_content" height="100"></external-summernote>
                    </div>
                </div>
            </template>


            <template v-slot:footer>
                <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
                <button type="button" class="btn btn-orange save" @click="saveRow()">저장</button>
            </template>
        </external-bs-modal>

        <design-widget-multi-modal-free v-model="replaceFreeModal"></design-widget-multi-modal-free>
        <design-widget-multi-modal-storage v-model="replaceStorageModal"></design-widget-multi-modal-storage>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-multi-modal-each",object : {
            template: "#design-widget-multi-modal-each-template",
            props: {
                modelValue : {type: Object, default: {}},
                site_code : {type : String, default : "st68f18a193702e"},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-multi-modal-each",
                    component_idx: "",

                    temp : {
                        $table : "",
                    },
                    row : {},
                    rows : [],

                    menus : [],
                    replaceFreeModal : this.lib.createModalObject(),
                    replaceStorageModal : this.lib.createModalObject(),
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
            },
            async mounted() {
                this.api.table("menu").where("site_code",this.site_code).get(this.menus);

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {
                getParentRow() {
                    return this.modelValue.parent_row || this.modelValue.row || this.row;
                },
                openReplaceFreeModal() {
                    this.$openModal(this.replaceFreeModal, {
                        row: this.getParentRow(),
                        replace_target: this.row,
                    });
                },
                openReplaceStorageModal() {
                    this.$openModal(this.replaceStorageModal, {
                        row: this.getParentRow(),
                        replace_target: this.row,
                    });
                },
                async changeReplaceFile(event) {
                    await this.vue.changeFile(event, this.row, '_replace_upload_file', {
                        callback: () => {
                            if(this.row._replace_upload_file && this.row._replace_upload_file.src) {
                                this.row.src = this.row._replace_upload_file.src;
                            }
                        }
                    });

                    if(event && event.target) {
                        event.target.value = "";
                    }
                },
                saveRow() {
                    // 🔥 저장 버튼 클릭시 원본에 반영
                    Object.assign(this.modelValue.row, this.row);

                    // 모달 닫기
                    this.$emit('update:modelValue', { ...this.modelValue, status: false });
                }
            },
            computed: {
                modal: {
                    get() {
                        return this.modelValue;
                    },
                    set(value) {
                        this.$emit('update:modelValue', value);
                    }
                },
            },
            watch: {

                async "modelValue.status"(value, old_value) {
                    const $modal = $("#widget_setting_jd");

                    if(!value) {
                        if ($modal.data('bs.modal')) {
                            $modal.data('bs.modal').options.backdrop = true;
                            $modal.data('bs.modal').options.keyboard = true;
                        }
                        return false;
                    }

                    // 🔥 모달 열릴 때 복사
                    this.row = { ...this.modelValue.row };
                    if (!('option_mouse_click_url_type' in this.row)) this.row.option_mouse_click_url_type = "URL";
                    if (!('option_mouse_click_url' in this.row)) this.row.option_mouse_click_url = "";
                    if (!('option_mouse_click_target' in this.row)) this.row.option_mouse_click_target = "_self";
                    if (!('each_text_content' in this.row)) this.row.each_text_content = "";
                    if (!('each_over_content' in this.row)) this.row.each_over_content = "";
                    if (!('alt_text' in this.row)) this.row.alt_text = "";

                    if ($modal.data('bs.modal')) {
                        $modal.data('bs.modal').options.backdrop = 'static';
                        $modal.data('bs.modal').options.keyboard = false;
                    }
                }
            }
        }});
</script>

<style>
    .design-widget-multi-modal-each-help-icon {
        flex-shrink: 0;
    }

    .design-widget-multi-modal-each-help-tooltip {
        display: none;
        position: absolute;
        left: calc(100% + 8px);
        top: 50%;
        transform: translateY(-50%);
        background: rgba(0, 0, 0, 0.9);
        color: #fff;
        padding: 8px 12px;
        border-radius: 6px;
        font-size: 13px;
        white-space: nowrap;
        box-shadow: 0 2px 8px rgba(0,0,0,0.3);
        z-index: 1000;
    }

    .design-widget-multi-modal-each-help-tooltip.active {
        display: inline-block;
    }

    .jd-each-image-replace-actions {
        display: flex;
        justify-content: center;
        gap: 8px;
        margin-top: 10px;
    }
</style>
<script type="text/x-template" id="design-widget-multi-modal-free-template">
    <div v-if="load">
        <external-bs-modal v-model="modelValue" @update:modelValue="value => $emit('update:modelValue', value)">
            <template v-slot:header>
                <h5 class="modal-title" id="exampleModalLabel">무료 이미지</h5>
            </template>

            <template v-slot:default>
                <div>
                    <div style="margin-bottom: 15px;">
                        <div style="position: relative;">
                            <input type="text"
                                   class="form-control free-modal-image-modal-search-input"
                                   placeholder="검색어를 입력해 주세요."
                                   v-model="keyword"
                                   @keyup.enter="getImages()"/>
                            <button type="button"
                                    class="free-modal-image-modal-search-btn"
                                    @click="getImages()">
                                <img src="/file/icon/돋보기.svg">
                            </button>
                        </div>
                    </div>
                    <div class="text-right text-muted">Powered by Freepik</div>
                    <div v-if="loading">이미지 불러오는중...</div>
                    <template v-else>
                        <div v-if="rows.length == 0">{{text_content}}</div>
                        <div v-else>
                            <div class="free-modal-image-grid-flex">
                                <template v-for="item in visibleRows">
                                    <div class="free-modal-image-item"
                                         @click="choiceImage(item)"
                                         :class="activeImage(item)">
                                        <img :src="getImageSrc(item)">
                                        <div class="free-modal-image-overlay">
                                            <div class="free-modal-image-info">
                                                <div class="free-modal-author-name">{{getAuthorName(item)}}</div>
                                                <div class="free-modal-image-description">
                                                    {{getDescription(item)}}
                                                </div>
                                                <div class="free-modal-image-meta" v-if="getSourceUrl(item)">
                                                    <span>Freepik</span>
                                                    <a :href="getSourceUrl(item)"
                                                       target="_blank"
                                                       rel="noopener noreferrer"
                                                       class="free-modal-image-link"
                                                       @click.stop>원본</a>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </template>
                            </div>

                            <nav aria-label="...">
                                <ul class="pagination pagination-sm">
                                    <li class="page-item" :class="{disabled : !canGoPrevApiPage}" @click="goPrevApiPage()">
                                        <span class="page-link">&lt;&lt;</span>
                                    </li>
                                    <template v-for="num in displayedPages">
                                        <li :key="'freepik-page-' + num" class="page-item" :class="{active : num === page}" aria-current="page" @click="page = num;">
                                            <span class="page-link">{{num}}</span>
                                        </li>
                                    </template>
                                    <li class="page-item" :class="{disabled : !canGoNextApiPage}" @click="goNextApiPage()">
                                        <span class="page-link">&gt;&gt;</span>
                                    </li>
                                </ul>
                            </nav>

                            <div class="text-center" style="margin-top: 20px;" v-show="false">
                                <button type="button" class="btn btn-default btn-load-more" @click="getImages()">
                                    <i class="glyphicon glyphicon-refresh"></i> 새로고침
                                </button>
                            </div>
                        </div>
                    </template>

                </div>
            </template>


            <template v-slot:footer>
                <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
                <button type="button" class="btn btn-orange save" @click="saveImage()">선택</button>
            </template>
        </external-bs-modal>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-multi-modal-free",object : {
            template: "#design-widget-multi-modal-free-template",
            props: {
                modelValue : {type: Object, default: {}},
                widget_type : {type: String, default: ""},
                user_idx : {type: String, default: ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-multi-modal-free",
                    component_idx: "",
                    loading : false,

                    temp : {
                        $table : "",
                    },
                    row: {},
                    rows : [],
                    rowsByApiPage : {},

                    file_idx : "",
                    limit : 9,
                    page : 1,
                    apiPageSize : 99,
                    currentApiPage : 1,
                    lastApiPage : null,

                    image_url : "",
                    keyword : "",
                    searchedKeyword : "",
                    select_items : [],
                    text_content : "이미지가 없습니다.",
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
            },
            async mounted() {
                this.table = await this.api.table("jd_file");

                this.load = true;

                this.$nextTick(async () => {
                });
            },
            updated() {

            },
            methods: {
                async getImages(reset = true) {
                    this.loading = true;
                    if(reset) {
                        this.page = 1;
                        this.currentApiPage = 1;
                        this.text_content = "이미지가 없습니다.";
                        this.rows = [];
                        this.rowsByApiPage = {};
                        this.lastApiPage = null;
                        this.searchedKeyword = this.keyword;
                    }

                    try {
                        await this.requestApiPage(1, reset ? this.keyword : this.searchedKeyword);
                        this.applyApiPage(1);
                    } catch (err) {
                        this.rows = [];
                        this.rowsByApiPage = {};
                        this.text_content = "현재 요청하신 무료 이미지의 사용이 불가능합니다. 죄송합니다.";
                    } finally {
                        this.loading = false;
                    }
                },
                getApiPage(page) {
                    return Math.ceil(page / this.localPageSize) || 1;
                },
                getLocalPage(page) {
                    return ((page - 1) % this.localPageSize) + 1;
                },
                async requestApiPage(apiPage, keyword = "") {
                    const targetKeyword = keyword !== "" ? keyword : this.searchedKeyword;
                    if(this.rowsByApiPage[apiPage]) {
                        return this.rowsByApiPage[apiPage];
                    }

                    let res = await this.lib.ajax("get_resources",{term : targetKeyword, page : apiPage},"/JayDream/provider/freepik/api.php");
                    let data = res?.data?.data?.data || [];

                    this.rowsByApiPage = Object.assign({}, this.rowsByApiPage, {
                        [apiPage] : data
                    });
                    this.rows = data;

                    if(data.length < this.apiPageSize) {
                        this.lastApiPage = apiPage;
                    }

                    return data;
                },
                applyApiPage(apiPage) {
                    this.currentApiPage = apiPage;
                    this.rows = this.rowsByApiPage[apiPage] || [];
                    this.page = ((apiPage - 1) * this.localPageSize) + 1;
                },
                async goNextApiPage() {
                    if(this.loading || !this.canGoNextApiPage) return;

                    const targetApiPage = this.currentApiPage + 1;

                    this.loading = true;
                    try {
                        const data = await this.requestApiPage(targetApiPage);
                        if(!data.length) {
                            this.lastApiPage = targetApiPage - 1;
                            return;
                        }
                        this.applyApiPage(targetApiPage);
                    } catch (err) {
                        this.text_content = "현재 요청하신 무료 이미지의 사용이 불가능합니다. 죄송합니다.";
                    } finally {
                        this.loading = false;
                    }
                },
                goPrevApiPage() {
                    if(this.loading || !this.canGoPrevApiPage) return;
                    this.applyApiPage(this.currentApiPage - 1);
                },
                normalizeImageUrl(url) {
                    if(!url) return "";
                    return url.replace(/^http:\/\//i, "https://");
                },
                getImageSrc(item) {
                    return this.normalizeImageUrl(item && item.image && item.image.source ? item.image.source.url : "");
                },
                getSourceUrl(item) {
                    return this.normalizeImageUrl(item && item.url ? item.url : "");
                },
                getAuthorName(item) {
                    return item && item.author && item.author.name ? item.author.name : "Freepik";
                },
                getDescription(item) {
                    if(item && item.title) return item.title;
                    if(item && item.filename) return item.filename;
                    return "제목 없음";
                },
                saveImage() {
                    if(this.isReplaceMode()) {
                        const selected = this.select_items[0];
                        if(selected && selected.src) {
                            this.replaceTargetImage(selected);
                        }

                        this.$closeModal(this.modelValue);
                        return;
                    }

                    this.modelValue.row.images = [...this.select_items];
                    this.$closeModal(this.modelValue);
                },
                isReplaceMode() {
                    return !!(this.modelValue && this.modelValue.replace_target);
                },
                replaceTargetImage(item) {
                    this.modelValue.replace_target.src = item.src;
                    delete this.modelValue.replace_target._replace_upload_file;
                },
                activeImage(item) {
                    return this.select_items.some(selected => selected.src === this.getImageSrc(item)) ? "active" : "";
                },
                choiceImage(item) {
                    const imageSrc = this.getImageSrc(item);
                    if(this.isReplaceMode()) {
                        this.select_items = imageSrc ? [{ src: imageSrc }] : [];
                        return;
                    }

                    const index = this.select_items.findIndex(selected => selected.src === imageSrc);

                    if (index > -1) {
                        this.select_items.splice(index, 1);
                    } else {
                        this.select_items.push({ src: imageSrc });
                    }
                },
            },
            computed: {
                localPageSize() {
                    return this.apiPageSize / this.limit;
                },
                visibleRows() {
                    const localPage = this.getLocalPage(this.page);
                    const start = (localPage - 1) * this.limit;
                    const end = localPage * this.limit;
                    return this.rows.slice(start, end);
                },
                totalPage() {
                    if(!this.rows.length) return 0;
                    return this.rows.length % this.limit === 0 ? this.rows.length / this.limit : Math.floor(this.rows.length / this.limit) + 1;
                },
                displayedPages() {
                    const start = this.page - this.getLocalPage(this.page) + 1;
                    return Array.from({length : this.totalPage}, (_, index) => start + index);
                },
                canGoPrevApiPage() {
                    return this.currentApiPage > 1;
                },
                canGoNextApiPage() {
                    if(this.lastApiPage !== null) {
                        return this.currentApiPage < this.lastApiPage;
                    }

                    return this.rows.length === this.apiPageSize;
                }
            },
            watch: {
                async "modelValue.status"(value, old_value) {
                    const $modal = $("#widget_setting_jd");

                    if(!value) {
                        this.file_idx = "";
                        this.rows = [];
                        this.rowsByApiPage = {};
                        this.page = 1;
                        this.currentApiPage = 1;
                        this.lastApiPage = null;
                        this.searchedKeyword = "";
                        this.select_items = [];
                        this.text_content = "이미지가 없습니다.";
                        this.modelValue.replace_target = null;

                        if($modal.data('bs.modal')) {
                            $modal.data('bs.modal').options.backdrop = true;
                            $modal.data('bs.modal').options.keyboard = true;
                        }
                        return false;
                    }

                    if($modal.data('bs.modal')) {
                        $modal.data('bs.modal').options.backdrop = 'static';
                        $modal.data('bs.modal').options.keyboard = false;
                    }

                    await this.getImages();

                    if(this.isReplaceMode()) {
                        this.select_items = this.modelValue.replace_target && this.modelValue.replace_target.src ? [{ src: this.modelValue.replace_target.src }] : [];
                    } else {
                        this.select_items = Array.isArray(this.modelValue.row.images) ? [...this.modelValue.row.images] : [];
                    }
                }
            }
        }});
</script>

<style>
    .free-modal-image-grid-flex {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        padding: 10px 0;
    }

    .free-modal-image-item {
        cursor: pointer;
        border-radius: 4px;
        overflow: hidden;
        border: 1px solid #ddd;
        transition: all 0.2s;
        aspect-ratio: 1 / 1;
        position: relative;
    }

    .free-modal-image-item img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
        transition: all 0.2s;
    }

    .free-modal-image-item:hover {
        border-color: #337ab7;
        box-shadow: 0 2px 8px rgba(0,0,0,0.15);
        transform: scale(1.05);
    }

    .free-modal-image-item.active {
        outline: 3px solid red;
        outline-offset: -3px;
    }

    .free-modal-image-overlay {
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
        background: linear-gradient(to top, rgba(0,0,0,0.95) 0%, rgba(0,0,0,0.85) 40%, rgba(0,0,0,0.3) 70%, transparent 100%);
        padding: 15px 10px 10px;
        opacity: 0;
        transition: opacity 0.3s;
        z-index: 999999;
    }

    .free-modal-image-item:hover .free-modal-image-overlay {
        opacity: 1;
    }

    .free-modal-image-info {
        color: white;
        font-size: 12px;
    }

    .free-modal-author-name {
        font-weight: bold;
        margin-bottom: 3px;
        font-size: 13px;
        text-shadow: 0 1px 3px rgba(0,0,0,0.8), 0 0 10px rgba(0,0,0,0.5);
    }

    .free-modal-image-description {
        font-size: 11px;
        line-height: 1.3;
        opacity: 0.95;
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        text-shadow: 0 1px 3px rgba(0,0,0,0.8), 0 0 8px rgba(0,0,0,0.5);
    }

    .free-modal-image-meta {
        margin-top: 6px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 10px;
        font-size: 11px;
    }

    .free-modal-image-link {
        color: #fff;
        text-decoration: underline;
    }

    .free-modal-image-link:hover,
    .free-modal-image-link:focus {
        color: #fff;
    }

    .free-modal-image-modal-search-input {
        padding-right: 45px !important;
        border-radius: 4px;
        border: 1px solid #ddd;
        height: 40px;
    }

    .free-modal-image-modal-search-input:focus {
        border-color: #66afe9;
        outline: 0;
        box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6);
    }

    .free-modal-image-modal-search-btn {
        position: absolute;
        right: 8px;
        top: 50%;
        transform: translateY(-50%);
        background: none;
        border: none;
        padding: 5px 10px;
        cursor: pointer;
        color: #999;
        transition: color 0.2s;
    }

    .free-modal-image-modal-search-btn:hover {
        color: #333;
    }

    .free-modal-image-modal-search-btn:focus {
        outline: none;
    }
</style>
<script type="text/x-template" id="design-widget-multi-modal-storage-template">
    <div v-if="load">
        <external-bs-modal v-model="modelValue" @update:modelValue="value => $emit('update:modelValue', value)">
            <template v-slot:header>
                <h5 class="modal-title" id="exampleModalLabel">이미지 보관함</h5>
            </template>

            <!-- body -->
            <template v-slot:default>
                <div>
                    <div v-if="loading">이미지 불러오는중...</div>
                    <template v-else>
                        <div v-if="rows.length == 0">이미지가 없습니다.</div>
                        <div v-else>
                            <div class="image-grid-flex">
                                <template v-for="item,index in rows">
                                    <div v-if="index >= (page - 1) * limit && index < page * limit" class="image-item" @click="choiceImage(item)" :class="activeImage(item)">
                                        <img :src="item.src">
                                        <button class="image-delete-btn" @click.stop="deleteImage(item)">
                                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                                <polyline points="3 6 5 6 21 6"></polyline>
                                                <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                                                <line x1="10" y1="11" x2="10" y2="17"></line>
                                                <line x1="14" y1="11" x2="14" y2="17"></line>
                                            </svg>
                                        </button>
                                    </div>
                                </template>
                            </div>

                            <nav aria-label="...">
                                <ul class="pagination pagination-sm">
                                    <template v-for="num in totalPage">
                                        <li class="page-item" :class="{active : num === page}" aria-current="page" @click="page = num;">
                                            <span class="page-link">{{num}}</span>
                                        </li>
                                    </template>
                                </ul>
                            </nav>
                        </div>
                    </template>

                </div>
            </template>


            <template v-slot:footer>
                <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
                <button type="button" class="btn btn-orange save" @click="saveImage()">저장</button>
            </template>
        </external-bs-modal>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-multi-modal-storage",object : {
            template: "#design-widget-multi-modal-storage-template",
            props: {
                modelValue : {type: Object, default: {}},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-multi-modal-storage",
                    component_idx: "",
                    loading : false,

                    temp : {
                        $table : "",
                    },
                    row: {},
                    rows : [],

                    limit : 9,
                    page : 1,

                    select_items : [],
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
            },
            async mounted() {
                this.table = await this.api.table("jd_file");

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {
                async deleteImage(item) {
                    if(!await this.lib.confirm("삭제시 연결된 이미지가 있을경우 엑박이 뜹니다 삭제 하시겠습니까?")) return false;

                    await this.api.table("jd_file").delete(item,{return : true});

                    await this.getImages();
                },
                async getImages() {
                    this.loading = true;

                    await this.table.where("user_idx",this.modelValue.row.user_idx).get(this.rows);

                    this.loading = false;

                },
                saveImage() {
                    if(this.isReplaceMode()) {
                        const selected = this.select_items[0];
                        if(selected && selected.src) {
                            this.replaceTargetImage(selected);
                        }

                        this.$closeModal(this.modelValue);
                        return;
                    }

                    // 전체 교체 - 객체 배열 그대로 저장
                    this.modelValue.row.images = [...this.select_items];
                    this.$closeModal(this.modelValue);
                },
                isReplaceMode() {
                    return !!(this.modelValue && this.modelValue.replace_target);
                },
                replaceTargetImage(item) {
                    this.modelValue.replace_target.src = item.src;
                    delete this.modelValue.replace_target._replace_upload_file;
                },
                activeImage(item) {
                    // 전체 교체 - 객체 배열에서 src 비교
                    return this.select_items.some(selected => selected.src === item.src) ? "active" : "";
                },
                choiceImage(item) {
                    if(this.isReplaceMode()) {
                        this.select_items = item && item.src ? [{ src: item.src }] : [];
                        return;
                    }

                    // 전체 교체 - 객체로 추가/제거
                    const index = this.select_items.findIndex(selected => selected.src === item.src);

                    if (index > -1) {
                        // 이미 있으면 제거
                        this.select_items.splice(index, 1);
                    } else {
                        // 없으면 객체로 추가
                        this.select_items.push({ src: item.src });
                    }
                },
            },
            computed: {
                totalPage() {
                    return this.rows.length % this.limit === 0 ? this.rows.length / this.limit : Math.floor(this.rows.length / this.limit) + 1;
                }
            },
            watch: {
                async "modelValue.status"(value, old_value) {
                    const $modal = $("#widget_setting_jd");

                    if(!value) {
                        this.file_idx = "";
                        this.rows = [];
                        this.modelValue.replace_target = null;

                        if ($modal.data('bs.modal')) {
                            $modal.data('bs.modal').options.backdrop = true;
                            $modal.data('bs.modal').options.keyboard = true;
                        }



                        return false;
                    }


                    if ($modal.data('bs.modal')) {
                        $modal.data('bs.modal').options.backdrop = 'static';
                        $modal.data('bs.modal').options.keyboard = false;
                    }


                    await this.getImages();

                    if(this.isReplaceMode()) {
                        this.select_items = this.modelValue.replace_target && this.modelValue.replace_target.src ? [{ src: this.modelValue.replace_target.src }] : [];
                    } else {
                        this.select_items = [...this.modelValue.row.images];
                    }
                }
            }
        }});
</script>

<style>
    .image-grid-flex {
        display: grid;
        grid-template-columns: repeat(3, 1fr); /* 4열 */
        padding: 10px 0;
    }

    .image-item {
        cursor: pointer;
        border-radius: 4px;
        overflow: hidden;
        border: 1px solid #ddd;
        transition: all 0.2s;
        aspect-ratio: 1 / 1; /* 정사각형 비율 */
        position: relative; /* 삭제 버튼 위치를 위해 추가 */
    }

    .image-item img {
        width: 100%;
        height: 100%;
        object-fit: cover; /* 이미지를 정사각형에 꽉 차게 */
        display: block;
        transition: all 0.2s;
    }

    .image-item:hover {
        border-color: #337ab7;
        box-shadow: 0 2px 8px rgba(0,0,0,0.15);
        transform: scale(1.05);
    }

    .image-item.active {
        outline: 3px solid red;
        outline-offset: -3px;
    }

    /* 삭제 버튼 스타일 */
    .image-delete-btn {
        position: absolute;
        top: 4px;
        right: 4px;
        width: 28px;
        height: 28px;
        background-color: rgba(255, 255, 255, 0.9);
        border: 1px solid #ddd;
        border-radius: 4px;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
        opacity: 0;
        transition: all 0.2s;
        padding: 0;
    }

    .image-delete-btn svg {
        color: #dc3545;
    }

    .image-item:hover .image-delete-btn {
        opacity: 1;
    }

    .image-delete-btn:hover {
        background-color: #dc3545;
        border-color: #dc3545;
    }

    .image-delete-btn:hover svg {
        color: white;
    }
</style>
<script type="text/x-template" id="design-widget-multi-modal-tab1-template">
    <div>
        <h4 class="jd-image-modal-title">
            <img src="/file/icon/이미지추가.svg"> 이미지 추가
        </h4>

        <div class="design-widget-multi-modal-container">
            <!-- 버튼 그룹 -->
            <div class="button-group" style="margin-bottom: 15px;">
                <button class="btn btn-orange btn-sm design-widget-multi-modal-btn-upload" @click="$refs.fileInput.click()">직접 업로드</button>
                <button class="btn btn-orange btn-sm design-widget-multi-modal-btn-free" @click="$openModal(modal2,{row})">무료 이미지</button>
                <button class="btn btn-orange btn-sm design-widget-multi-modal-btn-storage" @click="$openModal(modal1,{row})">이미지 보관함</button>
            </div>
            <input class="hidden" type="file" ref="fileInput" @change="vue.changeFile($event,row,'upload_file')" name="names[]" multiple>

            <!-- 이미지 그리드 (가로 스크롤) -->
            <div class="design-widget-multi-modal-scroll-wrapper" style="overflow-x: auto; overflow-y: hidden; white-space: nowrap; margin-bottom: 15px;">
                <draggable v-model="allImages" @end="onDragEnd" item-key="primary">
                    <template #item="{ element : item, index }">
                        <div class="design-widget-multi-modal-image-grid" style="display: inline-block;">
                            <div class="design-widget-multi-modal-image-item" style="display: inline-block; vertical-align: top; width: 200px; margin-right: 10px;">
                                <div class="thumbnail" style="position: relative;">
                                    <img :src="item.src" style="width: 100%; height: 150px; object-fit: cover;">
                                    <button class="btn btn-danger btn-xs" style="position: absolute; top: 5px; right: 5px;"
                                            @click="deleteImage(item,index)">
                                        <i class="glyphicon glyphicon-trash"></i>
                                    </button>
                                    <!-- URL 오버레이 -->
                                    <div style="position: absolute; bottom: 0; left: 0; right: 0; background: rgba(0,0,0,0.55); color: #fff; font-size: 11px; padding: 4px 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" v-if="item.option_mouse_click_url">
                                        <i class="glyphicon glyphicon-link" style="margin-right: 3px;"></i>{{ item.option_mouse_click_url }}
                                    </div>
                                </div>

                                <div class="text-center design-widget-multi-modal-btn-wrapper" style="margin-top: 0;">
                                    <button class="btn btn-default btn-sm btn-block design-widget-multi-modal-btn-edit" style="color: #e64d11; border-radius: 0; border-top: none; margin-top: 0;" @click="$openModal(modal3,{row : item, parent_row : row})">
                                        <img src="/file/icon/개별설정.svg"> 개별 설정
                                    </button>
                                </div>

                            </div>
                        </div>
                    </template>
                </draggable>

            </div>

            <p style="color: #999; font-size: 12px; margin-top: 15px;">
                ※ 마우스 드래그로 이미지의 순서를 변경할 수 있습니다.
            </p>
        </div>
    </div>

    <design-widget-multi-modal-storage v-model="modal1" @update:modelValue="value => $emit('update:modelValue', value)"></design-widget-multi-modal-storage>
    <design-widget-multi-modal-free v-model="modal2" @update:modelValue="value => $emit('update:modelValue', value)"></design-widget-multi-modal-free>
    <design-widget-multi-modal-each v-model="modal3" :site_code ></design-widget-multi-modal-each>
</script>

<script>
    JayDream_components.push({name : "design-widget-multi-modal-tab1",object : {
            template: "#design-widget-multi-modal-tab1-template",
            props: {
                row : {type : String, default : ""},
                site_code : {type : String, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-multi-modal-tab1",
                    component_idx: "",

                    modal1 : this.lib.createModalObject(),
                    modal2 : this.lib.createModalObject(),
                    modal3 : this.lib.createModalObject(),

                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.sessions = await this.session.all();

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {
                deleteImage(item,index) {
                    // allImages 배열에서 제거
                    const allImages = this.allImages;
                    allImages.splice(index, 1);

                    // priority 재설정하면서 원본 배열에 반영
                    this.allImages = allImages;
                },
                async onDragEnd(evt,array) {
                    if (evt.oldIndex === evt.newIndex) return;

                    const movedItem = array[evt.newIndex];

                    const start = Math.min(evt.oldIndex, evt.newIndex);
                    const end = Math.max(evt.oldIndex, evt.newIndex);

                    // console.log(`이동된 범위: ${start} ~ ${end}`);

                    for (let i = start; i <= end; i++) {
                        const item = array[i];
                        item.priority = i;

                        this.$postData(item,{return : true});
                        // console.log(`수정 대상 idx ${i}:`, item);
                    }

                    // console.log('드래그 완료:', evt.oldIndex, '→', evt.newIndex);
                },
            },
            computed: {
                allImages: {
                    get() {
                        return [
                            ...this.row.upload_file,
                            ...this.row.images
                        ].sort((a, b) => {
                            const aPriority = a.priority ?? Infinity;
                            const bPriority = b.priority ?? Infinity;
                            return aPriority - bPriority;
                        });
                    },
                    set(value) {
                        // priority만 재설정
                        value.forEach((item, index) => {
                            item.priority = index;
                        });

                        // 🔥 원본 배열도 업데이트 (삭제된 항목 반영)
                        this.row.upload_file = this.row.upload_file.filter(item =>
                            value.includes(item)
                        );

                        this.row.images = this.row.images.filter(item =>
                            value.includes(item)
                        );
                    }
                }
            },
            watch: {

            }
        }});
</script>
<script type="text/x-template" id="design-widget-multi-modal-tab2-template">
    <div class="row">
        <div class="col-md-12">
            <h4 class="jd-image-modal-title">
                <img src="/file/icon/레이아웃.svg"> 레이아웃 설정
            </h4>
            <div class="row" style="margin-bottom: 20px;">
                <div class="col-md-6">
                    <p style="font-weight: bold">한 줄 당 이미지 개수</p>
                    <div class="d-flex gap-5">
                        <div class="d-flex align-items-center gap-3 mb-3">
                            <span>PC</span>
                            <select class="form-control" style="width: 75px;" v-model="row.line_image_pc">
                                <template v-for="item in 10">
                                    <option :value="item">{{item}}개</option>
                                </template>
                            </select>
                        </div>


                        <div class="d-flex align-items-center gap-3 mb-3">
                            <span>모바일</span>
                            <select class="form-control" style="width: 75px;" v-model="row.line_image_mobile">
                                <template v-for="item in 10">
                                    <option :value="item">{{item}}개</option>
                                </template>
                            </select>
                        </div>
                    </div>
                </div>

                <div class="col-md-6">
                    <p style="font-weight: bold">이미지 사이 간격</p>
                    <div class="d-flex gap-5">
                        <div class="d-flex align-items-center gap-3 mb-3">
                            <span>좌우 간격</span>
                            <input type="text" v-number class="form-control input-sm" style="width: 80px; margin: 0 5px;" v-model="row.gap_x">
                            <span>px</span>
                        </div>


                        <div class="d-flex align-items-center gap-3 mb-3">
                            <span>상하 간격</span>
                            <input type="text" v-number class="form-control input-sm" style="width: 80px; margin: 0 5px;" v-model="row.gap_y">
                            <span>px</span>
                        </div>
                    </div>
                </div>
            </div>

            <div class="row">
                <div class="col-md-6">
                    <p style="font-weight: bold">이미지 비율</p>
                    <div class="d-flex align-items-center gap-3">
                        <label class="radio-inline" style="padding-left: 0;">
                            <input type="radio" value="fixed" v-model="row.ratio_type"> 고정비율
                        </label>
                        <select class="form-control" style="width: 70px;" v-model="row.ratio_value">
                            <option value="1:1">1:1</option>
                            <option value="3:4">3:4</option>
                            <option value="4:3">4:3</option>
                            <option value="9:16">9:16</option>
                            <option value="16:9">16:9</option>
                            <option value="4:1.5">4:1.5</option>
                            <option value="5:7">5:7</option>
                            <option value="원형">원형</option>
                        </select>


                        <label class="radio-inline">
                            <input type="radio" value="original" v-model="row.ratio_type"> 원본비율
                        </label>
                    </div>
                </div>

                <div class="col-md-6">
                    <p style="font-weight: bold">더보기 버튼</p>
                    <div class="d-flex align-items-center gap-3">
                        <label class="radio-inline" style="padding-left: 0;">
                            <input type="radio" value="false" v-model="row.more_btn"> 미사용
                        </label>

                        <label class="radio-inline">
                            <input type="radio" value="true" v-model="row.more_btn"> 사용
                        </label>
                        <select class="form-control" style="width: 65px;" v-model="row.more_btn_line">
                            <template v-for="item in 10">
                                <option :value="item">{{item}}</option>
                            </template>
                        </select>
                        <span>줄 초과 시</span>
                    </div>
                </div>
            </div>
        </div>
    </div>
</script>

<script>
    JayDream_components.push({name : "design-widget-multi-modal-tab2",object : {
            template: "#design-widget-multi-modal-tab2-template",
            props: {
                row : {type : String, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-multi-modal-tab2",
                    component_idx: "",

                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {

            },
            computed: {

            },
            watch: {

            }
        }});
</script><script type="text/x-template" id="design-widget-multi-modal-tab3-template">
    <div class="row">
        <div class="col-md-12">
            <h4 class="jd-image-modal-title">
                <img src="/file/icon/basic.svg"> 공통 설정
            </h4>

            <div class="row">
                <div class="col-md-12" style="margin-bottom: 20px;">
                    <p style="font-weight: bold">이미지 스타일</p>

                    <div class="d-flex gap-5">
                        <div class="d-flex align-items-center gap-2">
                            <input type="checkbox" v-model="row.box_shadow_use"> 그림자
                            <select class="form-control input-sm" style="width: 80px; display: inline-block;" v-model="row.box_shadow_opacity">
                                <option value="10">10%</option>
                                <option value="20">20%</option>
                                <option value="30">30%</option>
                                <option value="40">40%</option>
                                <option value="50">50%</option>
                                <option value="60">60%</option>
                                <option value="70">70%</option>
                                <option value="80">80%</option>
                                <option value="90">90%</option>
                            </select>
                        </div>

                        <div class="d-flex align-items-center gap-2">
                            <input type="checkbox" v-model="row.border_use"> 테두리

                            <!-- 컬러피커 -->
                            <external-picker v-model="row.border_color"></external-picker>

                            <!-- 두께 입력 -->
                            <div class="number-input-wrapper">
                                <input type="text" v-model="row.border_width" class="form-control form-control-sm" v-number>
                                <div class="number-stepper">
                                    <button type="button" class="stepper-up" @click="row.border_width++;">
                                        <svg width="10" height="6" viewBox="0 0 10 6">
                                            <path d="M5 0L10 6H0L5 0Z" fill="#666"/>
                                        </svg>
                                    </button>
                                    <button type="button" class="stepper-down" @click="row.border_width--;">
                                        <svg width="10" height="6" viewBox="0 0 10 6">
                                            <path d="M5 6L0 0H10L5 6Z" fill="#666"/>
                                        </svg>
                                    </button>
                                </div>
                            </div>
                        </div>

                        <div class="d-flex align-items-center gap-2">
                            <input type="checkbox" v-model="row.border_radius_use" :disabled="row.ratio_type == 'fixed' && row.ratio_value == '원형'"> 모서리 둥글게

                            <div class="number-input-wrapper">
                                <img src="/file/icon/Group 95.svg" class="input-icon-left">
                                <input type="text" v-model="row.border_radius_px" class="form-control form-control-sm" v-number>
                                <div class="number-stepper">
                                    <button type="button" class="stepper-up" @click="row.border_radius_px++;">
                                        <svg width="10" height="6" viewBox="0 0 10 6">
                                            <path d="M5 0L10 6H0L5 0Z" fill="#666"/>
                                        </svg>
                                    </button>
                                    <button type="button" class="stepper-down" @click="row.border_radius_px--;">
                                        <svg width="10" height="6" viewBox="0 0 10 6">
                                            <path d="M5 6L0 0H10L5 6Z" fill="#666"/>
                                        </svg>
                                    </button>
                                </div>
                            </div>
                        </div>

                    </div>
                </div>

                <div class="col-md-12">
                    <p style="font-weight: bold">마우스 클릭 동작</p>

                    <div class="d-flex align-items-center gap-2">
                        <label class="radio-inline" style="padding-left: 0;">
                            <input type="radio" value="동작없음" v-model="row.mouse_click"> 동작없음
                        </label>

                        <label class="radio-inline">
                            <input type="radio" value="링크 이동" v-model="row.mouse_click"> 링크 이동
                        </label>

                        <label class="radio-inline">
                            <input type="radio" value="크게 보기" v-model="row.mouse_click"> 크게 보기
                        </label>
                    </div>

                    <p style="color: #999; font-size: 12px; margin-top: 15px;">
                        ※ 링크 등록은 이미지 `개별 설정`에서 가능합니다.
                    </p>
                </div>
            </div>
        </div>
    </div>
</script>

<script>
    JayDream_components.push({name : "design-widget-multi-modal-tab3",object : {
            template: "#design-widget-multi-modal-tab3-template",
            props: {
                row : {type : String, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-multi-modal-tab3",
                    component_idx: "",

                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {

            },
            computed: {

            },
            watch: {

            }
        }});
</script><script type="text/x-template" id="design-widget-multi-modal-tab4-template">
<div>
    <div class="panel panel-default" style="margin-bottom: 10px;">
        <div class="panel-heading" style="cursor: pointer; display: flex; justify-content: space-between; align-items: center;" data-toggle="collapse" data-target="#textSection">
            <div>
                <img src="/file/icon/text.svg" style="height: 23px">
                <span class="jd-effect-text">텍스트 삽입</span>
                <button class="jd-effect-btn">
                    <img :src="`/file/icon/${row.option_text}.svg`" style="width: 16px; height: 16px;">
                    <span class="fs-14">{{row.option_text}}</span>
                </button>
            </div>
            <i class="glyphicon glyphicon-chevron-down"></i>
        </div>
        <div id="textSection" class="panel-collapse collapse">
            <div class="panel-body">
                <div class="jd-text-section-row">
                    <div class="jd-text-option-list">
                        <div class="jd-action-button" :class="{active : row.option_text == '사용안함'}" @click="row.option_text = '사용안함'; editor = false;">
                            <img src="/file/icon/사용안함.svg">
                            <span>사용안함</span>
                        </div>

                        <div class="jd-action-button" :class="{active : row.option_text == '이미지 하단'}" @click="row.option_text = '이미지 하단'">
                            <img src="/file/icon/이미지 하단.svg" >
                            <span>이미지 하단</span>
                        </div>

                        <div class="jd-action-button" :class="{active : row.option_text == '이미지 겹침'}" @click="row.option_text = '이미지 겹침'">
                            <img src="/file/icon/이미지 겹침.svg">
                            <span>이미지 겹침</span>
                        </div>
                    </div>

                    <div class="jd-text-setting-panel">
                        <div v-if="row.option_text == '사용안함'" style="flex : 1; text-align : center; align-content: center;">
                            <span style="font-size: large; color : #959595; ">텍스트를 추가하지 않았습니다.</span>
                        </div>
                        <div v-if="row.option_text == '이미지 하단'" style="flex : 1; align-content: center;">
                            <p class="jd-text-help">
                                ※ 생성된 텍스트를 더블클릭하면 문구와 서식을 상세 설정할 수 있습니다.
                            </p>
                            <div class="jd-text-margin-table jd-text-margin-table-standalone">
                                <div class="jd-text-margin-table-header">
                                    <span></span>
                                    <span>상</span>
                                    <span>하</span>
                                    <span>좌</span>
                                    <span>우</span>
                                </div>
                                <div class="jd-text-margin-table-row">
                                    <div class="jd-text-margin-table-label">PC 여백</div>
                                    <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_top">
                                    <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_bottom">
                                    <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_left">
                                    <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_right">
                                </div>
                                <div class="jd-text-margin-table-row">
                                    <div class="jd-text-margin-table-label">모바일 여백</div>
                                    <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_top">
                                    <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_bottom">
                                    <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_left">
                                    <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_right">
                                </div>
                            </div>
                        </div>

                        <div v-if="row.option_text == '이미지 겹침'" style="flex:1;">
                            <p class="jd-text-help">
                                ※ 생성된 텍스트를 더블클릭하면 문구와 서식을 상세 설정할 수 있습니다.
                            </p>
                            <div class="jd-text-overlay-setting">
                                <!-- 텍스트 위치 -->
                                <div class="jd-text-position-setting">
                                    <div style="margin-bottom: 5px; font-weight: bold;">텍스트 위치</div>
                                    <div class="jd-text-position-grid">
                                        <div class="jd-position-cell" :class="{active: row.option_text_position === 'top-left'}" @click="row.option_text_position = 'top-left'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_text_position === 'top-center'}" @click="row.option_text_position = 'top-center'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_text_position === 'top-right'}" @click="row.option_text_position = 'top-right'"></div>

                                        <div class="jd-position-cell" :class="{active: row.option_text_position === 'middle-left'}" @click="row.option_text_position = 'middle-left'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_text_position === 'middle-center'}" @click="row.option_text_position = 'middle-center'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_text_position === 'middle-right'}" @click="row.option_text_position = 'middle-right'"></div>

                                        <div class="jd-position-cell" :class="{active: row.option_text_position === 'bottom-left'}" @click="row.option_text_position = 'bottom-left'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_text_position === 'bottom-center'}" @click="row.option_text_position = 'bottom-center'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_text_position === 'bottom-right'}" @click="row.option_text_position = 'bottom-right'"></div>
                                    </div>
                                </div>

                                <!-- 텍스트 여백 -->
                                <div class="jd-text-margin-table">
                                    <div class="jd-text-margin-table-header">
                                        <span></span>
                                        <span>상</span>
                                        <span>하</span>
                                        <span>좌</span>
                                        <span>우</span>
                                    </div>
                                    <div class="jd-text-margin-table-row">
                                        <div class="jd-text-margin-table-label">PC 여백</div>
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_top">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_bottom">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_left">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_margin_right">
                                    </div>
                                    <div class="jd-text-margin-table-row">
                                        <div class="jd-text-margin-table-label">모바일 여백</div>
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_top">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_bottom">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_left">
                                        <input type="text" v-number class="form-control input-sm" v-model="row.option_text_mobile_margin_right">
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <div class="panel panel-default" style="margin-bottom: 10px;">
        <div class="panel-heading" style="cursor: pointer; display: flex; justify-content: space-between; align-items: center;" data-toggle="collapse" data-target="#scrollSection">
            <div>
                <img src="/file/icon/scroll.svg" style="height: 23px">
                <span class="jd-effect-text">스크롤 애니메이션</span>
                <button class="jd-effect-btn">
                    <img :src="`/file/icon/${row.option_scroll}.svg`" style="width: 16px; height: 16px;">
                    <span class="fs-14">{{row.option_scroll}}</span>
                </button>
            </div>
            <i class="glyphicon glyphicon-chevron-down"></i>
        </div>
        <div id="scrollSection" class="panel-collapse collapse">
            <div class="panel-body">
                <div class="row" style="display: flex;">
                    <div class="col-md-6" style="border-right: 1px solid #ddd;">
                        <div class="jd-action-button" :class="{active : row.option_scroll == '효과없음'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '효과없음'">
                            <img src="/file/icon/효과없음.svg">
                            <span>효과없음</span>
                        </div>
                        <div class="jd-action-button" :class="{active : row.option_scroll == '페이드'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '페이드'">
                            <img src="/file/icon/페이드.svg">
                            <span>페이드</span>
                        </div>
                        <div class="jd-action-button" :class="{active : row.option_scroll == '줌인'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '줌인'">
                            <img src="/file/icon/줌인.svg">
                            <span>줌인</span>
                        </div>
                        <div class="jd-action-button" :class="{active : row.option_scroll == '줌아웃'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '줌아웃'">
                            <img src="/file/icon/줌아웃.svg">
                            <span>줌아웃</span>
                        </div>
                        <div class="jd-action-button" :class="{active : row.option_scroll == '플립'}" @click=" row.option_scroll_direction = 'up'; row.option_scroll = '플립'">
                            <img src="/file/icon/플립.svg">
                            <span>플립</span>
                        </div>
                    </div>

                    <div class="col-md-6" style="display : flex;">
                        <template v-if="row.option_scroll == '효과없음'">
                            <div style="flex: 1; text-align: center; align-content: center;">
                                <span style="font-size: large; color : #959595; ">스크롤 애니메이션을 선택하지 않았습니다.</span>
                            </div>
                        </template>

                        <template v-else>
                            <div style="display: flex; flex-direction: column; gap: 15px; padding: 15px;">
                                <!-- 방향 -->
                                <div style="display: flex; align-items: center; gap: 10px;">
                                    <label style="margin: 0; font-weight: 600; font-size: 14px;">방향</label>
                                    <div style="display: flex; gap: 6px;">
                                        <button class="jd-direction-btn" v-if="row.option_scroll != '플립'" @click="row.option_scroll_direction = 'center'" :class="{active : row.option_scroll_direction == 'center'}">
                                            <i class="fas fa-circle" style="font-size: 12px;"></i>
                                        </button>
                                        <button class="jd-direction-btn" @click="row.option_scroll_direction = 'up'" :class="{active : row.option_scroll_direction == 'up'}">
                                            <i class="fas fa-arrow-up" style="font-size: 14px;"></i>
                                        </button>
                                        <button class="jd-direction-btn" @click="row.option_scroll_direction = 'down'" :class="{active : row.option_scroll_direction == 'down'}">
                                            <i class="fas fa-arrow-down" style="font-size: 14px;"></i>
                                        </button>
                                        <button class="jd-direction-btn" @click="row.option_scroll_direction = 'left'" :class="{active : row.option_scroll_direction == 'left'}">
                                            <i class="fas fa-arrow-left" style="font-size: 14px;"></i>
                                        </button>
                                        <button class="jd-direction-btn" @click="row.option_scroll_direction = 'right'" :class="{active : row.option_scroll_direction == 'right'}">
                                            <i class="fas fa-arrow-right" style="font-size: 14px;"></i>
                                        </button>
                                    </div>
                                </div>

                                <div style="display: flex; align-items: center; gap: 5px;">
                                    <label style="margin: 0; font-weight: 600; font-size: 14px;">지속시간</label>
                                    <input type="number" v-number v-model="row.option_scroll_duration" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                    <span style="font-size: 14px; color: #666;">ms</span>

                                    <label style="margin: 0; font-weight: 600; font-size: 14px; margin-left : 10px;">지연시간</label>
                                    <input type="number" v-number v-model="row.option_scroll_delay" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                    <span style="font-size: 14px; color: #666;">ms</span>
                                </div>

                            </div>
                        </template>

                    </div>
                </div>
            </div>
        </div>
    </div>

    <div class="panel panel-default" style="margin-bottom: 10px;">
        <div class="panel-heading" style="cursor: pointer; display: flex; justify-content: space-between; align-items: center;" data-toggle="collapse" data-target="#hoverSection">
            <div>
                <img src="/file/icon/mouse_over.svg" style="height: 23px">
                <span class="jd-effect-text">마우스 오버 애니메이션</span>
                <button class="jd-effect-btn">
                    <img :src="`/file/icon/${row.option_mouse_over}.svg`" style="width: 16px; height: 16px;">
                    <span class="fs-14">{{row.option_mouse_over}}</span>
                </button>
            </div>
            <i class="glyphicon glyphicon-chevron-down"></i>
        </div>
        <div id="hoverSection" class="panel-collapse collapse">
            <div class="panel-body">
                <div class="row" style="display: flex;">
                    <div class="col-md-6" style="border-right: 1px solid #ddd;">
                        <div class="jd-action-button" :class="{active : row.option_mouse_over == '효과없음'}" @click="row.option_mouse_over = '효과없음'">
                            <img src="/file/icon/효과없음.svg">
                            <span>효과없음</span>
                        </div>

                        <div class="jd-action-button" :class="{active : row.option_mouse_over == '페이드'}" @click="row.option_mouse_over_direction = 'center'; row.option_mouse_over = '페이드';">
                            <img src="/file/icon/페이드.svg">
                            <span>페이드</span>
                        </div>

                        <div class="jd-action-button" :class="{active : row.option_mouse_over == '줌인'}" @click="row.option_mouse_over_direction = 'center'; row.option_mouse_over = '줌인'">
                            <img src="/file/icon/줌인.svg">
                            <span>줌인</span>
                        </div>

                        <div class="jd-action-button" :class="{active : row.option_mouse_over == '줌아웃'}" @click="row.option_mouse_over_direction = 'center'; row.option_mouse_over = '줌아웃'">
                            <img src="/file/icon/줌아웃.svg">
                            <span>줌아웃</span>
                        </div>

                        <div class="jd-action-button" :class="{active : row.option_mouse_over == '플립'}" @click="row.option_mouse_over_direction = 'up'; row.option_mouse_over = '플립'">
                            <img src="/file/icon/플립.svg">
                            <span>플립</span>
                        </div>

                        <div class="jd-action-button" :class="{active : row.option_mouse_over == '테두리'}" @click="row.option_mouse_over = '테두리'">
                            <img src="/file/icon/테두리.svg">
                            <span>테두리</span>
                        </div>

                        <div class="jd-action-button" :class="{active : row.option_mouse_over == '그림자'}" @click="row.option_mouse_over = '그림자'">
                            <img src="/file/icon/그림자.svg">
                            <span>그림자</span>
                        </div>

                        <div class="jd-action-button" :class="{active : row.option_mouse_over == '색 오버레이'}" @click="row.option_mouse_over = '색 오버레이'">
                            <img src="/file/icon/색 오버레이.svg">
                            <span>색 오버레이</span>
                        </div>

                        <div class="jd-action-button" :class="{active : row.option_mouse_over == '텍스트'}" @click="row.option_mouse_over = '텍스트'">
                            <img src="/file/icon/텍스트.svg">
                            <span>텍스트</span>
                        </div>
                    </div>

                    <div class="col-md-6" style="display : flex; align-items: center;">
                        <template v-if="row.option_mouse_over == '효과없음'">
                            <div style="flex: 1; text-align: center; align-content: center;">
                                <span style="font-size: large; color : #959595; ">마우스오버 애니메이션을 선택하지 않았습니다.</span>
                            </div>
                        </template>

                        <template v-if="['페이드','줌인','줌아웃','플립'].includes(row.option_mouse_over)">
                            <div style="display: flex; flex-direction: column; gap: 15px; padding: 15px;">
                                <!-- 방향 -->
                                <div style="display: flex; align-items: center; gap: 10px;">
                                    <label style="margin: 0; font-weight: 600; font-size: 14px;">방향</label>
                                    <div style="display: flex; gap: 6px;">
                                        <button class="jd-direction-btn" v-if="row.option_mouse_over != '플립'" @click="row.option_mouse_over_direction = 'center'" :class="{active : row.option_mouse_over_direction == 'center'}">
                                            <i class="fas fa-circle" style="font-size: 12px;"></i>
                                        </button>
                                        <button class="jd-direction-btn" @click="row.option_mouse_over_direction = 'up'" :class="{active : row.option_mouse_over_direction == 'up'}">
                                            <i class="fas fa-arrow-up" style="font-size: 14px;"></i>
                                        </button>
                                        <button class="jd-direction-btn" @click="row.option_mouse_over_direction = 'down'" :class="{active : row.option_mouse_over_direction == 'down'}">
                                            <i class="fas fa-arrow-down" style="font-size: 14px;"></i>
                                        </button>
                                        <button class="jd-direction-btn" @click="row.option_mouse_over_direction = 'left'" :class="{active : row.option_mouse_over_direction == 'left'}">
                                            <i class="fas fa-arrow-left" style="font-size: 14px;"></i>
                                        </button>
                                        <button class="jd-direction-btn" @click="row.option_mouse_over_direction = 'right'" :class="{active : row.option_mouse_over_direction == 'right'}">
                                            <i class="fas fa-arrow-right" style="font-size: 14px;"></i>
                                        </button>
                                    </div>
                                </div>

                                <div style="display: flex; align-items: center; gap: 5px;">
                                    <label style="margin: 0; font-weight: 600; font-size: 14px;">지속시간</label>
                                    <input type="number" v-number v-model="row.option_mouse_over_duration" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                    <span style="font-size: 14px; color: #666;">ms</span>

                                    <label style="margin: 0; font-weight: 600; font-size: 14px; margin-left : 10px;">지연시간</label>
                                    <input type="number" v-number v-model="row.option_mouse_over_delay" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                    <span style="font-size: 14px; color: #666;">ms</span>
                                </div>

                            </div>
                        </template>

                        <template v-if="row.option_mouse_over == '테두리'">
                            <div>
                                <div style="display: flex; gap : 10px; align-items: center;">
                                    <span style="margin: 0; font-weight: 600; font-size: 14px;">테두리 색상</span>


                                    <div style="width: 30px; height: 30px; border-radius: 50%; overflow: hidden; cursor: pointer; border: 1px solid #ddd;">
                                        <input type="color"
                                               v-model="row.option_mouse_over_border_color"
                                               style="width: 150%; height: 150%; border: none; cursor: pointer; transform: translate(-17%, -17%);">
                                    </div>
                                </div>

                                <div style="display: flex; gap : 10px; align-items: center;">
                                    <span style="margin: 0; font-weight: 600; font-size: 14px;">테두리 굵기</span>
                                    <input type="number"
                                           class="form-control input-sm"
                                           v-model.number="row.option_mouse_over_border_width"
                                           min="1"
                                           style="width: 60px; display: inline-block;">
                                </div>
                            </div>
                        </template>

                        <template v-if="row.option_mouse_over == '그림자'">
                            <div style="display: flex; gap : 10px; align-items: center;">
                                <span style="margin: 0; font-weight: 600; font-size: 14px;">그림자 농도</span>
                                <select class="form-control input-sm" style="width: 80px; display: inline-block;" v-model="row.option_mouse_over_box_shadow_opacity">
                                    <option value="10">10%</option>
                                    <option value="20">20%</option>
                                    <option value="30">30%</option>
                                    <option value="40">40%</option>
                                    <option value="50">50%</option>
                                    <option value="60">60%</option>
                                    <option value="70">70%</option>
                                    <option value="80">80%</option>
                                    <option value="90">90%</option>
                                </select>
                            </div>
                        </template>

                        <template v-if="row.option_mouse_over == '색 오버레이'">
                            <div>
                                <div style="display: flex; gap : 10px; align-items: center;">
                                    <span style="margin: 0; font-weight: 600; font-size: 14px;">색상</span>

                                    <div style="width: 30px; height: 30px; border-radius: 50%; overflow: hidden; cursor: pointer; border: 1px solid #ddd;">
                                        <input type="color"
                                               v-model="row.option_mouse_over_background_color"
                                               style="width: 150%; height: 150%; border: none; cursor: pointer; transform: translate(-17%, -17%);">
                                    </div>
                                </div>

                                <div style="display: flex; gap : 10px; align-items: center;">
                                    <span style="margin: 0; font-weight: 600; font-size: 14px;">색 투명도</span>
                                    <select class="form-control input-sm" style="width: 80px; display: inline-block;" v-model="row.option_mouse_over_overlay_opacity">
                                        <option value="10">10%</option>
                                        <option value="20">20%</option>
                                        <option value="30">30%</option>
                                        <option value="40">40%</option>
                                        <option value="50">50%</option>
                                        <option value="60">60%</option>
                                        <option value="70">70%</option>
                                        <option value="80">80%</option>
                                        <option value="90">90%</option>
                                    </select>
                                </div>
                            </div>
                        </template>

                        <template v-if="row.option_mouse_over == '텍스트'">
                            <div style="display: flex; flex-direction: column; gap: 5px; padding: 15px; width: 100%;">
                                <div style="display: flex; gap : 5px; align-items: center;">
                                    <span style="margin: 0; font-weight: 600; font-size: 14px;">위치</span>
                                    <div class="jd-text-position-grid">
                                        <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'top-left'}" @click="row.option_mouse_over_text_position = 'top-left'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'top-center'}" @click="row.option_mouse_over_text_position = 'top-center'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'top-right'}" @click="row.option_mouse_over_text_position = 'top-right'"></div>

                                        <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'middle-left'}" @click="row.option_mouse_over_text_position = 'middle-left'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'middle-center'}" @click="row.option_mouse_over_text_position = 'middle-center'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'middle-right'}" @click="row.option_mouse_over_text_position = 'middle-right'"></div>

                                        <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'bottom-left'}" @click="row.option_mouse_over_text_position = 'bottom-left'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'bottom-center'}" @click="row.option_mouse_over_text_position = 'bottom-center'"></div>
                                        <div class="jd-position-cell" :class="{active: row.option_mouse_over_text_position === 'bottom-right'}" @click="row.option_mouse_over_text_position = 'bottom-right'"></div>
                                    </div>

                                    <span style="margin: 0; font-weight: 600; font-size: 14px;">텍스트 여백</span>
                                    <div style="max-width: 190px;">
                                        <div class="text-center" style="margin-bottom: 5px;">
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_mouse_over_text_padding_top" style="width: 60px; display: inline-block;">
                                        </div>
                                        <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;">
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_mouse_over_text_padding_left" style="width: 60px;">
                                            <div style="width: 60px; height: 30px; background: #f5f5f5; border: 1px solid #ddd;"></div>
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_mouse_over_text_padding_right" style="width: 60px;">
                                        </div>
                                        <div class="text-center">
                                            <input type="text" v-number class="form-control input-sm" v-model="row.option_mouse_over_text_padding_bottom" style="width: 60px; display: inline-block;">
                                        </div>
                                    </div>
                                </div>

                                <div style="display:flex; align-items: center; gap: 10px;">
                                    <span style="margin: 0; font-weight: 600; font-size: 14px;">전환 애니메이션</span>
                                    <select class="form-control input-sm" style="width: 80px; display: inline-block; flex : 1" v-model="row.option_mouse_over_text_animation">
                                        <option value="">없음</option>
                                        <option value="페이드">페이드</option>
                                        <option value="슬라이드">슬라이드</option>
                                        <option value="플립">플립</option>
                                    </select>
                                </div>
                            </div>
                        </template>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</script>

<script>
    JayDream_components.push({name : "design-widget-multi-modal-tab4",object : {
            template: "#design-widget-multi-modal-tab4-template",
            props: {
                row : {type : Object, default : null},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-multi-modal-tab4",
                    component_idx: "",

                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {

            },
            computed: {

            },
            watch: {

            }
        }});
</script>
<script type="text/x-template" id="design-widget-multi-modal-template">
    <div v-if="load" class="jaydream" style="font-size:14px;">
        <div>
            <design-widget-multi-modal-tab1 :row :site_code></design-widget-multi-modal-tab1>
            <hr>
            <design-widget-multi-modal-tab2 :row></design-widget-multi-modal-tab2>
            <hr>
            <design-widget-multi-modal-tab3 :row></design-widget-multi-modal-tab3>
            <hr>
            <design-widget-multi-modal-tab4 :row></design-widget-multi-modal-tab4>
        </div>

        <div class="modal-footer" style="position:sticky;right:0;bottom:0;">
            <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
            <button type="button" class="btn btn-orange save" @click="postData()">저장</button>
        </div>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-multi-modal",object : {
            template: "#design-widget-multi-modal-template",
            props: {
                primary : {type : String, default : ""},
                widget_code : {type : String, default : "1"},
                site_code : {type : String, default : "st68f18a193702e"},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-multi-modal",
                    component_idx: "",

                    table : null,
                    row: {
                        widget_code : this.widget_code,

                        images : [],
                        upload_file : [], // 사용안하고 업로드만 체크하는요

                        user_idx : "",

                        /* tab2 */
                        // 한줄당 이미지
                        line_image_pc : 4,
                        line_image_mobile : 2,
                        //  이미지 사이 간격
                        gap_x : 0,
                        gap_y : 0,
                        // 이미지 비율
                        ratio_type : "fixed",
                        ratio_value : "1:1",
                        // 더보기 버튼
                        more_btn : false,
                        more_btn_line : "2",

                        /* tab3 */
                        // 그림자
                        box_shadow_use : false,
                        box_shadow_opacity : "20",
                        // 테두리
                        border_use : false,
                        border_width : "1",
                        border_color : "#000000",
                        //모서리둥글게
                        border_radius_use : false,
                        border_radius_px : "6",
                        // 마우스클릭동작
                        mouse_click : "동작없음",

                        /* tab4 */
                        // 텍스트
                        option_text : "사용안함",
                        option_text_content : "더블 클릭하여 텍스트를 수정해주세요.",
                        option_text_position : "middle-center",
                        option_text_background : "transparent",
                        option_text_margin_top : "0",
                        option_text_margin_right : "0",
                        option_text_margin_bottom : "0",
                        option_text_margin_left : "0",
                        option_text_mobile_margin_top : "0",
                        option_text_mobile_margin_right : "0",
                        option_text_mobile_margin_bottom : "0",
                        option_text_mobile_margin_left : "0",
                        // 스크롤
                        option_scroll : "효과없음",
                        option_scroll_direction : "center",
                        option_scroll_duration : "1000",
                        option_scroll_delay : "0",
                        // 마우스 오버
                        // 페이드~ 플립
                        option_mouse_over : "효과없음",
                        option_mouse_over_direction : "center",
                        option_mouse_over_duration : "1000",
                        option_mouse_over_delay : "0",
                        //테두리
                        option_mouse_over_border_color : "#000000",
                        option_mouse_over_border_width : "1",
                        //그림자
                        option_mouse_over_box_shadow_opacity : "20",
                        //색 오버레이
                        option_mouse_over_background_color : "#000000",
                        option_mouse_over_overlay_opacity: 50,
                        //텍스트
                        option_mouse_over_text_position : "middle-center",
                        option_mouse_over_text_animation : "",
                        option_mouse_over_text_padding_top : 0,
                        option_mouse_over_text_padding_left : 0,
                        option_mouse_over_text_padding_right : 0,
                        option_mouse_over_text_padding_bottom : 0,
                    },

                    rows : [],
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                // await this.table.get(this.rows,{paging : 10})

                this.sessions = await this.session.all();
                this.row.user_idx = this.sessions.mem_idx;

                this.table = await this.api.table("jd_widget_multi");
                this.table.where('widget_code',this.widget_code);
                let row = await this.table.get();
                if(row.length > 0) {
                    this.row = row[0];
                    
                    // 실제 사용하지 않는 컬럼이므로 미리 지정
                    this.row.upload_file = [];
                }
                this.normalizeTextMobileMargins();

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {
                normalizeTextMobileMargins() {
                    const defaults = {
                        option_text_mobile_margin_top: "0",
                        option_text_mobile_margin_right: "0",
                        option_text_mobile_margin_bottom: "0",
                        option_text_mobile_margin_left: "0",
                    };

                    Object.keys(defaults).forEach((key) => {
                        if(this.row[key] === undefined || this.row[key] === null || this.row[key] === '') {
                            this.row[key] = defaults[key];
                        }
                    });
                },
                async saveImageFile(file, memo) {
                    const res = await this.lib.ajax("file_save", {
                        "user_idx": this.row.user_idx,
                        "upload_file": file,
                        "width": file.width,
                        "height": file.height,
                        "memo": memo,
                        "widget_code": this.row.widget_code,
                        "site_code": this.site_code,
                    });

                    return res['file'];
                },
                clearReplaceUploadFile(item) {
                    if(!item) return;
                    delete item._replace_upload_file;
                },
                async saveReplaceImageFiles(items, memo) {
                    if(!Array.isArray(items)) return;

                    for(const item of items) {
                        if(!item || !item._replace_upload_file) continue;

                        const savedFile = await this.saveImageFile(item._replace_upload_file, memo);
                        item.src = savedFile.src;
                        this.clearReplaceUploadFile(item);
                    }
                },
                async postData() {
                    window.design.loadingOn()

                    await this.saveReplaceImageFiles(this.row.images, "jd_widget_multi");

                    // 업로드 파일 저장
                    for(const [idx, file] of this.row.upload_file.entries()) {
                        const uploadFile = file._replace_upload_file || file;
                        let savedFile = await this.saveImageFile(uploadFile, "jd_widget_multi");

                        // 기존 file 객체에서 _type과 upload 관련 제거하고 src만 교체
                        let newImage = { ...file };
                        newImage.src = savedFile.src;
                        this.clearReplaceUploadFile(newImage);

                        // images 배열에 추가
                        this.row.images.push(newImage);
                    }

                    // upload_file 비우기
                    this.row.upload_file = [];

                    await this.table.post(this.row,{return : true});

                    window.design.loadingOff()
                    window.JAYDREAM_VUE_GLOBAL[this.widget_code].reload = true;
                    $(this.$el).closest('.modal').modal('hide');
                }
            },
            computed: {

            },
            watch: {
                async "row.ratio_value"(value, old_value) {
                    if(value == '원형') this.row.border_radius_use = false
                }
            }
        }});
</script>
<style>
    .jd-image-modal-title {
        font-weight: bold;
        margin-bottom: 15px;
        font-size: 16px;
    }


    .design-widget-multi-modal-thumbnail {
        transition: transform 0.2s;
    }

    .design-widget-multi-modal-thumbnail:hover {
        transform: scale(1.05);
    }

    .design-widget-multi-modal-btn-delete {
        opacity: 0;
        transition: opacity 0.2s;
    }

    .design-widget-multi-modal-thumbnail:hover .design-widget-multi-modal-btn-delete {
        opacity: 1;
    }

    .design-widget-multi-modal-btn-edit {
        color: #e64d11 !important;
        margin-top: 0 !important;
    }

    .design-widget-multi-modal-btn-edit:hover {
        color: #c43d0a !important;
    }

    .jd-text-section-row {
        display: flex;
        align-items: stretch;
        min-height: 170px;
    }

    .jd-text-option-list {
        flex: 0 0 360px;
        max-width: 360px;
        border-right: 1px solid #ddd;
        padding-right: 14px;
        display: flex;
        gap: 10px;
        align-items: flex-start;
        flex-wrap: wrap;
        align-content: flex-start;
    }

    .jd-text-setting-panel {
        flex: 1 1 auto;
        min-width: 0;
        padding-left: 16px;
        display: flex;
        align-items: center;
    }

    .jd-text-overlay-setting {
        display: flex;
        align-items: flex-start;
        gap: 28px;
        flex-wrap: nowrap;
        width: 100%;
    }

    .jd-text-position-setting {
        flex: 0 0 auto;
    }

    .jd-text-help {
        color: #999;
        font-size: 12px;
        margin: 0 0 10px;
    }

    .jd-text-margin-table {
        display: grid;
        grid-template-columns: 76px repeat(4, 58px);
        gap: 8px 10px;
        align-items: center;
    }

    .jd-text-margin-table-standalone {
        justify-content: flex-start;
    }

    .jd-text-margin-table-header {
        display: contents;
    }

    .jd-text-margin-table-header span {
        font-weight: bold;
        font-size: 13px;
        text-align: center;
    }

    .jd-text-margin-table-row {
        display: contents;
    }

    .jd-text-margin-table-label {
        font-weight: bold;
        white-space: nowrap;
    }

    .jd-text-margin-table input {
        width: 58px;
        text-align: center;
    }

    @media (max-width: 980px) {
        .jd-text-section-row {
            flex-direction: column;
        }

        .jd-text-option-list {
            flex: 0 0 auto;
            max-width: none;
            border-right: none;
            border-bottom: 1px solid #ddd;
            padding-right: 0;
            padding-bottom: 12px;
            margin-bottom: 12px;
        }

        .jd-text-setting-panel {
            padding-left: 0;
        }
    }

    .number-input-wrapper {
        position: relative;
        width: 80px;
    }

    .number-input-wrapper input {
        width: 100%;
        padding-left: 24px;   /* 왼쪽 아이콘 공간 */
        padding-right: 24px;  /* 오른쪽 스테퍼 공간 */
        text-align: center;
        font-size: 14px;
    }

    .input-icon-left {
        position: absolute;
        left: 6px;
        top: 50%;
        transform: translateY(-50%);
        width: 14px;
        height: 14px;
        pointer-events: none;
    }

    .number-stepper {
        position: absolute;
        right: 4px;
        top: 50%;
        transform: translateY(-50%);
        display: flex;
        flex-direction: column;
        gap: 0;
    }

    .number-stepper button {
        border: none;
        background: none;
        padding: 0;
        margin: 0;
        cursor: pointer;
        line-height: 1;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 14px;
        height: 10px;
    }

    .number-stepper button:hover svg path {
        fill: #333;
    }

    .number-stepper button:active svg path {
        fill: #999;
    }

    .number-stepper svg {
        display: block;
    }

    .panel-default > .panel-heading {
        background-image: linear-gradient(to bottom, #ffffff 0%, #ffffff 100%) !important;
    }
</style>
<script type="text/x-template" id="design-widget-multi-template">
    <div v-if="load">
        <template v-if="row.idx">
            <!-- 모바일 -->
            <template v-if="isMobileView()" >
                <!-- original -->
                <template v-if="row.ratio_type === 'original'">
                    <div class="design-widget-multi-display-mobile"
                         :style="{ columnCount: row.line_image_mobile, columnGap: row.gap_x + 'px' }">
                        <div v-for="(image, index) in sortedImages" :key="index"
                             :style="{ marginBottom: row.gap_y + 'px', breakInside: 'avoid' }"
                             @mouseenter="setHover(image, true)" @mouseleave="setHover(image, false)">
                            <div :style="getImageDivStyle(image)">
                                <div class="design-widget-multi-display-image-wrapper" :style="imageWrapperStyle" @click="handleImageClick(image)">
                                    <img :src="getImageSrc(image)" alt="" style="position: static; height: auto; width: 100%;" :title="image.alt_text || ''">

                                    <template v-if="row.option_text == '이미지 겹침' && image.each_text_content">
                                        <div :style="getTextStyle()">
                                            <div v-html="image.each_text_content" class="jd-image-text-content"></div>
                                        </div>
                                    </template>

                                    <div v-if="row.option_mouse_over == '색 오버레이' && image._hover"
                                         :style="getOverlayStyle()"
                                         style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;">
                                    </div>

                                    <template v-if="row.option_mouse_over == '텍스트' && image._hover && image.each_over_content">
                                        <div :style="getHoverTextPositionStyle()">
                                            <div :style="getHoverTextAnimationStyle()">
                                                <span v-html="image.each_over_content"></span>
                                            </div>
                                        </div>
                                    </template>
                                </div>

                                <template v-if="row.option_text == '이미지 하단' && image.each_text_content">
                                    <div :style="getTextStyle()">
                                        <div v-html="image.each_text_content" class="jd-image-text-content"></div>
                                    </div>
                                </template>
                            </div>
                        </div>
                    </div>
                </template>

                <!-- 기존 flex -->
                <template v-else>
                    <div class="design-widget-multi-display-mobile">
                        <div class="design-widget-multi-display-row" v-for="(lineImages, lineIndex) in mobileImageLines" :key="lineIndex"
                             :style="getImageRowStyle(row.line_image_mobile, lineIndex, mobileImageLines.length)">
                            <div class="design-widget-multi-display-item"
                                 v-for="(image, imageIndex) in lineImages"
                                 :key="imageIndex"
                                 :style="getImageItemStyle(row.line_image_mobile)">

                                <div ref="imageElement" @mouseenter="setHover(image, true)" @mouseleave="setHover(image, false)">
                                    <div :style="getImageDivStyle(image)">
                                        <div class="design-widget-multi-display-image-wrapper" :style="imageWrapperStyle" @click="handleImageClick(image)">
                                            <img :src="getImageSrc(image)" alt="" :style="row.ratio_type === 'original' ? { position: 'static', height: 'auto' } : {}" :title="image.alt_text || ''">

                                            <template v-if="row.option_text == '이미지 겹침' && image.each_text_content">
                                                <div :style="getTextStyle()">
                                                    <div v-html="image.each_text_content" class="jd-image-text-content"></div>
                                                </div>
                                            </template>

                                            <div v-if="row.option_mouse_over == '색 오버레이' && image._hover"
                                                 :style="getOverlayStyle()"
                                                 style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;">
                                            </div>

                                            <template v-if="row.option_mouse_over == '텍스트' && image._hover && image.each_over_content">
                                                <div :style="getHoverTextPositionStyle()">
                                                    <div :style="getHoverTextAnimationStyle()">
                                                        <span v-html="image.each_over_content"></span>
                                                    </div>
                                                </div>
                                            </template>
                                        </div>

                                        <template v-if="row.option_text == '이미지 하단' && image.each_text_content">
                                            <div :style="getTextStyle()">
                                                <div v-html="image.each_text_content" class="jd-image-text-content"></div>
                                            </div>
                                        </template>
                                    </div>
                                </div>

                            </div>
                        </div>

                        <div class="design-widget-multi-display-more-btn" v-if="showMoreBtn">
                            <button class="btn btn-default btn-block" @click="showAll = !showAll">
                                {{ showAll ? '접기' : '더보기' }}
                                <img src="/file/icon/stroke.svg"
                                     :style="{ transform: showAll ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform 0.3s', width: '12px' }">
                            </button>
                        </div>
                    </div>
                </template>
            </template>

            <!-- PC -->
            <template v-else>
                <!-- original -->
                <template v-if="row.ratio_type === 'original'">
                    <div class="design-widget-multi-display-pc"
                         :style="{ columnCount: row.line_image_pc, columnGap: row.gap_x + 'px' }">
                        <div v-for="(image, index) in sortedImages" :key="index"
                             :style="{ marginBottom: row.gap_y + 'px', breakInside: 'avoid' }"
                             @mouseenter="setHover(image, true)" @mouseleave="setHover(image, false)">
                            <div :style="getImageDivStyle(image)">
                                <div class="design-widget-multi-display-image-wrapper" :style="imageWrapperStyle" @click="handleImageClick(image)">
                                    <img :src="getImageSrc(image)" alt="" style="position: static; height: auto; width: 100%;" :title="image.alt_text || ''">

                                    <template v-if="row.option_text == '이미지 겹침' && image.each_text_content">
                                        <div :style="getTextStyle()">
                                            <div v-html="image.each_text_content" class="jd-image-text-content"></div>
                                        </div>
                                    </template>

                                    <div v-if="row.option_mouse_over == '색 오버레이' && image._hover"
                                         :style="getOverlayStyle()"
                                         style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;">
                                    </div>

                                    <template v-if="row.option_mouse_over == '텍스트' && image._hover && image.each_over_content">
                                        <div :style="getHoverTextPositionStyle()">
                                            <div :style="getHoverTextAnimationStyle()">
                                                <span v-html="image.each_over_content"></span>
                                            </div>
                                        </div>
                                    </template>
                                </div>

                                <template v-if="row.option_text == '이미지 하단' && image.each_text_content">
                                    <div :style="getTextStyle()">
                                        <div v-html="image.each_text_content" class="jd-image-text-content"></div>
                                    </div>
                                </template>
                            </div>
                        </div>
                    </div>
                </template>

                <!-- 기존 flex -->
                <template v-else>
                    <div class="design-widget-multi-display-pc">
                        <div class="design-widget-multi-display-row" v-for="(lineImages, lineIndex) in pcImageLines" :key="lineIndex"
                             :style="getImageRowStyle(row.line_image_pc, lineIndex, pcImageLines.length)">
                            <div class="design-widget-multi-display-item"
                                 v-for="(image, imageIndex) in lineImages"
                                 :key="imageIndex"
                                 :style="getImageItemStyle(row.line_image_pc)">

                                <div ref="imageElement" @mouseenter="setHover(image, true)" @mouseleave="setHover(image, false)">
                                    <div :style="getImageDivStyle(image)">
                                        <div class="design-widget-multi-display-image-wrapper" :style="imageWrapperStyle" @click="handleImageClick(image)">
                                            <img :src="getImageSrc(image)" alt="" :style="row.ratio_type === 'original' ? { position: 'static', height: 'auto' } : {}" :title="image.alt_text || ''">

                                            <template v-if="row.option_text == '이미지 겹침' && image.each_text_content">
                                                <div :style="getTextStyle()">
                                                    <div v-html="image.each_text_content" class="jd-image-text-content"></div>
                                                </div>
                                            </template>

                                            <div v-if="row.option_mouse_over == '색 오버레이' && image._hover"
                                                 :style="getOverlayStyle()"
                                                 style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;">
                                            </div>

                                            <template v-if="row.option_mouse_over == '텍스트' && image._hover && image.each_over_content">
                                                <div :style="getHoverTextPositionStyle()">
                                                    <div :style="getHoverTextAnimationStyle()">
                                                        <span v-html="image.each_over_content"></span>
                                                    </div>
                                                </div>
                                            </template>
                                        </div>

                                        <template v-if="row.option_text == '이미지 하단' && image.each_text_content">
                                            <div :style="getTextStyle()">
                                                <div v-html="image.each_text_content" class="jd-image-text-content"></div>
                                            </div>
                                        </template>
                                    </div>
                                </div>

                            </div>
                        </div>

                        <div class="design-widget-multi-display-more-btn" v-if="showMoreBtn">
                            <button class="btn btn-default btn-block" @click="showAll = !showAll">
                                {{ showAll ? '접기' : '더보기' }}
                                <img src="/file/icon/stroke.svg"
                                     :style="{ transform: showAll ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform 0.3s', width: '12px' }">
                            </button>
                        </div>
                    </div>
                </template>
            </template>
        </template>

        <template v-else>
            <div class="jd-imageupload-box jd-widget-placeholder">
                <img class="jd-widget-placeholder-icon" src="/file/icon/widget-add/multi.svg" alt="">
                <p class="jd-imageupload-text">더블 클릭하여 멀티 이미지를 추가하세요.</p>
            </div>
        </template>

        <div v-if="viewImage" class="widget-image-overlay" @click="viewImage = ''">
            <img :src="viewImage" @click.stop >
            <button class="widget-overlay-close" @click="viewImage = ''">&times;</button>
        </div>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-multi",object : {
            template: "#design-widget-multi-template",
            props: {
                primary : {type : String, default : ""},
                widget_code : {type : String, default : ""},
                user_idx : {type : String, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-multi",
                    component_idx: "",

                    table : null,
                    row: {},
                    rows : [],

                    showAll: false,

                    viewImage : "",
                    scrollEvent: false, // 🔥 추가
                    observer: null, // 🔥 추가
                    viewportWidth : typeof window !== "undefined" ? window.innerWidth : 0,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_multi");
                this.table.where('widget_code',this.widget_code);
                let row = await this.table.get();
                if(row.length > 0) this.row = row[0];
                this.normalizeTextMobileMargins();

                this.handleResize = () => {
                    this.viewportWidth = window.innerWidth;
                };
                window.addEventListener("resize", this.handleResize);

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                    if(!this.row.idx) $(`#${this.widget_code} .firewall`).addClass('widget_setting_btn');

                    this.initScrollObserver(); // 🔥 추가
                });
            },
            updated() {

            },
            beforeUnmount() {
                window.removeEventListener("resize", this.handleResize);
            },
            methods: {
                isMobileView() {
                    return this.lib.isMobile()
                        || document.body.classList.contains('_mbv')
                        || this.viewportWidth <= 768;
                },
                normalizeTextMobileMargins() {
                    const defaults = {
                        option_text_mobile_margin_top: "0",
                        option_text_mobile_margin_right: "0",
                        option_text_mobile_margin_bottom: "0",
                        option_text_mobile_margin_left: "0",
                    };

                    Object.keys(defaults).forEach((key) => {
                        if(this.row[key] === undefined || this.row[key] === null || this.row[key] === '') {
                            this.row[key] = defaults[key];
                        }
                    });
                },

                setHover(image, value) {
                    image._hover = value;
                },

                getImageSrc(image) {
                    if(this.row.option_mouse_over == '이미지 전환' && image._hover) {
                        return this.row.option_mouse_over_image_src;
                    }
                    return image.src;
                },

                handleImageClick(image) {
                    if (this.row.mouse_click === '동작없음') {
                        return;
                    }

                    if (this.row.mouse_click === '링크 이동') {
                        const url = image.option_mouse_click_url || '';
                        const target = image.option_mouse_click_target || '_self';
                        if(url) this.lib.href(url, target);
                    }

                    if (this.row.mouse_click === '크게 보기') {
                        this.viewImage = image.src;
                    }
                },

                getImageDivStyle(image) {
                    let style = {};

                    if(this.row.mouse_click != "동작없음") {
                        style.cursor = 'pointer';
                    }

                    // 스크롤 애니메이션 (공통)
                    if(this.row.option_scroll !== '효과없음' && this.scrollEvent) {
                        const effect = this.row.option_scroll;
                        const direction = this.row.option_scroll_direction || 'center';
                        const duration = parseInt(this.row.option_scroll_duration) || 1000;
                        const delay = parseInt(this.row.option_scroll_delay) || 0;

                        const animationName = effect === '페이드' ? 'fade' :
                            effect === '줌인' ? 'zoom-in' :
                                effect === '줌아웃' ? 'zoom-out' :
                                    effect === '플립' ? 'flip' : 'fade';

                        style.animation = `${animationName}-${direction} ${duration}ms ease ${delay}ms both`;
                    }

                    // 마우스 오버 애니메이션 (공통 - 페이드~플립)
                    if(['페이드','줌인','줌아웃','플립'].includes(this.row.option_mouse_over) && image._hover) {
                        const effect = this.row.option_mouse_over;
                        const direction = this.row.option_mouse_over_direction || 'center';
                        const duration = parseInt(this.row.option_mouse_over_duration) || 1000;
                        const delay = parseInt(this.row.option_mouse_over_delay) || 0;

                        const animationName = effect === '페이드' ? 'fade' :
                            effect === '줌인' ? 'zoom-in' :
                                effect === '줌아웃' ? 'zoom-out' :
                                    effect === '플립' ? 'flip' : 'fade';

                        style.animation = `${animationName}-${direction} ${duration}ms ease ${delay}ms both`;
                    }

                    // 호버 스타일 (공통 - 테두리, 그림자)
                    if(image._hover) {
                        if(this.row.option_mouse_over == '테두리') {
                            style.outline = `${this.row.option_mouse_over_border_width}px solid ${this.row.option_mouse_over_border_color}`;
                        }

                        if(this.row.option_mouse_over == '그림자') {
                            style.boxShadow = `0px 4px 8px rgba(0, 0, 0, ${this.row.option_mouse_over_box_shadow_opacity / 100})`;
                        }
                    }

                    return style;
                },

                getTextStyle() {
                    let style = {};

                    style.fontSize = "16px";
                    style.background = this.row.option_text_background;
                    style.maxWidth = "100%";

                    const margins = this.getTextMargins();
                    const marginTop = margins.top;
                    const marginRight = margins.right;
                    const marginBottom = margins.bottom;
                    const marginLeft = margins.left;

                    style.marginTop = marginTop + "px";
                    style.marginRight = marginRight + "px";
                    style.marginBottom = marginBottom + "px";
                    style.marginLeft = marginLeft + "px";

                    if(this.row.option_text == "이미지 겹침") {
                        style.position = 'absolute';
                        style.left = `${marginLeft}px`;
                        style.right = `${marginRight}px`;
                        style.width = 'auto';
                        style.maxWidth = 'none';
                        style.boxSizing = 'border-box';
                        style.margin = '0';

                        const position = this.row.option_text_position || 'middle-center';
                        const verticalPosition = position.split('-')[0];
                        const horizontalPosition = position.split('-')[1];

                        if(horizontalPosition === 'center') {
                            style.textAlign = 'center';
                        } else if(horizontalPosition === 'right') {
                            style.textAlign = 'right';
                        } else {
                            style.textAlign = 'left';
                        }

                        if(verticalPosition === 'top') {
                            style.top = `${marginTop}px`;
                        } else if(verticalPosition === 'middle') {
                            const verticalOffset = marginTop - marginBottom;
                            style.top = '50%';
                            style.transform = verticalOffset
                                ? `translateY(calc(-50% ${verticalOffset > 0 ? '+' : '-'} ${Math.abs(verticalOffset)}px))`
                                : 'translateY(-50%)';
                        } else if(verticalPosition === 'bottom') {
                            style.bottom = `${marginBottom}px`;
                        }
                    }

                    return style;
                },
                getTextMargins() {
                    const prefix = this.isMobileView() ? 'option_text_mobile_margin_' : 'option_text_margin_';

                    return {
                        top: parseInt(this.row[prefix + 'top'], 10) || 0,
                        right: parseInt(this.row[prefix + 'right'], 10) || 0,
                        bottom: parseInt(this.row[prefix + 'bottom'], 10) || 0,
                        left: parseInt(this.row[prefix + 'left'], 10) || 0,
                    };
                },

                getOverlayStyle() {
                    const color = this.row.option_mouse_over_background_color;
                    const opacity = this.row.option_mouse_over_overlay_opacity / 100;

                    const r = parseInt(color.slice(1, 3), 16);
                    const g = parseInt(color.slice(3, 5), 16);
                    const b = parseInt(color.slice(5, 7), 16);

                    return {
                        backgroundColor: `rgba(${r}, ${g}, ${b}, ${opacity})`
                    };
                },

                getHoverTextPositionStyle() {
                    let style = {};
                    style.position = 'absolute';

                    const position = this.row.option_mouse_over_text_position;

                    const mt = parseInt(this.row.option_mouse_over_text_margin_top) || 0;
                    const mr = parseInt(this.row.option_mouse_over_text_margin_right) || 0;
                    const mb = parseInt(this.row.option_mouse_over_text_margin_bottom) || 0;
                    const ml = parseInt(this.row.option_mouse_over_text_margin_left) || 0;

                    style.padding = `${mt}px ${mr}px ${mb}px ${ml}px`;

                    if(position === 'top-left') {
                        style.top = '0';
                        style.left = '0';
                    } else if(position === 'top-center') {
                        style.top = '0';
                        style.left = '50%';
                        style.transform = 'translateX(-50%)';
                    } else if(position === 'top-right') {
                        style.top = '0';
                        style.right = '0';
                    } else if(position === 'middle-left') {
                        style.top = '50%';
                        style.left = '0';
                        style.transform = 'translateY(-50%)';
                    } else if(position === 'middle-center') {
                        style.top = '50%';
                        style.left = '50%';
                        style.transform = 'translate(-50%, -50%)';
                    } else if(position === 'middle-right') {
                        style.top = '50%';
                        style.right = '0';
                        style.transform = 'translateY(-50%)';
                    } else if(position === 'bottom-left') {
                        style.bottom = '0';
                        style.left = '0';
                    } else if(position === 'bottom-center') {
                        style.bottom = '0';
                        style.left = '50%';
                        style.transform = 'translateX(-50%)';
                    } else if(position === 'bottom-right') {
                        style.bottom = '0';
                        style.right = '0';
                    }

                    return style;
                },

                getHoverTextAnimationStyle() {
                    let style = {};

                    if(this.row.option_mouse_over_text_animation) {
                        const animation = this.row.option_mouse_over_text_animation;
                        const duration = 500;

                        if(animation === '페이드') {
                            style.animation = `hover-text-fade ${duration}ms ease both`;
                        } else if(animation === '슬라이드') {
                            style.animation = `hover-text-slide ${duration}ms ease both`;
                        } else if(animation === '플립') {
                            style.animation = `hover-text-flip ${duration}ms ease both`;
                        }
                    }

                    return style;
                },

                initScrollObserver() {
                    this.observer = new IntersectionObserver((entries) => {
                        entries.forEach(entry => {
                            if (entry.isIntersecting) {
                                this.scrollEvent = true;
                            } else {
                                this.scrollEvent = false;
                            }
                        });
                    }, {
                        threshold: 0.1
                    });

                    if(this.$refs.imageElement && this.$refs.imageElement.length) {
                        this.$refs.imageElement.forEach(el => {
                            this.observer.observe(el);
                        });
                    }
                },
                handleImageClick(item) {
                    console.log(item)
                    if (this.row.mouse_click === '동작없음') {
                        // 아무 동작 안함
                        return;
                    }

                    if (this.row.mouse_click === '링크 이동') {
                        if(!item.option_mouse_click_url) return;

                        this.lib.href(item.option_mouse_click_url,item.option_mouse_click_target)
                    }

                    if (this.row.mouse_click === '크게 보기') {
                        this.viewImage = item.src;
                    }
                },
                getImageGapX() {
                    return Math.max(0, parseInt(this.row.gap_x, 10) || 0);
                },
                getImageGapY() {
                    return Math.max(0, parseInt(this.row.gap_y, 10) || 0);
                },
                getImageColumnCount(columns) {
                    return Math.max(1, parseInt(columns, 10) || 1);
                },
                getImageRowStyle(columns, lineIndex, totalLines) {
                    const gapX = this.getImageGapX();
                    const gapY = this.getImageGapY();

                    return {
                        columnGap: `${gapX}px`,
                        marginBottom: lineIndex < totalLines - 1 ? `${gapY}px` : '0'
                    };
                },
                getImageItemStyle(columns) {
                    const gapX = this.getImageGapX();
                    const columnCount = this.getImageColumnCount(columns);
                    const totalGap = gapX * (columnCount - 1);

                    return {
                        width: `calc((100% - ${totalGap}px) / ${columnCount})`
                    };
                }
            },
            computed: {
                appState() {
                    return window.JAYDREAM_VUE_GLOBAL[this.$app_name];
                },
                // 🔥 priority로 정렬된 이미지
                sortedImages() {
                    const images = this.row.images || [];
                    return [...images].sort((a, b) => {
                        const aPriority = a.priority ?? Infinity;
                        const bPriority = b.priority ?? Infinity;
                        return aPriority - bPriority;
                    });
                },

                // 🔥 PC 이미지 줄 나누기
                pcImageLines() {
                    const lines = [];
                    const perLine = parseInt(this.row.line_image_pc);
                    const images = this.sortedImages;

                    for (let i = 0; i < images.length; i += perLine) {
                        lines.push(images.slice(i, i + perLine));
                    }

                    if (this.row.more_btn && this.row.more_btn_line && !this.showAll) {
                        return lines.slice(0, parseInt(this.row.more_btn_line));
                    }

                    return lines;
                },

                // 🔥 모바일 이미지 줄 나누기
                mobileImageLines() {
                    const lines = [];
                    const perLine = parseInt(this.row.line_image_mobile);
                    const images = this.sortedImages;

                    for (let i = 0; i < images.length; i += perLine) {
                        lines.push(images.slice(i, i + perLine));
                    }

                    // 🔥 더보기 버튼이 있고 전체보기가 아니면 제한된 줄만 반환
                    if (this.row.more_btn && this.row.more_btn_line && !this.showAll) {
                        return lines.slice(0, parseInt(this.row.more_btn_line));
                    }

                    return lines;
                },

                // 🔥 더보기 버튼 표시 여부
                showMoreBtn() {
                    if (!this.row.more_btn) return false;

                    const maxLines = this.row.more_btn_line;
                    const images = this.sortedImages;

                    if (this.lib.isMobile()) {
                        const totalLines = Math.ceil(images.length / this.row.line_image_mobile);
                        return totalLines > maxLines;
                    } else {
                        const totalLines = Math.ceil(images.length / this.row.line_image_pc);
                        return totalLines > maxLines;
                    }
                },
                // 🔥 이미지 래퍼 스타일
                imageWrapperStyle() {
                    let style = {};

                    if (this.row.ratio_type === 'original') {
                        style.height = 'auto';
                        style.paddingBottom = '0';
                    } else if (this.row.ratio_type === 'fixed' && this.row.ratio_value) {
                        if (this.row.ratio_value === '원형') {
                            style.paddingBottom = '100%';
                            style.borderRadius = '50%';
                        } else {
                            const [width, height] = this.row.ratio_value.split(':').map(Number);
                            const paddingBottom = (height / width) * 100;
                            style.paddingBottom = `${paddingBottom}%`;
                        }
                    } else {
                        style.paddingBottom = '75%';
                    }

                    if (this.row.border_use) {
                        style.border = `${this.row.border_width}px solid ${this.row.border_color}`;
                    }
                    if (this.row.border_radius_use) {
                        style.borderRadius = `${this.row.border_radius_px}px`;
                    }
                    if (this.row.box_shadow_use) {
                        const opacity = this.row.box_shadow_opacity / 100;
                        style.boxShadow = `0 2px 8px rgba(0, 0, 0, ${opacity})`;
                    }
                    if (this.row.mouse_click !== '동작없음') {
                        style.cursor = 'pointer';
                    }

                    return style;
                },
            },
            watch: {
                appState: {
                    async handler(newVal) {
                        if(newVal.reload) {
                            let row = await this.table.get();
                            if(row.length > 0) this.row = row[0];
                            this.appState.reload = false;
                        }
                    },
                    deep: true
                }
            }
        }});
</script>

<style>
    .design-widget-multi-display-pc,
    .design-widget-multi-display-mobile {
        width: 100%;
    }

    .design-widget-multi-display-row {
        display: flex;
    }

    .design-widget-multi-display-item {
        box-sizing: border-box;
    }

    .design-widget-multi-display-image-wrapper {
        position: relative;
        width: 100%;
        overflow: hidden;
        background: #f5f5f5;
    }

    .design-widget-multi-display-image-wrapper img {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

    .design-widget-multi-display-more-btn {
        text-align: center;
        margin: 30px 0;
    }

    .design-widget-multi-display-more-btn .btn {
        width: 100%;
        height: 40px;
        font-size: 16px;
        color: #222222;
        background-color: #FFFFFF;
        border: 1px solid #DDDDDD;
        border-radius: 4px;
        padding: 0;
        line-height: 38px;
    }

    .design-widget-multi-display-more-btn .btn:hover {
        background-color: #f8f8f8;
    }

    .design-widget-multi-display-more-btn .btn:focus,
    .design-widget-multi-display-more-btn .btn:active {
        background-color: #FFFFFF;
        border-color: #DDDDDD;
        box-shadow: none;
        outline: none;
    }

    /* 기존 스타일 유지하고 아래 추가 */

    /* 키프레임들 */
    @keyframes fade-up { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
    @keyframes fade-down { from { opacity: 0; transform: translateY(-30px); } to { opacity: 1; transform: translateY(0); } }
    @keyframes fade-left { from { opacity: 0; transform: translateX(30px); } to { opacity: 1; transform: translateX(0); } }
    @keyframes fade-right { from { opacity: 0; transform: translateX(-30px); } to { opacity: 1; transform: translateX(0); } }
    @keyframes fade-center { from { opacity: 0; } to { opacity: 1; } }

    @keyframes zoom-in-up { from { opacity: 0; transform: scale(0.8) translateY(30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-in-down { from { opacity: 0; transform: scale(0.8) translateY(-30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-in-left { from { opacity: 0; transform: scale(0.8) translateX(30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-in-right { from { opacity: 0; transform: scale(0.8) translateX(-30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-in-center { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }

    @keyframes zoom-out-up { from { opacity: 0; transform: scale(1.2) translateY(30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-out-down { from { opacity: 0; transform: scale(1.2) translateY(-30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-out-left { from { opacity: 0; transform: scale(1.2) translateX(30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-out-right { from { opacity: 0; transform: scale(1.2) translateX(-30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-out-center { from { opacity: 0; transform: scale(1.2); } to { opacity: 1; transform: scale(1); } }

    @keyframes flip-up { from { opacity: 0; transform: perspective(400px) rotateX(90deg); } to { opacity: 1; transform: perspective(400px) rotateX(0); } }
    @keyframes flip-down { from { opacity: 0; transform: perspective(400px) rotateX(-90deg); } to { opacity: 1; transform: perspective(400px) rotateX(0); } }
    @keyframes flip-left { from { opacity: 0; transform: perspective(400px) rotateY(90deg); } to { opacity: 1; transform: perspective(400px) rotateY(0); } }
    @keyframes flip-right { from { opacity: 0; transform: perspective(400px) rotateY(-90deg); } to { opacity: 1; transform: perspective(400px) rotateY(0); } }

    /* 호버 텍스트 애니메이션 */
    @keyframes hover-text-fade {
        from { opacity: 0; }
        to { opacity: 1; }
    }

    @keyframes hover-text-slide {
        from {
            opacity: 0;
            transform: translateY(20px);
        }
        to {
            opacity: 1;
            transform: translateY(0);
        }
    }

    @keyframes hover-text-flip {
        from {
            opacity: 0;
            transform: perspective(400px) rotateX(90deg);
        }
        to {
            opacity: 1;
            transform: perspective(400px) rotateX(0);
        }
    }

    /* 이미지 오버레이 */
    .widget-image-overlay {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.9);
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 910000;
        cursor: pointer;
    }

    .widget-image-overlay img {
        max-width: 90%;
        max-height: 90%;
        object-fit: contain;
        cursor: default;
    }

    .widget-overlay-close {
        position: absolute;
        top: 20px;
        right: 30px;
        font-size: 40px;
        color: white;
        background: none;
        border: none;
        cursor: pointer;
        padding: 0;
        width: 40px;
        height: 40px;
        line-height: 1;
    }

    .widget-overlay-close:hover {
        color: #ccc;
    }
</style>
<script type="text/x-template" id="design-widget-slide-modal-each-image-template">
    <div v-if="load">
        <external-bs-modal v-model="modal">
            <template v-slot:header>
                <h5 class="modal-title" id="exampleModalLabel">이미지 개별 설정</h5>
            </template>

            <!-- body -->
            <template v-slot:default>
                <div>
                    <div class="row">
                        <div class="col-md-12 text-center">
                            <img :src="row.src" style="max-width: 100%; max-height: 500px;">
                            <div class="jd-each-image-replace-actions">
                                <button type="button" class="btn btn-orange btn-sm" @click="$refs.replaceFileInput.click()">직접 업로드</button>
                                <button type="button" class="btn btn-orange btn-sm" @click="openReplaceFreeModal()">무료 이미지</button>
                                <button type="button" class="btn btn-orange btn-sm" @click="openReplaceStorageModal()">이미지 보관함</button>
                            </div>
                            <input class="hidden" type="file" accept="image/*" ref="replaceFileInput" @change="changeReplaceFile($event)">
                        </div>
                    </div>

                    <hr>

                    <div>
                        <h4 class="jd-image-modal-title">
                            <img src="/file/icon/링크 이동.svg"> 링크 설정
                        </h4>
                        <div style="display: inline-flex; align-items: center; gap: 8px; font-size: 16px; position: relative;">
                            <span style="white-space: nowrap;">링크입력</span>
                            <img src="/file/icon/물음표.svg"
                                 style="cursor: pointer; width: 16px; height: 16px;"
                                 class="design-widget-multi-modal-each-help-icon"
                                 @mouseenter="$event.target.nextElementSibling.classList.add('active')"
                                 @mouseleave="$event.target.nextElementSibling.classList.remove('active')">
                            <div class="design-widget-multi-modal-each-help-tooltip">
                                ‘마우스 클릭 동작’ 항목에서
                                ‘링크 이동’ 설정 시 이동할 링크입니다.
                            </div>
                        </div>


                        <div style="display: flex; gap : 5px; align-items: center;">
                            <select class="form-control" style="flex : 2;" v-model="row.option_mouse_click_url_type">
                                <option value="URL">URL</option>
                                <option value="페이지">페이지</option>
                            </select>

                            <template v-if="row.option_mouse_click_url_type == 'URL'">
                                <input type="text" class="form-control" style="flex : 8;" v-model="row.option_mouse_click_url" placeholder="https://">
                            </template>

                            <template v-if="row.option_mouse_click_url_type == '페이지'">
                                <template v-if="menus.length == 0">데이터를 불러들이는 중입니다.</template>

                                <template v-else>
                                    <select class="form-control" style="flex : 8;" v-model="row.option_mouse_click_url">
                                        <option value="">선택해주세요.</option>
                                        <template v-for="item in menus">
                                            <option :value="item.url">{{item.name}}</option>
                                        </template>
                                    </select>
                                </template>
                            </template>
                        </div>


                        <div style="display: flex; margin-top: 10px; gap : 10px; align-items: center;">
                            <input type="radio" value="_self" v-model="row.option_mouse_click_target"> 기존 창에서 이동
                            <input type="radio" value="_blank" v-model="row.option_mouse_click_target"> 새 창으로 열기
                        </div>
                    </div>

                    <hr>

                    <div>
                        <div style="display: inline-flex; align-items: center; gap: 8px; font-size: 16px; position: relative;">
                            <span style="white-space: nowrap;">alt 태그</span>
                            <img src="/file/icon/물음표.svg"
                                 style="cursor: pointer; width: 16px; height: 16px; margin-bottom: 3px;"
                                 class="design-widget-multi-modal-each-help-icon"
                                 @mouseenter="$event.target.nextElementSibling.classList.add('active')"
                                 @mouseleave="$event.target.nextElementSibling.classList.remove('active')">
                            <div class="design-widget-multi-modal-each-help-tooltip">
                                이미지 내용을 글로 설명하는 텍스트입니다.
                                접근성 향상과 SEO(검색 노출)에 도움이 됩니다.
                            </div>
                        </div>

                        <input type="text" v-model="row.alt_text" class="form-control form-control-sm" placeholder="(선택)alt 태그를 입력해주세요.">
                    </div>

                    <hr>

                    <div>
                        <h4 class="jd-image-modal-title">
                            <img src="/file/icon/slide/이미지_콘텐츠_설정.svg"> 이미지 콘텐츠 설정
                        </h4>

                        <div class="design-widget-slide-modal-each-image-layout">
                            <label class="design-widget-slide-modal-each-image-layout-item" v-for="opt in opts" :key="opt.value">
                                <input type="radio" :value="opt.value" v-model="row.content_layout" name="content_layout">
                                <div class="design-widget-slide-modal-each-image-layout-box" :class="{ active: row.content_layout === opt.value }">
                                    <div class="design-widget-slide-modal-each-image-layout-preview">
                                        <div class="design-widget-slide-modal-each-image-layout-img-placeholder">
                                            <svg width="20" height="16" viewBox="0 0 20 16" fill="#bbb"><rect width="20" height="16" rx="2"/><circle cx="6" cy="5" r="2" fill="#ddd"/><path d="M0 12l5-4 3 2 5-5 7 7v2c0 1-1 2-2 2H2c-1 0-2-1-2-2z" fill="#ddd"/></svg>
                                        </div>
                                        <div class="design-widget-slide-modal-each-image-layout-content" v-if="opt.value !== 'none'">
                                            <div class="design-widget-slide-modal-each-image-layout-line" v-if="opt.value === 'text' || opt.value === 'text_button'"></div>
                                            <div class="design-widget-slide-modal-each-image-layout-line short" v-if="opt.value === 'text' || opt.value === 'text_button'"></div>
                                            <div class="design-widget-slide-modal-each-image-layout-btn" v-if="opt.value === 'button' || opt.value === 'text_button'">BTN</div>
                                        </div>
                                    </div>
                                </div>
                                <span class="design-widget-slide-modal-each-image-layout-label">{{ opt.label }}</span>
                            </label>
                        </div>
                    </div>

                    <hr>

                    <div v-if="row.content_layout !== 'none'">
                        <div style="display: flex; gap: 20px; align-items: center;">
                            <!-- 텍스트 위치 -->
                            <div style="flex: 1; display: flex; align-items: center; gap: 15px;">
                                <div style="font-weight: bold;">위치</div>
                                <div class="jd-text-position-grid">
                                    <div class="jd-position-cell" :class="{active: row.content_position === 'top-left'}" @click="row.content_position = 'top-left'"></div>
                                    <div class="jd-position-cell" :class="{active: row.content_position === 'top-center'}" @click="row.content_position = 'top-center'"></div>
                                    <div class="jd-position-cell" :class="{active: row.content_position === 'top-right'}" @click="row.content_position = 'top-right'"></div>

                                    <div class="jd-position-cell" :class="{active: row.content_position === 'middle-left'}" @click="row.content_position = 'middle-left'"></div>
                                    <div class="jd-position-cell" :class="{active: row.content_position === 'middle-center'}" @click="row.content_position = 'middle-center'"></div>
                                    <div class="jd-position-cell" :class="{active: row.content_position === 'middle-right'}" @click="row.content_position = 'middle-right'"></div>

                                    <div class="jd-position-cell" :class="{active: row.content_position === 'bottom-left'}" @click="row.content_position = 'bottom-left'"></div>
                                    <div class="jd-position-cell" :class="{active: row.content_position === 'bottom-center'}" @click="row.content_position = 'bottom-center'"></div>
                                    <div class="jd-position-cell" :class="{active: row.content_position === 'bottom-right'}" @click="row.content_position = 'bottom-right'"></div>
                                </div>
                            </div>


                            <!-- 텍스트 여백 -->
                            <div style="flex: 1; display: flex; align-items: center; gap: 15px;">
                                <div font-weight: bold;">여백</div>
                            <div style="max-width: 250px;">
                                <div class="text-center" style="margin-bottom: 5px;">
                                    <input type="text" v-number class="form-control input-sm" v-model="row.content_margin_top" style="width: 60px; display: inline-block;">
                                </div>
                                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;">
                                    <input type="text" v-number class="form-control input-sm" v-model="row.content_margin_left" style="width: 60px; margin-right: 10px;">
                                    <div style="width: 60px; height: 30px; background: #f5f5f5; border: 1px solid #ddd;"></div>
                                    <input type="text" v-number class="form-control input-sm" v-model="row.content_margin_right" style="width: 60px; margin-left: 10px;">
                                </div>
                                <div class="text-center">
                                    <input type="text" v-number class="form-control input-sm" v-model="row.content_margin_bottom" style="width: 60px; display: inline-block;">
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div v-if="row.content_layout === 'text' || row.content_layout === 'text_button'">
                    <div style="display: inline-flex; align-items: center; gap: 8px; font-size: 16px; position: relative;">
                        <h4>텍스트 설정</h4>
                    </div>

                    <external-summernote :row field="each_text_content" height="100"></external-summernote>
                </div>

                <hr>

                <div v-if="row.content_layout === 'button' || row.content_layout === 'text_button'">
                    <div style="display: inline-flex; align-items: center; gap: 8px; font-size: 16px; position: relative;">
                        <h4>버튼 설정</h4>
                    </div>
                    <!-- 버튼 프리뷰 -->
                    <div class="text-center" style="margin-bottom: 15px; padding: 20px; background: #f9f9f9; border-radius: 6px;">
                        <button type="button" :style="btnStyle">{{ row.btn_text || 'Button' }}</button>
                    </div>

                    <!-- 너비, 높이, 버튼색, 배경색, 모서리 둥글게, 테두리, 폰트 크기 -->
                    <div class="design-widget-slide-modal-each-image-btn-settings">
                        <div class="design-widget-slide-modal-each-image-btn-row">
                            <div class="design-widget-slide-modal-each-image-btn-field">
                                <label>너비</label>
                                <div class="d-flex align-items-center gap-1">
                                    <input type="text" v-model="row.btn_width" class="form-control form-control-sm" v-number style="width:55px;">
                                    <span class="design-widget-slide-modal-each-image-btn-unit">px</span>
                                </div>
                            </div>
                            <div class="design-widget-slide-modal-each-image-btn-field">
                                <label>높이</label>
                                <div class="d-flex align-items-center gap-1">
                                    <input type="text" v-model="row.btn_height" class="form-control form-control-sm" v-number style="width:55px;">
                                    <span class="design-widget-slide-modal-each-image-btn-unit">px</span>
                                </div>
                            </div>
                            <div class="design-widget-slide-modal-each-image-btn-field">
                                <label>버튼색</label>
                                <external-picker v-model="row.btn_color"></external-picker>
                            </div>
                            <div class="design-widget-slide-modal-each-image-btn-field">
                                <label>배경색</label>
                                <external-picker v-model="row.btn_bg_color"></external-picker>
                            </div>
                            <div class="design-widget-slide-modal-each-image-btn-field">
                                <label>모서리</label>
                                <div class="number-input-wrapper">
                                    <img src="/file/icon/Group 95.svg" class="input-icon-left">
                                    <input type="text" v-model="row.btn_border_radius" class="form-control form-control-sm" v-number>
                                    <div class="number-stepper">
                                        <button type="button" class="stepper-up" @click="row.btn_border_radius++"><svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 0L10 6H0L5 0Z" fill="#666"/></svg></button>
                                        <button type="button" class="stepper-down" @click="row.btn_border_radius--"><svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 6L0 0H10L5 6Z" fill="#666"/></svg></button>
                                    </div>
                                </div>
                            </div>
                            <div class="design-widget-slide-modal-each-image-btn-field">
                                <label>테두리</label>
                                <div class="d-flex align-items-center gap-1">
                                    <input type="text" v-model="row.btn_border_width" class="form-control form-control-sm" v-number style="width:40px;">
                                    <span class="design-widget-slide-modal-each-image-btn-unit">px</span>
                                    <external-picker v-model="row.btn_border_color"></external-picker>
                                </div>
                            </div>
                            <div class="design-widget-slide-modal-each-image-btn-field">
                                <label>폰트</label>
                                <div class="d-flex align-items-center gap-1">
                                    <input type="text" v-model="row.btn_font_size" class="form-control form-control-sm" v-number style="width:45px;">
                                    <span class="design-widget-slide-modal-each-image-btn-unit">px</span>
                                </div>
                            </div>
                        </div>
                    </div>

                    <!-- 버튼 텍스트 -->
                    <div style="margin-top: 15px;">
                        <label>버튼 텍스트</label>
                        <input type="text" class="form-control" v-model="row.btn_text" placeholder="버튼 텍스트를 입력해주세요.">
                    </div>
                </div>
    </div>
    </template>


    <template v-slot:footer>
        <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
        <button type="button" class="btn btn-orange save" @click="saveRow()">저장</button>
    </template>
    </external-bs-modal>

    <design-widget-slide-modal-free v-model="replaceFreeModal"></design-widget-slide-modal-free>
    <design-widget-slide-modal-storage v-model="replaceStorageModal"></design-widget-slide-modal-storage>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide-modal-each-image",object : {
            template: "#design-widget-slide-modal-each-image-template",
            props: {
                modelValue : {type: Object, default: {}},
                site_code : {type : String, default : "st68f18a193702e"},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-slide-modal-each-image",
                    component_idx: "",

                    temp : {
                        $table : "",
                    },
                    row : {},
                    rows : [],

                    menus : [],
                    replaceFreeModal : this.lib.createModalObject(),
                    replaceStorageModal : this.lib.createModalObject(),

                    opts : [
                        { value: 'none', label: '콘텐츠 없음' },
                        { value: 'text', label: '텍스트' },
                        { value: 'button', label: '버튼' },
                        { value: 'text_button', label: '텍스트+버튼' }
                    ],
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
            },
            async mounted() {
                this.api.table("menu").where("site_code",this.site_code).get(this.menus);

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper

                });
            },
            updated() {

            },
            methods: {
                getParentRow() {
                    return this.modelValue.parent_row || this.modelValue.row || this.row;
                },
                openReplaceFreeModal() {
                    this.$openModal(this.replaceFreeModal, {
                        row: this.getParentRow(),
                        replace_target: this.row,
                    });
                },
                openReplaceStorageModal() {
                    this.$openModal(this.replaceStorageModal, {
                        row: this.getParentRow(),
                        replace_target: this.row,
                    });
                },
                async changeReplaceFile(event) {
                    await this.vue.changeFile(event, this.row, '_replace_upload_file', {
                        callback: () => {
                            if(this.row._replace_upload_file && this.row._replace_upload_file.src) {
                                this.row.src = this.row._replace_upload_file.src;
                                this.row.type = "image";
                            }
                        }
                    });

                    if(event && event.target) {
                        event.target.value = "";
                    }
                },
                saveRow() {
                    // 🔥 저장 버튼 클릭시 원본에 반영
                    Object.assign(this.modelValue.row, this.row);

                    // 모달 닫기
                    this.$emit('update:modelValue', { ...this.modelValue, status: false });
                }
            },
            computed: {
                modal: {
                    get() {
                        return this.modelValue;
                    },
                    set(value) {
                        this.$emit('update:modelValue', value);
                    }
                },
                btnStyle() {
                    return {
                        width: this.row.btn_width + 'px',
                        height: this.row.btn_height + 'px',
                        fontSize: this.row.btn_font_size + 'px',
                        color: this.row.btn_color,
                        backgroundColor: this.row.btn_bg_color,
                        borderRadius: this.row.btn_border_radius + 'px',
                        border: this.row.btn_border_width + 'px solid ' + this.row.btn_border_color
                    };
                }
            },
            watch: {

                async "modelValue.status"(value, old_value) {
                    const $modal = $("#widget_setting_jd");

                    if(!value) {
                        if ($modal.data('bs.modal')) {
                            $modal.data('bs.modal').options.backdrop = true;
                            $modal.data('bs.modal').options.keyboard = true;
                        }
                        return false;
                    }

                    // 🔥 모달 열릴 때 복사
                    this.row = { ...this.modelValue.row };
                    if (!('option_mouse_click_url_type' in this.row)) this.row.option_mouse_click_url_type = "URL";
                    if (!('option_mouse_click_url' in this.row)) this.row.option_mouse_click_url = "";
                    if (!('option_mouse_click_target' in this.row)) this.row.option_mouse_click_target = "_self";


                    if (!('content_layout' in this.row)) this.row.content_layout = "none";

                    if (!('content_position' in this.row)) this.row.content_position = "middle-center";
                    if (!('content_margin_top' in this.row)) this.row.content_margin_top = "0";
                    if (!('content_margin_left' in this.row)) this.row.content_margin_left = "0";
                    if (!('content_margin_right' in this.row)) this.row.content_margin_right = "0";
                    if (!('content_margin_bottom' in this.row)) this.row.content_margin_bottom = "0";

                    if (!('each_text_content' in this.row)) this.row.each_text_content = "";

                    if (!('btn_width' in this.row)) this.row.btn_width = 150;
                    if (!('btn_height' in this.row)) this.row.btn_height = 40;
                    if (!('btn_color' in this.row)) this.row.btn_color = "#000000";
                    if (!('btn_bg_color' in this.row)) this.row.btn_bg_color = "#ffffff";
                    if (!('btn_border_radius' in this.row)) this.row.btn_border_radius = 0;
                    if (!('btn_border_width' in this.row)) this.row.btn_border_width = 1;
                    if (!('btn_border_color' in this.row)) this.row.btn_border_color = "#000000";
                    if (!('btn_font_size' in this.row)) this.row.btn_font_size = 16;
                    if (!('btn_text' in this.row)) this.row.btn_text = "";

                    if (!('alt_text' in this.row)) this.row.alt_text = "";

                    this.$nextTick(() => {
                        $('#' + this.component_idx + ' .modal-dialog').css('width', '700px');
                    });

                    if ($modal.data('bs.modal')) {
                        $modal.data('bs.modal').options.backdrop = 'static';
                        $modal.data('bs.modal').options.keyboard = false;
                    }
                }
            }
        }});
</script>

<style>
    .design-widget-slide-modal-each-image-layout {
        display: flex;
        gap: 15px;
        margin: 15px 0;
    }

    .design-widget-slide-modal-each-image-layout-item {
        cursor: pointer;
        text-align: center;
        margin: 0;
        font-weight: normal;
    }

    .design-widget-slide-modal-each-image-layout-item input[type="radio"] {
        display: none;
    }

    .design-widget-slide-modal-each-image-layout-box {
        width: 130px;
        height: 90px;
        border: 2px solid #ddd;
        border-radius: 6px;
        padding: 8px;
        transition: border-color 0.2s;
        background: #f9f9f9;
    }

    .design-widget-slide-modal-each-image-layout-box:hover {
        border-color: #aaa;
    }

    .design-widget-slide-modal-each-image-layout-box.active {
        border-color: #e64d11;
    }

    .design-widget-slide-modal-each-image-layout-preview {
        display: flex;
        align-items: flex-start;
        gap: 8px;
        height: 100%;
    }

    .design-widget-slide-modal-each-image-layout-img-placeholder {
        width: 40px;
        height: 100%;
        background: #e8e8e8;
        border-radius: 3px;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
    }

    .design-widget-slide-modal-each-image-layout-content {
        display: flex;
        flex-direction: column;
        justify-content: center;
        gap: 5px;
        flex: 1;
        height: 100%;
    }

    .design-widget-slide-modal-each-image-layout-line {
        height: 4px;
        background: #ccc;
        border-radius: 2px;
        width: 100%;
    }

    .design-widget-slide-modal-each-image-layout-line.short {
        width: 60%;
    }

    .design-widget-slide-modal-each-image-layout-btn {
        background: #ddd;
        color: #888;
        font-size: 9px;
        padding: 3px 8px;
        border-radius: 3px;
        text-align: center;
        width: fit-content;
    }

    .design-widget-slide-modal-each-image-layout-label {
        display: block;
        margin-top: 8px;
        font-size: 13px;
        color: #555;
    }

    .design-widget-slide-modal-each-image-layout-box.active + .design-widget-slide-modal-each-image-layout-label {
        color: #e64d11;
        font-weight: 500;
    }

    .design-widget-slide-modal-each-image-btn-settings {
        margin-top: 10px;
    }

    .design-widget-slide-modal-each-image-btn-row {
        display: flex;
        flex-wrap: wrap;
        gap: 15px;
        align-items: flex-end;
    }

    .design-widget-slide-modal-each-image-btn-field {
        display: flex;
        flex-direction: column;
        gap: 5px;
    }

    .design-widget-slide-modal-each-image-btn-field > label {
        font-size: 13px;
        font-weight: 500;
        color: #333;
        margin: 0;
    }

    .design-widget-slide-modal-each-image-btn-unit {
        font-size: 12px;
        color: #888;
        margin-left: 4px;
    }

    .jd-each-image-replace-actions {
        display: flex;
        justify-content: center;
        gap: 8px;
        margin-top: 10px;
    }
</style>
<script type="text/x-template" id="design-widget-slide-modal-free-template">
    <div v-if="load">
        <external-bs-modal v-model="modelValue" @update:modelValue="value => $emit('update:modelValue', value)">
            <template v-slot:header>
                <h5 class="modal-title" id="exampleModalLabel">무료 이미지</h5>
            </template>

            <template v-slot:default>
                <div>
                    <div style="margin-bottom: 15px;">
                        <div style="position: relative;">
                            <input type="text"
                                   class="form-control free-modal-image-modal-search-input"
                                   placeholder="검색어를 입력해 주세요."
                                   v-model="keyword"
                                   @keyup.enter="getImages()"/>
                            <button type="button"
                                    class="free-modal-image-modal-search-btn"
                                    @click="getImages()">
                                <img src="/file/icon/돋보기.svg">
                            </button>
                        </div>
                    </div>
                    <div class="text-right text-muted">Powered by Freepik</div>
                    <div v-if="loading">이미지 불러오는중...</div>
                    <template v-else>
                        <div v-if="rows.length == 0">{{text_content}}</div>
                        <div v-else>
                            <div class="free-modal-image-grid-flex">
                                <template v-for="item in visibleRows">
                                    <div class="free-modal-image-item"
                                         @click="choiceImage(item)"
                                         :class="activeImage(item)">
                                        <img :src="getImageSrc(item)">
                                        <div class="free-modal-image-overlay">
                                            <div class="free-modal-image-info">
                                                <div class="free-modal-author-name">{{getAuthorName(item)}}</div>
                                                <div class="free-modal-image-description">
                                                    {{getDescription(item)}}
                                                </div>
                                                <div class="free-modal-image-meta" v-if="getSourceUrl(item)">
                                                    <span>Freepik</span>
                                                    <a :href="getSourceUrl(item)"
                                                       target="_blank"
                                                       rel="noopener noreferrer"
                                                       class="free-modal-image-link"
                                                       @click.stop>원본</a>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </template>
                            </div>

                            <nav aria-label="...">
                                <ul class="pagination pagination-sm">
                                    <li class="page-item" :class="{disabled : !canGoPrevApiPage}" @click="goPrevApiPage()">
                                        <span class="page-link">&lt;&lt;</span>
                                    </li>
                                    <template v-for="num in displayedPages">
                                        <li :key="'freepik-page-' + num" class="page-item" :class="{active : num === page}" aria-current="page" @click="page = num;">
                                            <span class="page-link">{{num}}</span>
                                        </li>
                                    </template>
                                    <li class="page-item" :class="{disabled : !canGoNextApiPage}" @click="goNextApiPage()">
                                        <span class="page-link">&gt;&gt;</span>
                                    </li>
                                </ul>
                            </nav>

                            <div class="text-center" style="margin-top: 20px;" v-show="false">
                                <button type="button" class="btn btn-default btn-load-more" @click="getImages()">
                                    <i class="glyphicon glyphicon-refresh"></i> 새로고침
                                </button>
                            </div>
                        </div>
                    </template>

                </div>
            </template>


            <template v-slot:footer>
                <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
                <button type="button" class="btn btn-orange save" @click="saveImage()">저장</button>
            </template>
        </external-bs-modal>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide-modal-free",object : {
            template: "#design-widget-slide-modal-free-template",
            props: {
                modelValue : {type: Object, default: {}},
                widget_type : {type: String, default: ""},
                user_idx : {type: String, default: ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-slide-modal-free",
                    component_idx: "",
                    loading : false,

                    temp : {
                        $table : "",
                    },
                    row: {},
                    rows : [],
                    rowsByApiPage : {},

                    file_idx : "",
                    limit : 9,
                    page : 1,
                    apiPageSize : 99,
                    currentApiPage : 1,
                    lastApiPage : null,

                    image_url : "",
                    keyword : "",
                    searchedKeyword : "",
                    select_items : [],
                    text_content : "이미지가 없습니다.",
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
            },
            async mounted() {
                this.table = await this.api.table("jd_file");

                this.load = true;

                this.$nextTick(async () => {
                });
            },
            updated() {

            },
            methods: {
                async getImages(reset = true) {
                    this.loading = true;
                    if(reset) {
                        this.page = 1;
                        this.currentApiPage = 1;
                        this.text_content = "이미지가 없습니다.";
                        this.rows = [];
                        this.rowsByApiPage = {};
                        this.lastApiPage = null;
                        this.searchedKeyword = this.keyword;
                    }

                    try {
                        await this.requestApiPage(1, reset ? this.keyword : this.searchedKeyword);
                        this.applyApiPage(1);
                    } catch (err) {
                        this.rows = [];
                        this.rowsByApiPage = {};
                        this.text_content = "현재 요청하신 무료 이미지의 사용이 불가능합니다. 죄송합니다.";
                    } finally {
                        this.loading = false;
                    }
                },
                getApiPage(page) {
                    return Math.ceil(page / this.localPageSize) || 1;
                },
                getLocalPage(page) {
                    return ((page - 1) % this.localPageSize) + 1;
                },
                async requestApiPage(apiPage, keyword = "") {
                    const targetKeyword = keyword !== "" ? keyword : this.searchedKeyword;
                    if(this.rowsByApiPage[apiPage]) {
                        return this.rowsByApiPage[apiPage];
                    }

                    let res = await this.lib.ajax("get_resources",{term : targetKeyword, page : apiPage},"/JayDream/provider/freepik/api.php");
                    let data = res?.data?.data?.data || [];

                    this.rowsByApiPage = Object.assign({}, this.rowsByApiPage, {
                        [apiPage] : data
                    });
                    this.rows = data;

                    if(data.length < this.apiPageSize) {
                        this.lastApiPage = apiPage;
                    }

                    return data;
                },
                applyApiPage(apiPage) {
                    this.currentApiPage = apiPage;
                    this.rows = this.rowsByApiPage[apiPage] || [];
                    this.page = ((apiPage - 1) * this.localPageSize) + 1;
                },
                async goNextApiPage() {
                    if(this.loading || !this.canGoNextApiPage) return;

                    const targetApiPage = this.currentApiPage + 1;

                    this.loading = true;
                    try {
                        const data = await this.requestApiPage(targetApiPage);
                        if(!data.length) {
                            this.lastApiPage = targetApiPage - 1;
                            return;
                        }
                        this.applyApiPage(targetApiPage);
                    } catch (err) {
                        this.text_content = "현재 요청하신 무료 이미지의 사용이 불가능합니다. 죄송합니다.";
                    } finally {
                        this.loading = false;
                    }
                },
                goPrevApiPage() {
                    if(this.loading || !this.canGoPrevApiPage) return;
                    this.applyApiPage(this.currentApiPage - 1);
                },
                normalizeImageUrl(url) {
                    if(!url) return "";
                    return url.replace(/^http:\/\//i, "https://");
                },
                getImageSrc(item) {
                    return this.normalizeImageUrl(item && item.image && item.image.source ? item.image.source.url : "");
                },
                getSourceUrl(item) {
                    return this.normalizeImageUrl(item && item.url ? item.url : "");
                },
                getAuthorName(item) {
                    return item && item.author && item.author.name ? item.author.name : "Freepik";
                },
                getDescription(item) {
                    if(item && item.title) return item.title;
                    if(item && item.filename) return item.filename;
                    return "제목 없음";
                },
                saveImage() {
                    if(this.isReplaceMode()) {
                        const selected = this.select_items[0];
                        if(selected && selected.src) {
                            this.replaceTargetImage(selected);
                        }

                        this.$closeModal(this.modelValue);
                        return;
                    }

                    this.modelValue.row.images = [...this.select_items];
                    this.$closeModal(this.modelValue);
                },
                isReplaceMode() {
                    return !!(this.modelValue && this.modelValue.replace_target);
                },
                replaceTargetImage(item) {
                    this.modelValue.replace_target.src = item.src;
                    this.modelValue.replace_target.type = item.type || "image";
                    delete this.modelValue.replace_target._replace_upload_file;
                },
                activeImage(item) {
                    return this.select_items.some(selected => selected.src === this.getImageSrc(item)) ? "active" : "";
                },
                choiceImage(item) {
                    const imageSrc = this.getImageSrc(item);
                    if(this.isReplaceMode()) {
                        this.select_items = imageSrc ? [{ src: imageSrc, type: "image" }] : [];
                        return;
                    }

                    const index = this.select_items.findIndex(selected => selected.src === imageSrc);

                    if (index > -1) {
                        this.select_items.splice(index, 1);
                    } else {
                        this.select_items.push({ src: imageSrc, type: "image" });
                    }
                },
            },
            computed: {
                localPageSize() {
                    return this.apiPageSize / this.limit;
                },
                visibleRows() {
                    const localPage = this.getLocalPage(this.page);
                    const start = (localPage - 1) * this.limit;
                    const end = localPage * this.limit;
                    return this.rows.slice(start, end);
                },
                totalPage() {
                    if(!this.rows.length) return 0;
                    return this.rows.length % this.limit === 0 ? this.rows.length / this.limit : Math.floor(this.rows.length / this.limit) + 1;
                },
                displayedPages() {
                    const start = this.page - this.getLocalPage(this.page) + 1;
                    return Array.from({length : this.totalPage}, (_, index) => start + index);
                },
                canGoPrevApiPage() {
                    return this.currentApiPage > 1;
                },
                canGoNextApiPage() {
                    if(this.lastApiPage !== null) {
                        return this.currentApiPage < this.lastApiPage;
                    }

                    return this.rows.length === this.apiPageSize;
                }
            },
            watch: {
                async "modelValue.status"(value, old_value) {
                    const $modal = $("#widget_setting_jd");

                    if(!value) {
                        this.file_idx = "";
                        this.rows = [];
                        this.rowsByApiPage = {};
                        this.page = 1;
                        this.currentApiPage = 1;
                        this.lastApiPage = null;
                        this.searchedKeyword = "";
                        this.select_items = [];
                        this.text_content = "이미지가 없습니다.";
                        this.modelValue.replace_target = null;

                        if($modal.data('bs.modal')) {
                            $modal.data('bs.modal').options.backdrop = true;
                            $modal.data('bs.modal').options.keyboard = true;
                        }
                        return false;
                    }

                    if($modal.data('bs.modal')) {
                        $modal.data('bs.modal').options.backdrop = 'static';
                        $modal.data('bs.modal').options.keyboard = false;
                    }

                    await this.getImages();

                    if(this.isReplaceMode()) {
                        this.select_items = this.modelValue.replace_target && this.modelValue.replace_target.src ? [{ src: this.modelValue.replace_target.src, type: this.modelValue.replace_target.type || "image" }] : [];
                    } else {
                        this.select_items = Array.isArray(this.modelValue.row.images) ? [...this.modelValue.row.images] : [];
                    }
                }
            }
        }});
</script>

<style>
    .free-modal-image-grid-flex {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        padding: 10px 0;
    }

    .free-modal-image-item {
        cursor: pointer;
        border-radius: 4px;
        overflow: hidden;
        border: 1px solid #ddd;
        transition: all 0.2s;
        aspect-ratio: 1 / 1;
        position: relative;
    }

    .free-modal-image-item img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
        transition: all 0.2s;
    }

    .free-modal-image-item:hover {
        border-color: #337ab7;
        box-shadow: 0 2px 8px rgba(0,0,0,0.15);
        transform: scale(1.05);
    }

    .free-modal-image-item.active {
        outline: 3px solid red;
        outline-offset: -3px;
    }

    .free-modal-image-overlay {
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
        background: linear-gradient(to top, rgba(0,0,0,0.95) 0%, rgba(0,0,0,0.85) 40%, rgba(0,0,0,0.3) 70%, transparent 100%);
        padding: 15px 10px 10px;
        opacity: 0;
        transition: opacity 0.3s;
        z-index: 999999;
    }

    .free-modal-image-item:hover .free-modal-image-overlay {
        opacity: 1;
    }

    .free-modal-image-info {
        color: white;
        font-size: 12px;
    }

    .free-modal-author-name {
        font-weight: bold;
        margin-bottom: 3px;
        font-size: 13px;
        text-shadow: 0 1px 3px rgba(0,0,0,0.8), 0 0 10px rgba(0,0,0,0.5);
    }

    .free-modal-image-description {
        font-size: 11px;
        line-height: 1.3;
        opacity: 0.95;
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        text-shadow: 0 1px 3px rgba(0,0,0,0.8), 0 0 8px rgba(0,0,0,0.5);
    }

    .free-modal-image-meta {
        margin-top: 6px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 10px;
        font-size: 11px;
    }

    .free-modal-image-link {
        color: #fff;
        text-decoration: underline;
    }

    .free-modal-image-link:hover,
    .free-modal-image-link:focus {
        color: #fff;
    }

    .free-modal-image-modal-search-input {
        padding-right: 45px !important;
        border-radius: 4px;
        border: 1px solid #ddd;
        height: 40px;
    }

    .free-modal-image-modal-search-input:focus {
        border-color: #66afe9;
        outline: 0;
        box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6);
    }

    .free-modal-image-modal-search-btn {
        position: absolute;
        right: 8px;
        top: 50%;
        transform: translateY(-50%);
        background: none;
        border: none;
        padding: 5px 10px;
        cursor: pointer;
        color: #999;
        transition: color 0.2s;
    }

    .free-modal-image-modal-search-btn:hover {
        color: #333;
    }

    .free-modal-image-modal-search-btn:focus {
        outline: none;
    }
</style>
<script type="text/x-template" id="design-widget-slide-modal-storage-template">
    <div v-if="load">
        <external-bs-modal v-model="modelValue" @update:modelValue="value => $emit('update:modelValue', value)">
            <template v-slot:header>
                <h5 class="modal-title" id="exampleModalLabel">이미지 보관함</h5>
            </template>

            <!-- body -->
            <template v-slot:default>
                <div>
                    <div v-if="loading">이미지 불러오는중...</div>
                    <template v-else>
                        <div v-if="rows.length == 0">이미지가 없습니다.</div>
                        <div v-else>
                            <div class="image-grid-flex">
                                <template v-for="item,index in rows">
                                    <div v-if="index >= (page - 1) * limit && index < page * limit" class="image-item" @click="choiceImage(item)" :class="activeImage(item)">
                                        <img :src="item.src">
                                        <button class="image-delete-btn" @click.stop="deleteImage(item)">
                                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                                <polyline points="3 6 5 6 21 6"></polyline>
                                                <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                                                <line x1="10" y1="11" x2="10" y2="17"></line>
                                                <line x1="14" y1="11" x2="14" y2="17"></line>
                                            </svg>
                                        </button>
                                    </div>
                                </template>
                            </div>

                            <nav aria-label="...">
                                <ul class="pagination pagination-sm">
                                    <template v-for="num in totalPage">
                                        <li class="page-item" :class="{active : num === page}" aria-current="page" @click="page = num;">
                                            <span class="page-link">{{num}}</span>
                                        </li>
                                    </template>
                                </ul>
                            </nav>
                        </div>
                    </template>

                </div>
            </template>


            <template v-slot:footer>
                <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
                <button type="button" class="btn btn-orange save" @click="saveImage()">저장</button>
            </template>
        </external-bs-modal>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide-modal-storage",object : {
            template: "#design-widget-slide-modal-storage-template",
            props: {
                modelValue : {type: Object, default: {}},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-slide-modal-storage",
                    component_idx: "",
                    loading : false,

                    temp : {
                        $table : "",
                    },
                    row: {},
                    rows : [],

                    limit : 9,
                    page : 1,

                    select_items : [],
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
            },
            async mounted() {
                this.table = await this.api.table("jd_file");

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {
                async deleteImage(item) {
                    if(!await this.lib.confirm("삭제시 연결된 이미지가 있을경우 엑박이 뜹니다 삭제 하시겠습니까?")) return false;

                    await this.api.table("jd_file").delete(item,{return : true});

                    await this.getImages();
                },
                async getImages() {
                    this.loading = true;

                    await this.table.where("user_idx",this.modelValue.row.user_idx).get(this.rows);

                    this.loading = false;

                },
                saveImage() {
                    if(this.isReplaceMode()) {
                        const selected = this.select_items[0];
                        if(selected && selected.src) {
                            this.replaceTargetImage(selected);
                        }

                        this.$closeModal(this.modelValue);
                        return;
                    }

                    // 전체 교체 - 객체 배열 그대로 저장
                    this.modelValue.row.images = [...this.select_items];
                    this.$closeModal(this.modelValue);
                },
                isReplaceMode() {
                    return !!(this.modelValue && this.modelValue.replace_target);
                },
                replaceTargetImage(item) {
                    this.modelValue.replace_target.src = item.src;
                    this.modelValue.replace_target.type = item.type || "image";
                    delete this.modelValue.replace_target._replace_upload_file;
                },
                activeImage(item) {
                    // 전체 교체 - 객체 배열에서 src 비교
                    return this.select_items.some(selected => selected.src === item.src) ? "active" : "";
                },
                choiceImage(item) {
                    if(this.isReplaceMode()) {
                        this.select_items = item && item.src ? [{ src: item.src, type: "image" }] : [];
                        return;
                    }

                    // 전체 교체 - 객체로 추가/제거
                    const index = this.select_items.findIndex(selected => selected.src === item.src);

                    if (index > -1) {
                        // 이미 있으면 제거
                        this.select_items.splice(index, 1);
                    } else {
                        // 없으면 객체로 추가
                        this.select_items.push({ src: item.src, type : "image" });
                    }
                },
            },
            computed: {
                totalPage() {
                    return this.rows.length % this.limit === 0 ? this.rows.length / this.limit : Math.floor(this.rows.length / this.limit) + 1;
                }
            },
            watch: {
                async "modelValue.status"(value, old_value) {
                    const $modal = $("#widget_setting_jd");

                    if(!value) {
                        this.file_idx = "";
                        this.rows = [];
                        this.modelValue.replace_target = null;

                        if ($modal.data('bs.modal')) {
                            $modal.data('bs.modal').options.backdrop = true;
                            $modal.data('bs.modal').options.keyboard = true;
                        }



                        return false;
                    }


                    if ($modal.data('bs.modal')) {
                        $modal.data('bs.modal').options.backdrop = 'static';
                        $modal.data('bs.modal').options.keyboard = false;
                    }


                    await this.getImages();

                    if(this.isReplaceMode()) {
                        this.select_items = this.modelValue.replace_target && this.modelValue.replace_target.src ? [{ src: this.modelValue.replace_target.src, type: this.modelValue.replace_target.type || "image" }] : [];
                    } else {
                        this.select_items = [...this.modelValue.row.images];
                    }
                }
            }
        }});
</script>
<script type="text/x-template" id="design-widget-slide-modal-tab1-template">
    <div>
        <h4 class="jd-image-modal-title">
            <img src="/file/icon/slide/용도.svg"> 용도 선택
        </h4>

        <div class="design-widget-slide-modal-tab1 btn btn-default slide-type-btn" :class="{active : row.slide_type == '일반 슬라이드'}" @click="row.slide_type = '일반 슬라이드'">
            <img src="/file/icon/slide/일반_슬라이드.svg">
            <span>일반 슬라이드</span>
        </div>

        <div class="design-widget-slide-modal-tab1 btn btn-default slide-type-btn" :class="{active : row.slide_type == '비주얼 섹션'}" @click="row.slide_type = '비주얼 섹션'">
            <img src="/file/icon/slide/비주얼_섹션.svg">
            <span>비주얼 섹션</span>
            <span style="position: relative; display: inline-block; margin-left: 8px;">
        <img src="/file/icon/물음표.svg"
             style="cursor: pointer; width: 16px; height: 16px;"
             class="design-widget-slide-modal-tab1-help-icon"
             @mouseenter="$event.target.nextElementSibling.classList.add('active')"
             @mouseleave="$event.target.nextElementSibling.classList.remove('active')">
        <div class="design-widget-slide-modal-tab1-help-tooltip">
            페이지 상단에서 브랜드를 강조하는<br>
            대표 비주얼 영역입니다.
        </div>
    </span>
        </div>
    </div>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide-modal-tab1",object : {
            template: "#design-widget-slide-modal-tab1-template",
            props: {
                row : {type : Object, default : null},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-slide-modal-tab1",
                    component_idx: "",

                    table : null,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {

            },
            computed: {

            },
            watch: {

            }
        }});
</script>

<style>
    .design-widget-slide-modal-tab1.slide-type-btn {
        border: 2px solid #ddd;
        border-radius: 8px;
        padding: 16px 24px;
        background: white;
        cursor: pointer;
        transition: all 0.2s;
        display: inline-block;
        width: auto;
        margin-right: 10px;
    }

    .design-widget-slide-modal-tab1.slide-type-btn:hover {
        border-color: #ff6b6b;
    }

    .design-widget-slide-modal-tab1.slide-type-btn.active {
        border-color: #ff6b6b;
        background: #fff5f5;
    }

    .design-widget-slide-modal-tab1.slide-type-btn img {
        width: 24px;
        height: 24px;
        margin-right: 10px;
        vertical-align: middle;
        transition: opacity 0.2s;
    }

    .design-widget-slide-modal-tab1.slide-type-btn span {
        color: #666;
        font-weight: 500;
        font-size: 15px;
        vertical-align: middle;
        transition: color 0.2s;
    }

    .design-widget-slide-modal-tab1.slide-type-btn.active span,
    .design-widget-slide-modal-tab1.slide-type-btn:hover span {
        color: #ff6b6b;
    }

    .design-widget-slide-modal-tab1-help-tooltip {
        display: none;
        position: absolute;
        left: calc(100% + 8px);
        top: 50%;
        transform: translateY(-50%);
        background: rgba(0, 0, 0, 0.9);
        color: #fff;
        padding: 8px 12px;
        border-radius: 6px;
        font-size: 13px;
        white-space: nowrap;
        box-shadow: 0 2px 8px rgba(0,0,0,0.3);
        z-index: 99999;
    }

    .design-widget-slide-modal-tab1-help-tooltip.active {
        display: inline-block;
    }
</style><script type="text/x-template" id="design-widget-slide-modal-tab2-template">
    <div>
        <h4 class="jd-image-modal-title">
            <img src="/file/icon/slide/슬라이드_스타일.svg"> 슬라이드 스타일 선택
        </h4>

        <div class="design-widget-slide-modal-tab2-scroll-container">
            <div class="design-widget-slide-modal-tab2-scroll-wrapper">
                <!-- Default -->
                <div class="design-widget-slide-modal-tab2-layout-btn" :class="{'design-widget-slide-modal-tab2-active' : row.slide_style == 'Default'}" @click="row.slide_style = 'Default'">
                    <div class="design-widget-slide-modal-tab2-preview">
                        <div class="design-widget-slide-modal-tab2-box">
                            <svg viewBox="0 0 160 90" width="140" height="78">
                                <!-- 중앙 이미지 -->
                                <rect x="20" y="10" width="120" height="70" rx="4" fill="#e0e0e0" stroke="#bbb" stroke-width="1.5"/>
                                <line x1="20" y1="55" x2="65" y2="30" stroke="#bbb" stroke-width="1.5"/>
                                <line x1="65" y1="30" x2="95" y2="50" stroke="#bbb" stroke-width="1.5"/>
                                <line x1="95" y1="50" x2="140" y2="25" stroke="#bbb" stroke-width="1.5"/>
                                <circle cx="55" cy="30" r="8" fill="#ccc"/>
                                <!-- 좌우 화살표 -->
                                <polygon points="8,45 16,38 16,52" fill="#999"/>
                                <polygon points="152,45 144,38 144,52" fill="#999"/>
                            </svg>
                        </div>
                    </div>
                </div>

                <!-- Vertical -->
                <div class="design-widget-slide-modal-tab2-layout-btn" :class="{'design-widget-slide-modal-tab2-active' : row.slide_style == 'Vertical'}" @click="row.slide_style = 'Vertical'">
                    <div class="design-widget-slide-modal-tab2-preview">
                        <div class="design-widget-slide-modal-tab2-box">
                            <svg viewBox="0 0 160 90" width="140" height="78">
                                <!-- 중앙 이미지 -->
                                <rect x="30" y="15" width="100" height="60" rx="4" fill="#e0e0e0" stroke="#bbb" stroke-width="1.5"/>
                                <line x1="30" y1="55" x2="65" y2="35" stroke="#bbb" stroke-width="1.5"/>
                                <line x1="65" y1="35" x2="90" y2="48" stroke="#bbb" stroke-width="1.5"/>
                                <line x1="90" y1="48" x2="130" y2="30" stroke="#bbb" stroke-width="1.5"/>
                                <circle cx="58" cy="33" r="7" fill="#ccc"/>
                                <!-- 상하 화살표 -->
                                <polygon points="80,4 74,12 86,12" fill="#999"/>
                                <polygon points="80,86 74,78 86,78" fill="#999"/>
                            </svg>
                        </div>
                    </div>
                </div>

                <!-- Slides per view -->
                <div class="design-widget-slide-modal-tab2-layout-btn" :class="{'design-widget-slide-modal-tab2-active' : row.slide_style == 'Slides per view'}" @click="row.slide_style = 'Slides per view'">
                    <div class="design-widget-slide-modal-tab2-preview">
                        <div class="design-widget-slide-modal-tab2-box">
                            <svg viewBox="0 0 160 90" width="140" height="78">
                                <!-- 왼쪽 반 잘린 이미지 -->
                                <rect x="-10" y="15" width="48" height="60" rx="4" fill="#d5d5d5" stroke="#bbb" stroke-width="1.5"/>
                                <!-- 이미지 3개 -->
                                <rect x="12" y="15" width="40" height="60" rx="4" fill="#e0e0e0" stroke="#bbb" stroke-width="1.5"/>
                                <circle cx="32" cy="35" r="5" fill="#ccc"/>
                                <rect x="58" y="15" width="40" height="60" rx="4" fill="#e0e0e0" stroke="#bbb" stroke-width="1.5"/>
                                <circle cx="78" cy="35" r="5" fill="#ccc"/>
                                <rect x="104" y="15" width="40" height="60" rx="4" fill="#e0e0e0" stroke="#bbb" stroke-width="1.5"/>
                                <circle cx="124" cy="35" r="5" fill="#ccc"/>
                                <!-- 오른쪽 반 잘린 이미지 -->
                                <rect x="150" y="15" width="48" height="60" rx="4" fill="#d5d5d5" stroke="#bbb" stroke-width="1.5"/>
                            </svg>
                        </div>
                    </div>
                </div>

                <!-- Centered auto -->
                <div class="design-widget-slide-modal-tab2-layout-btn" :class="{'design-widget-slide-modal-tab2-active' : row.slide_style == 'Centered auto'}" @click="row.slide_style = 'Centered auto'">
                    <div class="design-widget-slide-modal-tab2-preview">
                        <div class="design-widget-slide-modal-tab2-box">
                            <svg viewBox="0 0 160 90" width="140" height="78">
                                <!-- 왼쪽 작은 이미지 -->
                                <rect x="5" y="22" width="35" height="46" rx="4" fill="#d5d5d5" stroke="#bbb" stroke-width="1.5" opacity="0.6"/>
                                <!-- 중앙 큰 이미지 -->
                                <rect x="45" y="10" width="70" height="70" rx="4" fill="#e0e0e0" stroke="#bbb" stroke-width="1.5"/>
                                <line x1="45" y1="55" x2="70" y2="35" stroke="#bbb" stroke-width="1.5"/>
                                <line x1="70" y1="35" x2="90" y2="48" stroke="#bbb" stroke-width="1.5"/>
                                <line x1="90" y1="48" x2="115" y2="30" stroke="#bbb" stroke-width="1.5"/>
                                <circle cx="65" cy="30" r="7" fill="#ccc"/>
                                <!-- 오른쪽 작은 이미지 -->
                                <rect x="120" y="22" width="35" height="46" rx="4" fill="#d5d5d5" stroke="#bbb" stroke-width="1.5" opacity="0.6"/>
                            </svg>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide-modal-tab2",object : {
            template: "#design-widget-slide-modal-tab2-template",
            props: {
                row : {type : Object, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-slide-modal-tab2",
                    component_idx: "",

                    table : null,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.load = true;

                this.$nextTick(async () => {

                });
            },
            updated() {

            },
            methods: {

            },
            computed: {

            },
            watch: {

            }
        }});
</script>

<style>
    /* 스크롤 컨테이너 */
    .design-widget-slide-modal-tab2-scroll-container {
        overflow-x: auto;
        margin-top: 20px;
        padding-bottom: 10px;
    }

    .design-widget-slide-modal-tab2-scroll-container::-webkit-scrollbar {
        height: 8px;
    }

    .design-widget-slide-modal-tab2-scroll-container::-webkit-scrollbar-thumb {
        background: #ddd;
        border-radius: 4px;
    }

    .design-widget-slide-modal-tab2-scroll-container::-webkit-scrollbar-thumb:hover {
        background: #bbb;
    }

    .design-widget-slide-modal-tab2-scroll-wrapper {
        display: flex;
        gap: 15px;
        min-width: max-content;
    }

    /* 레이아웃 버튼 */
    .design-widget-slide-modal-tab2-layout-btn {
        border: 2px solid #ddd;
        border-radius: 8px;
        padding: 0;
        background: white;
        cursor: pointer;
        transition: all 0.2s;
        width: 180px;
        flex-shrink: 0;
    }

    .design-widget-slide-modal-tab2-layout-btn:hover {
        border-color: #ff6b6b;
    }

    .design-widget-slide-modal-tab2-layout-btn.design-widget-slide-modal-tab2-active {
        border-color: #ff6b6b;
    }

    .design-widget-slide-modal-tab2-preview {
        width: 100%;
        height: 100px;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .design-widget-slide-modal-tab2-box {
        width: 90%;
        height: 85%;
        border: 2px solid #ccc;
        border-radius: 4px;
        background: #f5f5f5;
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;
        overflow: hidden;
    }

    .design-widget-slide-modal-tab2-label {
        text-align: center;
        padding: 6px 0 8px;
        font-size: 12px;
        color: #666;
        font-weight: 500;
    }

    .design-widget-slide-modal-tab2-active .design-widget-slide-modal-tab2-label {
        color: #ff6b6b;
        font-weight: 600;
    }
</style><script type="text/x-template" id="design-widget-slide-modal-tab3-template">
    <div>
        <h4 class="jd-image-modal-title">
            <img src="/file/icon/이미지추가.svg"> 콘텐츠 추가
        </h4>

        <div class="design-widget-multi-modal-container">
            <!-- 버튼 그룹 -->
            <div class="button-group" style="margin-bottom: 15px;">
                <div class="design-widget-slide-modal-tab3 media-group">
                    <span class="media-label">이미지</span>
                    <button class="btn btn-orange btn-sm design-widget-multi-modal-btn-upload" @click="$refs.fileInput.click()">직접 업로드</button>
                    <button class="btn btn-orange btn-sm design-widget-multi-modal-btn-free" @click="$openModal(modal2,{row})">무료 이미지</button>
                    <button class="btn btn-orange btn-sm design-widget-multi-modal-btn-storage" @click="$openModal(modal1,{row})">이미지 보관함</button>
                </div>

                <div class="design-widget-slide-modal-tab3 media-group">
                    <span class="media-label">동영상</span>
                    <button class="btn btn-sm design-widget-multi-modal-btn-video" @click="$openModal(modal4,{row})">동영상</button>
                </div>
            </div>
            <input class="hidden" type="file" ref="fileInput" @change="vue.changeFile($event,row,'upload_file')" name="names[]" multiple>

            <!-- 이미지 그리드 (가로 스크롤) -->
            <div class="design-widget-multi-modal-scroll-wrapper" style="overflow-x: auto; overflow-y: hidden; white-space: nowrap; margin-bottom: 15px;">
                <draggable v-model="allImages" @end="onDragEnd" item-key="primary">
                    <template #item="{ element : item, index }">
                        <div class="design-widget-multi-modal-image-grid" style="display: inline-block;">
                            <div class="design-widget-multi-modal-image-item" style="display: inline-block; vertical-align: top; width: 200px; margin-right: 10px;">
                                <div class="thumbnail" style="position: relative;">
                                    <img :src="item.src" style="width: 100%; height: 150px; object-fit: cover;">
                                    <i v-if="item.type === 'video'" class="fa fa-youtube-play" style="position: absolute; top: 5px; left: 5px; background: rgba(0,0,0,0.6); color: #ff0000; padding: 4px 6px; border-radius: 3px; font-size: 16px;"></i>
                                    <button class="btn btn-danger btn-xs" style="position: absolute; top: 5px; right: 5px;"
                                            @click="deleteImage(item,index)">
                                        <i class="glyphicon glyphicon-trash"></i>
                                    </button>

                                    <!-- URL 오버레이 -->
                                    <div style="position: absolute; bottom: 0; left: 0; right: 0; background: rgba(0,0,0,0.55); color: #fff; font-size: 11px; padding: 4px 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"
                                         v-if="item.option_mouse_click_url || item.url">
                                        <i class="glyphicon glyphicon-link" style="margin-right: 3px;"></i>{{ item.option_mouse_click_url || item.url }}
                                    </div>
                                </div>

                                <div class="text-center design-widget-multi-modal-btn-wrapper" style="margin-top: 0;">
                                    <button class="btn btn-default btn-sm btn-block design-widget-multi-modal-btn-edit"
                                            :class="item.type === 'video' ? 'btn-edit-video' : ''"
                                            @click="openModal(item)">
                                        <img src="/file/icon/개별설정.svg"> 개별 설정
                                    </button>
                                </div>

                            </div>
                        </div>
                    </template>
                </draggable>

            </div>

            <p style="color: #999; font-size: 12px; margin-top: 15px;">
                ※ 마우스 드래그로 콘텐츠의 순서를 변경할 수 있습니다.
            </p>
        </div>
    </div>

    <design-widget-slide-modal-storage v-model="modal1" @update:modelValue="value => $emit('update:modelValue', value)"></design-widget-slide-modal-storage>
    <design-widget-slide-modal-free v-model="modal2" @update:modelValue="value => $emit('update:modelValue', value)"></design-widget-slide-modal-free>
    <design-widget-slide-modal-each-image v-model="modal3" :site_code ></design-widget-slide-modal-each-image>
    <design-widget-slide-modal-video v-model="modal4" :site_code ></design-widget-slide-modal-video>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide-modal-tab3",object : {
            template: "#design-widget-slide-modal-tab3-template",
            props: {
                row : {type : String, default : ""},
                site_code : {type : String, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-slide-modal-tab3",
                    component_idx: "",

                    modal1 : this.lib.createModalObject(),
                    modal2 : this.lib.createModalObject(),
                    modal3 : this.lib.createModalObject({width : 700}),
                    modal4 : this.lib.createModalObject(),

                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.sessions = await this.session.all();

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {
                openModal(item) {
                    console.log(item)
                    if(item.type == 'video') this.$openModal(this.modal4,{video_put : item})
                    else this.$openModal(this.modal3,{row : item, parent_row : this.row})
                },
                deleteImage(item,index) {
                    // allImages 배열에서 제거
                    const allImages = this.allImages;
                    allImages.splice(index, 1);

                    // priority 재설정하면서 원본 배열에 반영
                    this.allImages = allImages;
                },
                async onDragEnd(evt,array) {
                    if (evt.oldIndex === evt.newIndex) return;

                    const movedItem = array[evt.newIndex];

                    const start = Math.min(evt.oldIndex, evt.newIndex);
                    const end = Math.max(evt.oldIndex, evt.newIndex);

                    // console.log(`이동된 범위: ${start} ~ ${end}`);

                    for (let i = start; i <= end; i++) {
                        const item = array[i];
                        item.priority = i;

                        this.$postData(item,{return : true});
                        // console.log(`수정 대상 idx ${i}:`, item);
                    }

                    // console.log('드래그 완료:', evt.oldIndex, '→', evt.newIndex);
                },
            },
            computed: {
                allImages: {
                    get() {
                        return [
                            ...this.row.upload_file,
                            ...this.row.images
                        ].sort((a, b) => {
                            const aPriority = a.priority ?? Infinity;
                            const bPriority = b.priority ?? Infinity;
                            return aPriority - bPriority;
                        });
                    },
                    set(value) {
                        // priority만 재설정
                        value.forEach((item, index) => {
                            item.priority = index;
                        });

                        // 🔥 원본 배열도 업데이트 (삭제된 항목 반영)
                        this.row.upload_file = this.row.upload_file.filter(item =>
                            value.includes(item)
                        );

                        this.row.images = this.row.images.filter(item =>
                            value.includes(item)
                        );
                    }
                }
            },
            watch: {

            }
        }});
</script>

<style>
    .design-widget-slide-modal-tab3.media-group {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        margin-right: 20px;
    }

    .design-widget-slide-modal-tab3 .media-label {
        font-weight: 500;
        color: #333;
        margin-right: 5px;
    }

    .design-widget-slide-modal-tab3 .design-widget-multi-modal-btn-video {
        background-color: #7167BF;
        border-color: #7167BF;
        color: white;
    }

    .design-widget-slide-modal-tab3 .design-widget-multi-modal-btn-video:hover {
        background-color: #5f56a8;
        border-color: #5f56a8;
    }

    .design-widget-multi-modal-btn-edit.btn-edit-video {
        color: #7167BF !important;
    }

    .design-widget-multi-modal-btn-edit.btn-edit-video img {
        filter: invert(45%) sepia(50%) saturate(500%) hue-rotate(210deg);
    }
</style>
<script type="text/x-template" id="design-widget-slide-modal-tab4-template">
    <div class="row">
        <div class="col-md-12">
            <h4 class="jd-image-modal-title">
                <img src="/file/icon/레이아웃.svg"> 레이아웃 설정
            </h4>

            <div class="design-widget-slide-modal-tab4 layout-settings">
                <!-- 비율 -->
                <div class="layout-setting-item" v-if="false">
                    <label class="setting-label">비율</label>
                    <select class="form-control setting-select" v-model="row.ratio">
                        <option value="채우기">채우기</option>
                        <option value="원본 비율 유지">원본 비율 유지</option>
                    </select>
                </div>

                <!-- 높이 -->
                <div class="layout-setting-item">
                    <label class="setting-label">높이</label>
                    <div class="input-group setting-input-group">
                        <input type="number" class="form-control" v-model="row.height" placeholder="600">
                        <span class="input-group-addon">px</span>
                    </div>
                </div>

                <div class="layout-setting-item">
                    <label class="setting-label">모바일 전용 높이</label>
                    <div class="input-group setting-input-group">
                        <input type="number" class="form-control" v-model="row.mobile_height" placeholder="480">
                        <span class="input-group-addon">px</span>
                    </div>
                </div>

                <!-- 한 줄 당 이미지 개수 -->
                <template v-if="row.slide_style == 'Slides per view'">
                    <div class="layout-setting-item">
                        <label class="setting-label">한 줄 당 이미지 개수</label>
                        <div class="setting-dual-group">
                            <div class="setting-with-label">
                                <span class="device-label">PC</span>
                                <select class="form-control setting-select" v-model="row.line_image_pc">
                                    <template v-for="item in 8">
                                        <option :value="item">{{item}}개</option>
                                    </template>
                                </select>
                            </div>
                            <div class="setting-with-label">
                                <span class="device-label">모바일</span>
                                <select class="form-control setting-select" v-model="row.line_image_mobile">
                                    <template v-for="item in 8">
                                        <option :value="item">{{item}}개</option>
                                    </template>
                                </select>
                            </div>
                        </div>
                    </div>

                    <!-- 컨텐츠 사이 간격 -->
                    <div class="layout-setting-item">
                        <label class="setting-label">콘텐츠 사이 간격</label>
                        <div class="input-group setting-input-group">
                            <input type="number" class="form-control" v-model="row.gap" placeholder="10">
                            <span class="input-group-addon">px</span>
                        </div>
                    </div>
                </template>

            </div>
        </div>
    </div>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide-modal-tab4",object : {
            template: "#design-widget-slide-modal-tab4-template",
            props: {
                row : {type : Object, default : null},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-slide-modal-tab4",
                    component_idx: "",

                    table : null,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {

            },
            computed: {

            },
            watch: {

            }
        }});
</script>

<style>
    .design-widget-slide-modal-tab4.layout-settings {
        display: flex;
        gap: 20px;
        margin-top: 20px;
        align-items: flex-end;
    }

    .design-widget-slide-modal-tab4 .layout-setting-item {
        display: flex;
        flex-direction: column;
        gap: 8px;
    }

    .design-widget-slide-modal-tab4 .setting-label {
        font-size: 13px;
        color: #666;
        font-weight: bold;
        margin-bottom: 0;
    }

    .design-widget-slide-modal-tab4 .setting-select {
        width: 160px;
        height: 38px;
    }

    .design-widget-slide-modal-tab4 .setting-input-group {
        width: 160px;
    }

    .design-widget-slide-modal-tab4 .setting-input-group input {
        height: 38px;
    }

    .design-widget-slide-modal-tab4 .setting-input-group .input-group-addon {
        height: 38px;
        line-height: 38px;
        padding: 0 12px;
    }

    .design-widget-slide-modal-tab4 .setting-dual-group {
        display: flex;
        gap: 12px;
    }

    .design-widget-slide-modal-tab4 .setting-with-label {
        display: flex;
        align-items: center;
        gap: 6px;
    }

    .design-widget-slide-modal-tab4 .device-label {
        font-size: 13px;
        color: #666;
        font-weight: 500;
        white-space: nowrap;
    }

    .design-widget-slide-modal-tab4 .setting-dual-group .setting-select {
        width: 80px;
    }
</style><script type="text/x-template" id="design-widget-slide-modal-tab5-template">
    <div class="row">
        <div class="col-md-12">
            <h4 class="jd-image-modal-title">
                <img src="/file/icon/slide/슬라이드_설정.svg"> 슬라이드 설정
            </h4>

            <div class="design-widget-slide-modal-tab5-settings-container">
                <!-- 전환 효과 -->
                <div class="design-widget-slide-modal-tab5-setting-row">
                    <label class="design-widget-slide-modal-tab5-label">전환 효과</label>
                    <select class="form-control design-widget-slide-modal-tab5-select" v-model="row.slide_effect">
                        <option value="슬라이드">슬라이드</option>
                        <option value="페이드">페이드</option>
                    </select>
                </div>

                <!-- 전환 속도 -->
                <div class="design-widget-slide-modal-tab5-setting-row">
                    <label class="design-widget-slide-modal-tab5-label">전환 속도</label>
                    <div class="design-widget-slide-modal-tab5-dual-input">
                        <div class="input-group design-widget-slide-modal-tab5-input-group">
                            <input type="number" class="form-control" v-model="row.slide_speed" placeholder="0.2" step="0.1">
                            <span class="input-group-addon">초</span>
                        </div>
                    </div>
                </div>

                <!-- 전환 대기 시간 -->
                <div class="design-widget-slide-modal-tab5-setting-row">
                    <label class="design-widget-slide-modal-tab5-label">전환 대기 시간</label>
                    <div class="design-widget-slide-modal-tab5-dual-input">
                        <div class="input-group design-widget-slide-modal-tab5-input-group">
                            <input type="number" class="form-control" v-model="row.slide_wait" placeholder="5">
                            <span class="input-group-addon">초</span>
                        </div>
                    </div>
                </div>

                <!-- 전환 방식 -->
                <template v-if="row.slide_style == 'Slides per view'">

                    <div class="design-widget-slide-modal-tab5-setting-row">
                        <label class="design-widget-slide-modal-tab5-label">
                            전환 방식
                            <span style="position: relative; display: inline-block; margin-left: 4px;">
                                <img src="/file/icon/물음표.svg"
                                     style="cursor: pointer; width: 16px; height: 16px;"
                                     class="design-widget-slide-modal-tab1-help-icon"
                                     @mouseenter="$event.target.nextElementSibling.classList.add('active')"
                                     @mouseleave="$event.target.nextElementSibling.classList.remove('active')">
                                <div class="design-widget-slide-modal-tab1-help-tooltip">
                                    - 개별 전환: 슬라이드 콘텐츠 1개 단위로 전환됩니다.<br>
                                    - 묶음 전환: ‘한 줄 당 이미지 개수’ 에서 설정한 개수만큼 동시 전환됩니다.
                                </div>
                            </span>
                        </label>
                        <select class="form-control design-widget-slide-modal-tab5-select" v-model="row.slide_change">
                            <option value="개별 전환">개별 전환</option>
                            <option value="묶음 전환">묶음 전환</option>
                        </select>
                    </div>
                </template>
            </div>

            <!-- 체크박스 옵션들 -->
            <div class="design-widget-slide-modal-tab5-checkbox-group">
                <label class="design-widget-slide-modal-tab5-checkbox-item">
                    <input type="checkbox" v-model="row.slide_loop">
                    <span class="design-widget-slide-modal-tab5-checkbox-label">슬라이드 반복 재생</span>
                </label>
                <label class="design-widget-slide-modal-tab5-checkbox-item">
                    <input type="checkbox" v-model="row.slide_over">
                    <span class="design-widget-slide-modal-tab5-checkbox-label">마우스오버 시 멈춤</span>
                </label>
                <label class="design-widget-slide-modal-tab5-checkbox-item">
                    <input type="checkbox" v-model="row.slide_drag">
                    <span class="design-widget-slide-modal-tab5-checkbox-label">드래그로 넘기기</span>
                </label>
            </div>

            <!-- 마우스 클릭 동작 -->
            <div class="design-widget-slide-modal-tab5-radio-section">
                <label class="design-widget-slide-modal-tab5-section-label">마우스 클릭 동작</label>
                <div class="design-widget-slide-modal-tab5-radio-group">
                    <label class="design-widget-slide-modal-tab5-radio-item">
                        <input type="radio" name="mouse_click_action" value="동작없음" v-model="row.slide_click">
                        <span class="design-widget-slide-modal-tab5-radio-label">동작없음</span>
                    </label>
                    <label class="design-widget-slide-modal-tab5-radio-item">
                        <input type="radio" name="mouse_click_action" value="링크 이동" v-model="row.slide_click">
                        <span class="design-widget-slide-modal-tab5-radio-label">링크 이동</span>
                    </label>
                    <label class="design-widget-slide-modal-tab5-radio-item">
                        <input type="radio" name="mouse_click_action" value="크게 보기" v-model="row.slide_click">
                        <span class="design-widget-slide-modal-tab5-radio-label">크게 보기</span>
                    </label>
                </div>
                <p class="design-widget-slide-modal-tab5-note">※ 링크 등록은 콘텐츠 '개별 설정' 에서 가능합니다.</p>
            </div>
        </div>
    </div>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide-modal-tab5",object : {
            template: "#design-widget-slide-modal-tab5-template",
            props: {
                row : {type : Object, default : null},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-slide-modal-tab5",
                    component_idx: "",

                    table : null,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {

            },
            computed: {

            },
            watch: {

            }
        }});
</script>

<style>
    .design-widget-slide-modal-tab5-settings-container {
        display: flex;
        gap: 20px;
        margin-top: 20px;
        margin-bottom: 20px;
    }

    .design-widget-slide-modal-tab5-setting-row {
        display: flex;
        flex-direction: column;
        gap: 8px;
    }

    .design-widget-slide-modal-tab5-label {
        font-size: 13px;
        color: #666;
        font-weight: bold;
        margin-bottom: 0;
        display: flex;
        align-items: center;
        gap: 5px;
    }

    .design-widget-slide-modal-tab5-tooltip {
        color: #999;
        cursor: help;
        font-size: 14px;
    }

    .design-widget-slide-modal-tab5-select {
        width: 140px;
        height: 38px;
    }

    .design-widget-slide-modal-tab5-dual-input {
        display: flex;
        gap: 8px;
    }

    .design-widget-slide-modal-tab5-input-group {
        width: 140px;
    }

    .design-widget-slide-modal-tab5-input-group input {
        height: 38px;
    }

    .design-widget-slide-modal-tab5-input-group .input-group-addon {
        height: 38px;
        line-height: 38px;
        padding: 0 12px;
    }

    /* 체크박스 그룹 */
    .design-widget-slide-modal-tab5-checkbox-group {
        display: flex;
        gap: 20px;
        margin-bottom: 20px;
    }

    .design-widget-slide-modal-tab5-checkbox-item {
        display: flex;
        align-items: center;
        gap: 8px;
        cursor: pointer;
        margin-bottom: 0;
        font-weight: normal;
    }

    .design-widget-slide-modal-tab5-checkbox-item input[type="checkbox"] {
        width: 18px;
        height: 18px;
        cursor: pointer;
        margin: 0;
    }

    .design-widget-slide-modal-tab5-checkbox-label {
        font-size: 14px;
        color: #333;
        user-select: none;
    }

    /* 라디오 버튼 섹션 */
    .design-widget-slide-modal-tab5-radio-section {
        margin-top: 20px;
    }

    .design-widget-slide-modal-tab5-section-label {
        font-size: 14px;
        color: #333;
        font-weight: bold;
        margin-bottom: 12px;
        display: block;
    }

    .design-widget-slide-modal-tab5-radio-group {
        display: flex;
        gap: 20px;
        margin-bottom: 8px;
    }

    .design-widget-slide-modal-tab5-radio-item {
        display: flex;
        align-items: center;
        gap: 8px;
        cursor: pointer;
        margin-bottom: 0;
        font-weight: normal;
    }

    .design-widget-slide-modal-tab5-radio-item input[type="radio"] {
        width: 18px;
        height: 18px;
        cursor: pointer;
        margin: 0;
    }

    .design-widget-slide-modal-tab5-radio-label {
        font-size: 14px;
        color: #333;
        user-select: none;
    }

    .design-widget-slide-modal-tab5-note {
        font-size: 12px;
        color: #999;
        margin-top: 8px;
        margin-bottom: 0;
    }
</style>
<script type="text/x-template" id="design-widget-slide-modal-tab6-template">
    <div class="row">
        <div class="col-md-12">
            <h4 class="jd-image-modal-title">
                <img src="/file/icon/basic.svg"> 스타일 설정
            </h4>

            <!-- 그림자, 테두리, 모서리 둥글게 -->
            <div class="design-widget-slide-modal-tab6-options-row" v-if="row.slide_type == '일반 슬라이드'">
                <div class="design-widget-slide-modal-tab6-option-group">
                    <input type="checkbox" v-model="row.box_shadow_use">
                    <span>그림자</span>
                    <select class="form-control form-control-sm design-widget-slide-modal-tab6-select-small" v-model="row.box_shadow_opacity">
                        <option value="10">10%</option>
                        <option value="20">20%</option>
                        <option value="30">30%</option>
                        <option value="40">40%</option>
                        <option value="50">50%</option>
                        <option value="60">60%</option>
                        <option value="70">70%</option>
                        <option value="80">80%</option>
                        <option value="90">90%</option>
                        <option value="100">100%</option>
                    </select>
                </div>

                <div class="design-widget-slide-modal-tab6-option-group">
                    <input type="checkbox" v-model="row.border_use">
                    <span>테두리</span>
                    <external-picker v-model="row.border_color"></external-picker>
                    <div class="design-widget-slide-modal-tab6-number-input-wrapper">
                        <input type="text" v-model="row.border_width" class="form-control form-control-sm" v-number>
                        <div class="design-widget-slide-modal-tab6-number-stepper">
                            <button type="button" class="design-widget-slide-modal-tab6-stepper-up" @click="row.border_width++;">
                                <svg width="10" height="6" viewBox="0 0 10 6">
                                    <path d="M5 0L10 6H0L5 0Z" fill="#666"/>
                                </svg>
                            </button>
                            <button type="button" class="design-widget-slide-modal-tab6-stepper-down" @click="row.border_width--;">
                                <svg width="10" height="6" viewBox="0 0 10 6">
                                    <path d="M5 6L0 0H10L5 6Z" fill="#666"/>
                                </svg>
                            </button>
                        </div>
                    </div>
                </div>

                <div class="design-widget-slide-modal-tab6-option-group">
                    <input type="checkbox" v-model="row.border_radius_use">
                    <span>모서리 둥글게</span>
                    <div class="design-widget-slide-modal-tab6-number-input-wrapper design-widget-slide-modal-tab6-with-icon">
                        <img src="/file/icon/Group 95.svg" class="design-widget-slide-modal-tab6-input-icon-left">
                        <input type="text" v-model="row.border_radius_px" class="form-control form-control-sm" v-number>
                        <div class="design-widget-slide-modal-tab6-number-stepper">
                            <button type="button" class="design-widget-slide-modal-tab6-stepper-up" @click="row.border_radius_px++;">
                                <svg width="10" height="6" viewBox="0 0 10 6">
                                    <path d="M5 0L10 6H0L5 0Z" fill="#666"/>
                                </svg>
                            </button>
                            <button type="button" class="design-widget-slide-modal-tab6-stepper-down" @click="row.border_radius_px--;">
                                <svg width="10" height="6" viewBox="0 0 10 6">
                                    <path d="M5 6L0 0H10L5 6Z" fill="#666"/>
                                </svg>
                            </button>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 페이징 버튼 -->
            <div class="design-widget-slide-modal-tab6-section">
                <label class="design-widget-slide-modal-tab6-section-label">페이징 버튼</label>
                <div class="design-widget-slide-modal-tab6-paging-buttons">
                    <button class="design-widget-slide-modal-tab6-paging-btn" :class="{'design-widget-slide-modal-tab6-active': row.slide_paging === 'none'}" @click="row.slide_paging = 'none'">
                        사용안함
                    </button>
                    <button class="design-widget-slide-modal-tab6-paging-btn" :class="{'design-widget-slide-modal-tab6-active': row.slide_paging === 'dots'}" @click="row.slide_paging = 'dots'">
                        <span class="design-widget-slide-modal-tab6-dot design-widget-slide-modal-tab6-bar-active"></span>
                        <span class="design-widget-slide-modal-tab6-dot"></span>
                        <span class="design-widget-slide-modal-tab6-dot"></span>
                    </button>
                    <button class="design-widget-slide-modal-tab6-paging-btn" :class="{'design-widget-slide-modal-tab6-active': row.slide_paging === 'bullet'}" @click="row.slide_paging = 'bullet'">
                        <span class="design-widget-slide-modal-tab6-number-dot design-widget-slide-modal-tab6-number-active">1</span>
                        <span class="design-widget-slide-modal-tab6-number-dot">2</span>
                        <span class="design-widget-slide-modal-tab6-number-dot">3</span>
                    </button>
                    <button class="design-widget-slide-modal-tab6-paging-btn" :class="{'design-widget-slide-modal-tab6-active': row.slide_paging === 'fraction'}" @click="row.slide_paging = 'fraction'">
                        <span class="design-widget-slide-modal-tab6-fraction">1/3</span>
                    </button>
                </div>
            </div>

            <!-- 활성 버튼 색상, 비활성 버튼 색상 -->
            <div class="design-widget-slide-modal-tab6-options-row">
                <div class="design-widget-slide-modal-tab6-option-group">
                    <label class="design-widget-slide-modal-tab6-color-label">활성 버튼 색상</label>
                    <external-picker v-model="row.slide_paging_on_color"></external-picker>
                </div>

                <div class="design-widget-slide-modal-tab6-option-group">
                    <label class="design-widget-slide-modal-tab6-color-label">비활성 버튼 색상</label>
                    <external-picker v-model="row.slide_paging_off_color"></external-picker>
                </div>

                <div class="design-widget-slide-modal-tab6-option-group">
                    <label class="design-widget-slide-modal-tab6-color-label">크기</label>
                    <div class="input-group design-widget-slide-modal-tab6-input-group-medium">
                        <input type="number" class="form-control" v-model="row.slide_paging_size" placeholder="10">
                        <span class="input-group-addon">px</span>
                    </div>
                </div>

                <div class="design-widget-slide-modal-tab6-option-group">
                    <label class="design-widget-slide-modal-tab6-color-label">위치</label>
                    <select class="form-control design-widget-slide-modal-tab6-select-medium" v-model="row.slide_paging_position">
                        <option value="이미지 바깥">이미지 바깥</option>
                        <option value="이미지 겹침">이미지 겹침</option>
                    </select>
                </div>
            </div>

            <!-- 좌우 이동 화살표 -->
            <div class="design-widget-slide-modal-tab6-section">
                <label class="design-widget-slide-modal-tab6-section-label">좌우 이동 화살표</label>
                <div class="design-widget-slide-modal-tab6-paging-buttons">
                    <button class="design-widget-slide-modal-tab6-paging-btn" :class="{'design-widget-slide-modal-tab6-active': row.arrow_style === 'none'}" @click="row.arrow_style = 'none'">
                        사용안함
                    </button>
                    <button class="design-widget-slide-modal-tab6-paging-btn" :class="{'design-widget-slide-modal-tab6-active': row.arrow_style === 'chevron'}" @click="row.arrow_style = 'chevron'">
                        <i class="fal fa-chevron-left"></i>
                        <i class="fal fa-chevron-right"></i>
                    </button>
                    <button class="design-widget-slide-modal-tab6-paging-btn" :class="{'design-widget-slide-modal-tab6-active': row.arrow_style === 'arrow'}" @click="row.arrow_style = 'arrow'">
                        <i class="fal fa-arrow-left"></i>
                        <i class="fal fa-arrow-right"></i>
                    </button>
                    <button class="design-widget-slide-modal-tab6-paging-btn" :class="{'design-widget-slide-modal-tab6-active': row.arrow_style === 'caret'}" @click="row.arrow_style = 'caret'">
                        <i class="fa fa-caret-left"></i>
                        <i class="fa fa-caret-right"></i>
                    </button>
                    <button class="design-widget-slide-modal-tab6-paging-btn" :class="{'design-widget-slide-modal-tab6-active': row.arrow_style === 'angle'}" @click="row.arrow_style = 'angle'">
                        <i class="fa fa-angle-left"></i>
                        <i class="fa fa-angle-right"></i>
                    </button>
                </div>
            </div>

            <!-- 화살표 색상, 배경 색상 -->
            <div class="design-widget-slide-modal-tab6-options-row">
                <div class="design-widget-slide-modal-tab6-option-group">
                    <label class="design-widget-slide-modal-tab6-color-label">화살표 색상</label>
                    <external-picker v-model="row.arrow_color"></external-picker>
                </div>

                <div class="design-widget-slide-modal-tab6-option-group">
                    <label class="design-widget-slide-modal-tab6-color-label">배경 색상</label>
                    <external-picker v-model="row.arrow_bg_color"></external-picker>
                </div>

                <div class="design-widget-slide-modal-tab6-option-group">
                    <label class="design-widget-slide-modal-tab6-color-label">크기</label>
                    <div class="input-group design-widget-slide-modal-tab6-input-group-medium">
                        <input type="number" class="form-control" v-model="row.arrow_size" placeholder="10">
                        <span class="input-group-addon">px</span>
                    </div>
                </div>

                <div class="design-widget-slide-modal-tab6-option-group">
                    <label class="design-widget-slide-modal-tab6-color-label">위치</label>
                    <select class="form-control design-widget-slide-modal-tab6-select-medium" v-model="row.arrow_position">
                        <option value="이미지 바깥">이미지 바깥</option>
                        <option value="이미지 겹침">이미지 겹침</option>
                    </select>
                </div>
            </div>
        </div>
    </div>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide-modal-tab6",object : {
            template: "#design-widget-slide-modal-tab6-template",
            props: {
                row : {type : Object, default : null},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-slide-modal-tab6",
                    component_idx: "",

                    table : null,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {

            },
            computed: {

            },
            watch: {
                async "row.arrow_style"(value, old_value) {
                    if(value === 'angle') {
                        this.row.arrow_bg_color = "#EDEDED99";
                    }else {
                        this.row.arrow_bg_color = "#EDEDED00";
                    }
                },
                async "row.slide_paging"(value, old_value) {
                    if(value === 'dots') {
                        this.row.slide_paging_size = "10";
                    }else if(value === 'bullet') {
                        this.row.slide_paging_size = "20";
                    }
                }
            }
        }});
</script>

<style>
    /* 옵션 행 (그림자, 테두리, 모서리) */
    .design-widget-slide-modal-tab6-options-row {
        display: flex;
        align-items: center;
        gap: 20px;
        margin-top: 20px;
        margin-bottom: 20px;
        flex-wrap: wrap;
    }

    .design-widget-slide-modal-tab6-option-group {
        display: inline-flex;
        align-items: center;
        gap: 8px;
    }

    .design-widget-slide-modal-tab6-select-small {
        width: 80px;
    }

    /* 숫자 입력 wrapper */
    .design-widget-slide-modal-tab6-number-input-wrapper {
        position: relative;
        width: 80px;
    }

    .design-widget-slide-modal-tab6-number-input-wrapper input {
        padding-right: 25px;
        width: 100%;
    }

    .design-widget-slide-modal-tab6-with-icon input {
        padding-left: 28px !important;
    }

    .design-widget-slide-modal-tab6-input-icon-left {
        position: absolute;
        left: 6px;
        top: 50%;
        transform: translateY(-50%);
        width: 16px;
        height: 16px;
        pointer-events: none;
        z-index: 10;
    }

    .design-widget-slide-modal-tab6-number-stepper {
        position: absolute;
        right: 1px;
        top: 1px;
        bottom: 1px;
        width: 20px;
        display: flex;
        flex-direction: column;
        border-left: 1px solid #ccc;
    }

    .design-widget-slide-modal-tab6-stepper-up,
    .design-widget-slide-modal-tab6-stepper-down {
        flex: 1;
        border: none;
        background: white;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 0;
    }

    .design-widget-slide-modal-tab6-stepper-up:hover,
    .design-widget-slide-modal-tab6-stepper-down:hover {
        background: #f0f0f0;
    }

    .design-widget-slide-modal-tab6-stepper-up {
        border-bottom: 1px solid #ccc;
    }

    /* 섹션 */
    .design-widget-slide-modal-tab6-section {
        margin-bottom: 20px;
    }

    .design-widget-slide-modal-tab6-section-label {
        font-size: 14px;
        color: #333;
        font-weight: bold;
        margin-bottom: 12px;
        display: block;
    }

    /* 페이징 버튼 */
    .design-widget-slide-modal-tab6-paging-buttons {
        display: flex;
        gap: 10px;
    }

    .design-widget-slide-modal-tab6-paging-btn {
        width: 100px;
        height: 50px;
        border: 2px solid #ddd;
        border-radius: 6px;
        background: white;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 8px;
        transition: all 0.2s;
    }

    .design-widget-slide-modal-tab6-paging-btn:hover {
        border-color: #ff6b6b;
    }

    .design-widget-slide-modal-tab6-paging-btn.design-widget-slide-modal-tab6-active {
        border-color: #ff6b6b;
        background: #fff5f5;
    }

    .design-widget-slide-modal-tab6-dot {
        width: 8px;
        height: 8px;
        border-radius: 50%;
        background: #ccc;
    }

    .design-widget-slide-modal-tab6-bar {
        width: 20px;
        height: 3px;
        background: #ccc;
        border-radius: 2px;
    }

    .design-widget-slide-modal-tab6-bar-active {
        background: #4A90E2;
    }

    .design-widget-slide-modal-tab6-dash {
        width: 15px;
        height: 2px;
        background: #ccc;
    }

    .design-widget-slide-modal-tab6-dash-active {
        background: #333;
        width: 25px;
    }

    .design-widget-slide-modal-tab6-number-dot {
        width: 20px;
        height: 20px;
        border-radius: 50%;
        background: #f0f0f0;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 11px;
        color: #999;
    }

    .design-widget-slide-modal-tab6-number-active {
        background: #4A90E2;
        color: white;
    }

    .design-widget-slide-modal-tab6-fraction {
        font-size: 13px;
        color: #666;
        background: #f0f0f0;
        padding: 4px 10px;
        border-radius: 3px;
    }

    /* 색상 라벨 */
    .design-widget-slide-modal-tab6-color-label {
        font-size: 13px;
        color: #666;
        font-weight: bold;
        margin-bottom: 0;
        white-space: nowrap;
    }

    .design-widget-slide-modal-tab6-input-group-medium {
        width: 100px;
    }

    .design-widget-slide-modal-tab6-input-group-medium input {
        height: 38px;
    }

    .design-widget-slide-modal-tab6-input-group-medium .input-group-addon {
        height: 38px;
        line-height: 38px;
        padding: 0 12px;
    }

    .design-widget-slide-modal-tab6-select-medium {
        width: 130px;
        height: 38px;
    }
</style>
<script type="text/x-template" id="design-widget-slide-modal-video-template">
    <div v-if="load">
        <external-bs-modal v-model="modal">
            <template v-slot:header>
                <h5 class="modal-title" id="exampleModalLabel">
                    {{modal.video_put ? '동영상 개별 설정' : '동영상 추가'}}
                </h5>
            </template>

            <!-- body -->
            <template v-slot:default>
                <div>
                    <div class="row">
                        <div class="col-md-12 text-center">
                            <img :src="video.src" style="max-width: 100%; max-height: 500px;">
                        </div>
                    </div>

                    <h4 class="jd-image-modal-title">
                        <img src="/file/icon/slide/동영상.svg"> 동영상 설정
                    </h4>

                    <div class="layout-setting-item">
                        <label class="setting-label">유튜브 링크 입력</label>
                        <input type="text" class="form-control" v-model="video.url" placeholder="https://youtube/">
                    </div>
                    <p style="color: #999; font-size: 12px; margin-bottom: 15px;">
                        ※ 유튜브 동영상 링크(URL)로만 첨부가 가능합니다.
                    </p>
                    <p v-if="video_error" style="color:#e74c3c; font-size:12px; margin-top:-8px; margin-bottom:15px;">
                        유효한 링크가 아닙니다.
                    </p>

                    <div class="design-widget-slide-modal-tab5-checkbox-group">
                        <label class="design-widget-slide-modal-tab5-checkbox-item">
                            <input type="checkbox" v-model="video.auto">
                            <span class="design-widget-slide-modal-tab5-checkbox-label">자동 재생</span>
                        </label>


                        <label class="design-widget-slide-modal-tab5-checkbox-item">
                            <input type="checkbox" v-model="video.loop">
                            <span class="design-widget-slide-modal-tab5-checkbox-label">반복 재생</span>
                        </label>

                        <label class="design-widget-slide-modal-tab5-checkbox-item">
                            <input type="checkbox" v-model="video.mute" :disabled="video.auto">
                            <span class="design-widget-slide-modal-tab5-checkbox-label">음소거</span>
                        </label>
                    </div>



                </div>
            </template>


            <template v-slot:footer>
                <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
                <button type="button" class="btn btn-orange save" @click="save">저장</button>
            </template>
        </external-bs-modal>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide-modal-video",object : {
            template: "#design-widget-slide-modal-video-template",
            props: {
                modelValue : {type: Object, default: {}},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-slide-modal-video",
                    component_idx: "",

                    temp : {
                        $table : "",
                    },

                    video: {
                        url : "",
                        auto : true,
                        loop : true,
                        mute : true,
                    },
                    video_error: false,
                    rows : [],
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
            },
            async mounted() {
                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {
                save() {
                    // ✅ 입력 바뀌면 에러 초기화
                    this.video_error = false;

                    const id = this.extractYoutubeId(this.video.url);

                    // ✅ 유효하지 않으면 메시지 띄우고 종료
                    if (!id) {
                        this.video_error = true;
                        return;
                    }

                    if(this.modelValue.video_put) {
                        Object.assign(this.modelValue.video_put, this.video);
                    }else {
                        this.modelValue.row.images.push({
                            type: "video",
                            ...this.video,
                            src: 'https://img.youtube.com/vi/' + id + '/mqdefault.jpg',
                            priority: this.modelValue.row.images.length + this.modelValue.row.upload_file.length,
                        });
                    }



                    this.modal = { ...this.modal, status: false };
                },
                extractYoutubeId(url) {
                    if (!url) return "";

                    // 공백 제거
                    url = String(url).trim();

                    // ID만 들어온 경우도 허용(11자리)
                    if (/^[a-zA-Z0-9_-]{11}$/.test(url)) return url;

                    try {
                        const u = new URL(url);

                        // youtu.be/ID
                        if (u.hostname.includes("youtu.be")) {
                            const id = u.pathname.split("/").filter(Boolean)[0];
                            return /^[a-zA-Z0-9_-]{11}$/.test(id) ? id : "";
                        }

                        // youtube.com/*
                        if (u.hostname.includes("youtube.com")) {
                            // watch?v=ID
                            const v = u.searchParams.get("v");
                            if (v && /^[a-zA-Z0-9_-]{11}$/.test(v)) return v;

                            // /embed/ID, /shorts/ID, /v/ID
                            const parts = u.pathname.split("/").filter(Boolean);
                            const idx = parts.findIndex(p => ["embed", "shorts", "v"].includes(p));
                            if (idx !== -1 && parts[idx + 1] && /^[a-zA-Z0-9_-]{11}$/.test(parts[idx + 1])) {
                                return parts[idx + 1];
                            }
                        }

                        return "";
                    } catch (e) {
                        // URL 파싱 실패
                        return "";
                    }
                },
            },
            computed: {
                modal: {
                    get() {
                        return this.modelValue;
                    },
                    set(value) {
                        this.$emit('update:modelValue', value);
                    }
                },
                btnStyle() {
                    return {
                        width: this.video.btn_width + 'px',
                        height: this.video.btn_height + 'px',
                        fontSize: this.video.btn_font_size + 'px',
                        color: this.video.btn_color,
                        backgroundColor: this.video.btn_bg_color,
                        borderRadius: this.video.btn_border_radius + 'px',
                        border: this.video.btn_border_width + 'px solid ' + this.video.btn_border_color
                    };
                }
            },
            watch: {
                async "video.auto"(value, old_value) {
                    if(value) this.video.mute = true;
                },
                async "modelValue.status"(value, old_value) {
                    const $modal = $("#widget_setting_jd");

                    if(!value) {
                        this.video =  {
                            url : "",
                            loop : true,
                            content_layout : "none",
                            content_position : "middle-center",
                            content_margin_top : "0",
                            content_margin_left : "0",
                            content_margin_right : "0",
                            content_margin_bottom : "0",
                            each_text_content : "",
                            btn_width : 150,
                            btn_height : 40,
                            btn_color : "#000000",
                            btn_bg_color : "#ffffff",
                            btn_border_radius : 0,
                            btn_border_width : 1,
                            btn_border_color : "#000000",
                            btn_font_size : 16,
                            btn_text : "",
                        };
                        this.video_error = false;
                        this.modal.video_put = null;

                        if($modal.data('bs.modal')) {
                            $modal.data('bs.modal').options.backdrop = true;
                            $modal.data('bs.modal').options.keyboard = true;
                        }
                        return false;
                    }

                    if(this.modal.video_put) {
                        this.video = { ...this.modal.video_put };
                    }

                    if($modal.data('bs.modal')) {

                        $modal.data('bs.modal').options.backdrop = 'static';
                        $modal.data('bs.modal').options.keyboard = false;
                    }
                }
            }
        }});
</script><script type="text/x-template" id="design-widget-slide-modal-template">
    <div v-if="load" class="jaydream">
        <div>
            <!--<design-widget-slide-modal-tab1 :row></design-widget-slide-modal-tab1>-->
            <!--<hr>-->
            <design-widget-slide-modal-tab3 :row :site_code></design-widget-slide-modal-tab3>
            <hr>
            <design-widget-slide-modal-tab2 :row v-if="row.slide_type == '일반 슬라이드'"></design-widget-slide-modal-tab2>
            <hr>
            <design-widget-slide-modal-tab4 :row></design-widget-slide-modal-tab4>
            <hr>
            <design-widget-slide-modal-tab5 :row></design-widget-slide-modal-tab5>
            <hr>
            <design-widget-slide-modal-tab6 :row></design-widget-slide-modal-tab6>
            <hr>
            <div class="panel panel-default" style="margin-bottom: 10px;">
                <div class="panel-heading" style="cursor: pointer; display: flex; justify-content: space-between; align-items: center;" data-toggle="collapse" :data-target="'#slideScrollSection-' + component_idx">
                    <div>
                        <img src="/file/icon/scroll.svg" style="height: 23px">
                        <span class="jd-effect-text">스크롤 애니메이션</span>
                        <button class="jd-effect-btn">
                            <img :src="`/file/icon/${row.option_scroll}.svg`" style="width: 16px; height: 16px;">
                            <span class="fs-14">{{row.option_scroll}}</span>
                        </button>
                    </div>
                    <i class="glyphicon glyphicon-chevron-down"></i>
                </div>
                <div :id="'slideScrollSection-' + component_idx" class="panel-collapse collapse">
                    <div class="panel-body">
                        <div class="row" style="display: flex;">
                            <div class="col-md-6" style="border-right: 1px solid #ddd;">
                                <div class="jd-action-button" :class="{active : row.option_scroll == '효과없음'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '효과없음'">
                                    <img src="/file/icon/효과없음.svg">
                                    <span>효과없음</span>
                                </div>
                                <div class="jd-action-button" :class="{active : row.option_scroll == '페이드'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '페이드'">
                                    <img src="/file/icon/페이드.svg">
                                    <span>페이드</span>
                                </div>
                                <div class="jd-action-button" :class="{active : row.option_scroll == '줌인'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '줌인'">
                                    <img src="/file/icon/줌인.svg">
                                    <span>줌인</span>
                                </div>
                                <div class="jd-action-button" :class="{active : row.option_scroll == '줌아웃'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '줌아웃'">
                                    <img src="/file/icon/줌아웃.svg">
                                    <span>줌아웃</span>
                                </div>
                                <div class="jd-action-button" :class="{active : row.option_scroll == '플립'}" @click=" row.option_scroll_direction = 'up'; row.option_scroll = '플립'">
                                    <img src="/file/icon/플립.svg">
                                    <span>플립</span>
                                </div>
                            </div>

                            <div class="col-md-6" style="display : flex;">
                                <template v-if="row.option_scroll == '효과없음'">
                                    <div style="flex: 1; text-align: center; align-content: center;">
                                        <span style="font-size: large; color : #959595; ">스크롤 애니메이션을 선택하지 않았습니다.</span>
                                    </div>
                                </template>

                                <template v-else>
                                    <div style="display: flex; flex-direction: column; gap: 15px; padding: 15px;">
                                        <!-- 방향 -->
                                        <div style="display: flex; align-items: center; gap: 10px;">
                                            <label style="margin: 0; font-weight: 600; font-size: 14px;">방향</label>
                                            <div style="display: flex; gap: 6px;">
                                                <button class="jd-direction-btn" v-if="row.option_scroll != '플립'" @click="row.option_scroll_direction = 'center'" :class="{active : row.option_scroll_direction == 'center'}">
                                                    <i class="fas fa-circle" style="font-size: 12px;"></i>
                                                </button>
                                                <button class="jd-direction-btn" @click="row.option_scroll_direction = 'up'" :class="{active : row.option_scroll_direction == 'up'}">
                                                    <i class="fas fa-arrow-up" style="font-size: 14px;"></i>
                                                </button>
                                                <button class="jd-direction-btn" @click="row.option_scroll_direction = 'down'" :class="{active : row.option_scroll_direction == 'down'}">
                                                    <i class="fas fa-arrow-down" style="font-size: 14px;"></i>
                                                </button>
                                                <button class="jd-direction-btn" @click="row.option_scroll_direction = 'left'" :class="{active : row.option_scroll_direction == 'left'}">
                                                    <i class="fas fa-arrow-left" style="font-size: 14px;"></i>
                                                </button>
                                                <button class="jd-direction-btn" @click="row.option_scroll_direction = 'right'" :class="{active : row.option_scroll_direction == 'right'}">
                                                    <i class="fas fa-arrow-right" style="font-size: 14px;"></i>
                                                </button>
                                            </div>
                                        </div>

                                        <div style="display: flex; align-items: center; gap: 5px;">
                                            <label style="margin: 0; font-weight: 600; font-size: 14px;">지속시간</label>
                                            <input type="number" v-number v-model="row.option_scroll_duration" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                            <span style="font-size: 14px; color: #666;">ms</span>

                                            <label style="margin: 0; font-weight: 600; font-size: 14px; margin-left : 10px;">지연시간</label>
                                            <input type="number" v-number v-model="row.option_scroll_delay" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                            <span style="font-size: 14px; color: #666;">ms</span>
                                        </div>

                                    </div>
                                </template>

                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div class="modal-footer" style="position:sticky;right:0;bottom:0;">
            <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
            <button type="button" class="btn btn-orange save" @click="postData()">저장</button>
        </div>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide-modal",object : {
            template: "#design-widget-slide-modal-template",
            props: {
                primary : {type : String, default : ""},
                widget_code : {type : String, default : "1"},
                site_code : {type : String, default : "st68f18a193702e"},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-slide-modal",
                    component_idx: "",

                    table : null,
                    row: {
                        widget_code : this.widget_code,
                        user_idx : "",

                        // tab1
                        slide_type : "일반 슬라이드",
                        // tab2
                        slide_style : "Default",
                        // tab3
                        images : [],
                        upload_file : [], // 사용안하고 업로드만 체크하는요

                        // tab4 레이아웃
                        // 비율
                        ratio : "채우기",
                        // 높이
                        height : "600",
                        mobile_height : "480",
                        // 한줄당 이미지 개수
                        line_image_pc : 4,
                        line_image_mobile : 2,
                        //컨텐츠 사이 간격
                        gap  : "10",

                        // tab5 슬라이드 설정
                        // 전환효과
                        slide_effect :  "슬라이드",
                        // 전환속도
                        slide_speed : "0.2",
                        // 전환 대기 시간
                        slide_wait : "5",
                        // 전환 방식
                        slide_change : "개별 전환",
                        // 슬라이드 반복재생
                        slide_loop : true,
                        // 마우스 오버시 멈춤
                        slide_over : true,
                        // 드래그로 넘기기
                        slide_drag : true,
                        // 마우스 클릭 동작
                        slide_click : "동작없음",
                        // 스크롤
                        option_scroll : "효과없음",
                        option_scroll_direction : "center",
                        option_scroll_duration : "1000",
                        option_scroll_delay : "0",

                        // tab6 스타일 설정
                        // 그림자
                        box_shadow_use : false,
                        box_shadow_opacity : "20",
                        // 테두리
                        border_use : false,
                        border_width : "1",
                        border_color : "#000000",
                        //모서리둥글게
                        border_radius_use : false,
                        border_radius_px : "6",
                        //페이징 버튼
                        slide_paging : "none",
                        slide_paging_on_color : "#FFFFFF",
                        slide_paging_off_color : "#D9D9D9",
                        slide_paging_size : "10",
                        slide_paging_position : "이미지 겹침",
                        //좌우 이동 화살표
                        arrow_style : "none",
                        arrow_color : "#666666",
                        arrow_bg_color : "#EDEDED00",
                        arrow_size : "24",
                        arrow_position : "이미지 겹침"
                    },

                    rows : [],
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                // await this.table.get(this.rows,{paging : 10})

                this.sessions = await this.session.all();
                this.row.user_idx = this.sessions.mem_idx;

                this.table = await this.api.table("jd_widget_slide");
                this.table.where('widget_code',this.widget_code);
                let row = await this.table.get();
                if(row.length > 0) {
                    this.row = row[0];

                    // 실제 사용하지 않는 컬럼이므로 미리 지정
                    this.row.upload_file = [];
                }
                this.normalizeScrollOptions();

                this.load = true;

                this.$nextTick(async () => {
                    // 해당부분에 퍼블리싱 라이브러리,플러그인 선언부분 하시면 됩니다 ex) swiper
                });
            },
            updated() {

            },
            methods: {
                normalizeScrollOptions() {
                    const defaults = {
                        option_scroll: "효과없음",
                        option_scroll_direction: "center",
                        option_scroll_duration: "1000",
                        option_scroll_delay: "0",
                    };

                    Object.keys(defaults).forEach((key) => {
                        if(this.row[key] === undefined || this.row[key] === null || this.row[key] === '') {
                            this.row[key] = defaults[key];
                        }
                    });

                    if(this.row.option_scroll === '플립' && this.row.option_scroll_direction === 'center') {
                        this.row.option_scroll_direction = 'up';
                    }
                },
                async saveImageFile(file, memo) {
                    const res = await this.lib.ajax("file_save", {
                        "user_idx": this.row.user_idx,
                        "upload_file": file,
                        "width": file.width,
                        "height": file.height,
                        "memo": memo,
                        "widget_code": this.row.widget_code,
                        "site_code": this.site_code,
                    });

                    return res['file'];
                },
                clearReplaceUploadFile(item) {
                    if(!item) return;
                    delete item._replace_upload_file;
                },
                async saveReplaceImageFiles(items, memo) {
                    if(!Array.isArray(items)) return;

                    for(const item of items) {
                        if(!item || !item._replace_upload_file) continue;

                        const savedFile = await this.saveImageFile(item._replace_upload_file, memo);
                        item.src = savedFile.src;
                        item.type = item.type || "image";
                        this.clearReplaceUploadFile(item);
                    }
                },
                async postData() {
                    if(this.row.images.length === 0 && this.row.upload_file.length === 0) {
                        await this.lib.alert('이미지를 1개 이상 추가해주세요.');
                        return;
                    }

                    if(window.design) window.design.loadingOn()

                    await this.saveReplaceImageFiles(this.row.images, "jd_widget_multi");

                    // 업로드 파일 저장
                    for(const [idx, file] of this.row.upload_file.entries()) {
                        const uploadFile = file._replace_upload_file || file;
                        let savedFile = await this.saveImageFile(uploadFile, "jd_widget_multi");

                        // 기존 file 객체에서 _type과 upload 관련 제거하고 src만 교체
                        let newImage = { ...file };
                        newImage.src = savedFile.src;
                        newImage.type = newImage.type || "image";
                        this.clearReplaceUploadFile(newImage);

                        // images 배열에 추가
                        this.row.images.push(newImage);
                    }

                    // upload_file 비우기
                    this.row.upload_file = [];

                    await this.table.post(this.row,{return : true});

                    window.design.loadingOff()
                    window.JAYDREAM_VUE_GLOBAL[this.widget_code].reload = true;
                    $(this.$el).closest('.modal').modal('hide');
                }
            },
            computed: {

            },
            watch: {
                async "row.slide_type"(value, old_value) {
                    if(value == '비주얼 섹션') {
                        this.row.slide_style = "일반 슬라이드";
                        this.row.box_shadow_use = false;
                        this.row.box_shadow_opacity = "20";
                        this.row.border_use = false;
                        this.row.border_width = "1";
                        this.row.border_color = "#000000";
                        this.row.border_radius_use = false;
                        this.row.border_radius_px = "6";
                    }
                }
            }
        }});
</script>
<style>

</style>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css">
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>

<script type="text/x-template" id="design-widget-slide-template">
    <div v-if="load">
        <template v-if="row.idx">
            <div :id="'slide-' + component_idx"
                 class="jd-slide-widget"
                 ref="slideElement"
                 :class="{ 'jd-slide-shadow-enabled': row.box_shadow_use }"
                 :style="slideWidgetStyle">
                <div :class="row.arrow_position === '이미지 바깥' ? 'jd-slide-arrow-outside-wrap' : ''">

                    <!-- 화살표 바깥 왼쪽 -->
                    <div v-if="row.arrow_style !== 'none' && row.arrow_position === '이미지 바깥'"
                         class="swiper-button-prev jd-arrow-outside-btn"
                         :id="'swiper-prev-' + component_idx"
                         :style="arrowStyle">
                    </div>

                    <div class="swiper" :id="'swiper-' + component_idx" :class="slideStyleClass" :style="swiperContainerStyle">
                        <div class="swiper-wrapper">
                            <div class="swiper-slide" v-for="(img, index) in renderSlideImages" :key="index">
                                <!-- YouTube 비디오 -->
                                <template v-if="img.type === 'video'">
                                    <div class="jd-slide-video-wrap" :style="slideWrapStyle">
                                        <div class="jd-slide-shadow-frame" :style="slideFrameStyle">
                                            <div :id="'yt-player-' + component_idx + '-' + index" class="jd-slide-youtube"></div>
                                            <!-- 마우스 이벤트 차단 -->
                                            <div class="jd-slide-video-shield"></div>
                                            <!-- 텍스트/버튼 콘텐츠 오버레이 -->
                                            <div v-if="img.each_text_content || img.btn_text || img.content_layout === 'text_button'"
                                                 class="jd-slide-content-overlay"
                                                 :style="contentPositionStyle(img)">
                                                <div v-if="img.each_text_content" class="jd-slide-text" v-html="img.each_text_content"></div>
                                                <a v-if="img.btn_text || img.content_layout === 'text_button'"
                                                   class="jd-slide-btn"
                                                   :style="btnStyle(img)"
                                                   :href="img.url"
                                                   target="_blank"
                                                   @click.stop>{{ img.btn_text || 'Button' }}</a>
                                            </div>
                                        </div>
                                    </div>
                                </template>

                                <!-- 일반 이미지 -->
                                <template v-else>
                                    <div class="jd-slide-image-wrap" :style="slideWrapStyle"
                                         @click="img.content_layout !== 'button' && img.content_layout !== 'text_button' ? handleImageClick(img) : null">
                                        <div class="jd-slide-shadow-frame" :style="slideFrameStyle">
                                            <img :src="img.src"
                                                 class="jd-slide-img"
                                                 :style="imageStyle" :title="img.alt_text || ''"
                                            >
                                            <div v-if="img.content_layout && img.content_layout !== 'none'"
                                                 class="jd-slide-content-overlay"
                                                 :style="contentPositionStyle(img)">
                                                <div v-if="img.content_layout === 'text' || img.content_layout === 'text_button'"
                                                     class="jd-slide-text"
                                                     v-html="img.each_text_content"></div>
                                                <a v-if="img.content_layout === 'button' || img.content_layout === 'text_button'"
                                                   class="jd-slide-btn"
                                                   :style="{ ...btnStyle(img), marginTop: img.content_layout === 'text_button' ? '20px' : '0' }"
                                                   @click="handleImageClick(img)"
                                                   :target="img.option_mouse_click_target || '_self'"
                                                   @click.stop>{{ img.btn_text || 'Button' }}</a>
                                            </div>
                                        </div>
                                    </div>
                                </template>
                            </div>
                        </div>

                        <!-- 페이징 겹침일때만 안에 -->
                        <div v-if="row.slide_paging !== 'none' && row.slide_paging_position !== '이미지 바깥'"
                             class="swiper-pagination"
                             :id="'swiper-pagination-' + component_idx">
                        </div>

                        <!-- 화살표 겹침일때만 안에 -->
                        <template v-if="row.arrow_style !== 'none' && row.arrow_position !== '이미지 바깥'">
                            <div class="swiper-button-prev" :id="'swiper-prev-' + component_idx" :style="arrowStyle"></div>
                            <div class="swiper-button-next" :id="'swiper-next-' + component_idx" :style="arrowStyle"></div>
                        </template>
                    </div>

                    <!-- 화살표 바깥 오른쪽 -->
                    <div v-if="row.arrow_style !== 'none' && row.arrow_position === '이미지 바깥'"
                         class="swiper-button-next jd-arrow-outside-btn"
                         :id="'swiper-next-' + component_idx"
                         :style="arrowStyle">
                    </div>
                </div>

                <!-- 페이징 바깥일때 swiper 밖에 -->
                <div v-if="row.slide_paging !== 'none' && row.slide_paging_position === '이미지 바깥'"
                     class="swiper-pagination jd-pagination-outside"
                     :id="'swiper-pagination-' + component_idx">
                </div>
            </div>
        </template>

        <template v-else>
            <div class="jd-imageupload-box jd-widget-placeholder">
                <img class="jd-widget-placeholder-icon" src="/file/icon/widget-add/slide.svg" alt="">
                <p class="jd-imageupload-text">더블 클릭하여 슬라이드를 추가하세요.</p>
            </div>
        </template>

        <div v-if="viewImage" class="widget-image-overlay" @click="viewImage = ''">
            <img :src="viewImage" @click.stop>
            <button class="widget-overlay-close" @click="viewImage = ''">&times;</button>
        </div>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-slide",object : {
            template: "#design-widget-slide-template",
            props: {
                primary : {type : String, default : ""},
                user_idx : {type : String, default : ""},
                widget_code : {type : String, default : ""},
            },
            data() {
                return {
                    load : false,
                    component_name : "design-widget-slide",
                    component_idx: "",

                    table : null,
                    row: {},
                    rows : [],

                    swiperInstance: null,
                    ytPlayers: {},
                    currentSlideIndex: 0,
                    viewImage: '',
                    scrollEvent: false,
                    observer: null,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name;
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_slide");
                this.table.where('widget_code', this.widget_code);
                let row = await this.table.get();
                if(row.length > 0) this.row = row[0];
                this.normalizeScrollOptions();

                // images JSON 파싱
                if(this.row.images && typeof this.row.images === 'string') {
                    try { this.row.images = JSON.parse(this.row.images); } catch(e) {}
                }

                this.load = true;

                this.$nextTick(() => {
                    if(!this.row.idx) {
                        $(`#${this.widget_code} .firewall`).addClass('widget_setting_btn');
                        return;
                    }
                    this.initSwiper();
                    this.initArrow();
                    this.initYouTubeAPI();
                    this.initScrollObserver();
                });
            },
            methods: {
                normalizeScrollOptions() {
                    const defaults = {
                        option_scroll: "효과없음",
                        option_scroll_direction: "center",
                        option_scroll_duration: "1000",
                        option_scroll_delay: "0",
                    };

                    Object.keys(defaults).forEach((key) => {
                        if(this.row[key] === undefined || this.row[key] === null || this.row[key] === '') {
                            this.row[key] = defaults[key];
                        }
                    });

                    if(this.row.option_scroll === '플립' && this.row.option_scroll_direction === 'center') {
                        this.row.option_scroll_direction = 'up';
                    }
                },
                applyFractionColor() {
                    const el = document.getElementById(`swiper-pagination-${this.component_idx}`);
                    if(!el) return;

                    const current = el.querySelector('.swiper-pagination-current');
                    const total = el.querySelector('.swiper-pagination-total');

                    if(current) current.style.color = this.row.slide_paging_on_color || '#007aff';
                    if(total) total.style.color = this.row.slide_paging_off_color || 'rgba(0,0,0,0.2)';
                },
                // ── Swiper 초기화 ──
                initSwiper() {
                    const self = this;
                    const effectMap = {
                        '슬라이드': 'slide',
                        '페이드': 'fade',
                        '큐브': 'cube',
                        '플립': 'flip',
                    };
                    const effect = effectMap[this.row.slide_effect] || 'slide';
                    const waitTime = parseFloat(this.row.slide_wait || 5) * 1000;
                    const speed = parseFloat(this.row.slide_speed || 0.2) * 1000;
                    const gap = parseInt(this.row.gap) || 10;
                    const pcPerView = parseInt(this.row.line_image_pc) || 4;
                    const mobilePerView = parseInt(this.row.line_image_mobile) || 2;
                    const style = this.row.slide_style || 'Default';
                    const originalSlideCount = this.slideImages.length;
                    const renderSlideCount = this.renderSlideImages.length;
                    const hasRenderClones = renderSlideCount > originalSlideCount;
                    const requestedLoop = this.row.slide_loop !== false;
                    const loopEnabled = requestedLoop && renderSlideCount > 1;

                    const config = {
                        speed: speed,
                        loop: loopEnabled,
                        allowTouchMove: this.row.slide_drag !== false,
                        autoplay: {
                            delay: waitTime,
                            disableOnInteraction: false,
                            pauseOnMouseEnter: self.row.slide_over === true,
                        },
                        on: {
                            slideChangeTransitionStart: function() {
                                self.onSlideLeave(self.currentSlideIndex);
                            },
                            slideChangeTransitionEnd: function() {
                                self.currentSlideIndex = this.realIndex;
                                self.onSlideEnter(this.realIndex);
                            },
                            init: function() {
                                self.applyPaginationState(this);
                            },
                            slideChange: function() {
                                self.applyPaginationState(this);
                            },
                            reachEnd: function() {
                                if(!self.row.slide_loop) {
                                    this.autoplay.stop();
                                }
                            },
                        }
                    };

                    // 스타일별 설정
                    switch(style) {
                        case 'Vertical':
                            config.direction = 'vertical';
                            config.effect = effect;
                            break;

                        case 'Slides per view':
                            config.slidesPerView = mobilePerView;
                            config.spaceBetween = gap;

                            if(this.row.slide_change === '묶음 전환') {
                                config.slidesPerGroup = mobilePerView;
                            }

                            config.breakpoints = {
                                768: {
                                    slidesPerView: pcPerView,
                                    slidesPerGroup: this.row.slide_change === '묶음 전환' ? pcPerView : 1,
                                }
                            };
                            break;

                        case 'Centered auto':
                            config.slidesPerView = 1.5;
                            config.spaceBetween = 0;
                            config.centeredSlides = true;
                            config.breakpoints = {
                                768: { slidesPerView: 2.5 }
                            };
                            break;

                        default: // Default
                            config.effect = effect;
                            break;
                    }

                    if(effect === 'fade' && style === 'Default') {
                        config.fadeEffect = { crossFade: true };
                    }

                    if(this.row.slide_paging !== 'none') {
                        const onColor = this.row.slide_paging_on_color || '#007aff';
                        const offColor = this.row.slide_paging_off_color || 'rgba(0,0,0,0.2)';

                        config.pagination = {
                            el: `#swiper-pagination-${this.component_idx}`,
                            clickable: true,
                        };

                        if(this.row.slide_paging == 'fraction') {
                            config.pagination.type = this.row.slide_paging
                        }

                        if(hasRenderClones || this.row.slide_paging == 'bullet') {
                            config.pagination.renderBullet = function (index, className) {
                                if(hasRenderClones && index >= originalSlideCount) return '';
                                const label = self.row.slide_paging == 'bullet' ? (index + 1) : '';
                                return '<span class="' + className + '" data-slide-original-index="' + index + '">' + label + "</span>";
                            }
                        }


                    }

                    if(this.row.arrow_style !== 'none') {
                        config.navigation = {
                            nextEl: `#swiper-next-${this.component_idx}`,
                            prevEl: `#swiper-prev-${this.component_idx}`,
                        };
                    }

                    this.swiperInstance = new Swiper(`#swiper-${this.component_idx}`, config);
                },

                initArrow() {
                    const style = this.row.arrow_style;
                    if(!style || style === 'none') return;

                    const iconMap = {
                        chevron: ['fal fa-chevron-left',   'fal fa-chevron-right'],
                        arrow:   ['fal fa-arrow-left',    'fal fa-arrow-right'],
                        caret:   ['fa fa-caret-left',     'fa fa-caret-right'],
                        angle:   ['fa fa-angle-left',     'fa fa-angle-right'],
                    };

                    const icons = iconMap[style];
                    if(!icons) return;

                    const prev = document.getElementById(`swiper-prev-${this.component_idx}`);
                    const next = document.getElementById(`swiper-next-${this.component_idx}`);

                    if(prev) {
                        const size = (parseInt(this.row.arrow_size) || 24) * 2;
                        prev.innerHTML = style === 'angle'
                            ? `<span class="jd-arrow-circle" style="width:${size}px;height:${size}px;min-width:${size}px;min-height:${size}px;"><i class="${icons[0]}"></i></span>`
                            : `<i class="${icons[0]}"></i>`;
                        prev.style.backgroundColor = style === 'angle' ? 'transparent' : (this.row.arrow_bg_color || 'transparent');
                        prev.style.setProperty('--swiper-arrow-bg', this.row.arrow_bg_color || '#e0e0e0');
                    }
                    if(next) {
                        const size = (parseInt(this.row.arrow_size) || 24) * 2;
                        next.innerHTML = style === 'angle'
                            ? `<span class="jd-arrow-circle" style="width:${size}px;height:${size}px;min-width:${size}px;min-height:${size}px;"><i class="${icons[1]}"></i></span>`
                            : `<i class="${icons[1]}"></i>`;
                        next.style.backgroundColor = style === 'angle' ? 'transparent' : (this.row.arrow_bg_color || 'transparent');
                        next.style.setProperty('--swiper-arrow-bg', this.row.arrow_bg_color || '#e0e0e0');
                    }
                },

                // ── 슬라이드 떠날 때 ──
                onSlideLeave(index) {
                    if(this.ytPlayers[index]) {
                        this.ytPlayers[index].stopVideo();
                    }
                },

                // ── 슬라이드 진입 시 ──
                onSlideEnter(index) {
                    const img = this.renderSlideImages[index];
                    if(img && img.type === 'video') {
                        this.$nextTick(() => {
                            if(this.ytPlayers[index]) {
                                this.ytPlayers[index].seekTo(0);
                                this.ytPlayers[index].playVideo();
                            }
                        });
                    }
                },

                // ── YouTube ──
                initYouTubeAPI() {
                    const hasVideo = this.renderSlideImages.some(img => img.type === 'video');
                    if(!hasVideo) return;

                    if(typeof YT !== 'undefined' && YT.Player) {
                        this.createYTPlayers();
                    } else {
                        if(!document.querySelector('script[src*="youtube.com/iframe_api"]')) {
                            const tag = document.createElement('script');
                            tag.src = 'https://www.youtube.com/iframe_api';
                            document.head.appendChild(tag);
                        }
                        const self = this;
                        const origCallback = window.onYouTubeIframeAPIReady;
                        window.onYouTubeIframeAPIReady = function() {
                            if(origCallback) origCallback();
                            self.createYTPlayers();
                        };
                    }
                },

                createYTPlayers() {
                    this.renderSlideImages.forEach((img, index) => {
                        if(img.type !== 'video') return;
                        const videoId = this.extractYouTubeId(img.url);
                        if(!videoId) return;

                        const playerId = `yt-player-${this.component_idx}-${index}`;
                        if(!document.getElementById(playerId)) return;

                        const self = this;
                        this.ytPlayers[index] = new YT.Player(playerId, {
                            videoId: videoId,
                            width: '100%',
                            height: '100%',
                            playerVars: {
                                autoplay: img.auto ? 1 : 0,
                                mute: img.mute ? 1 : 0,
                                controls: 0,
                                rel: 0,
                                modestbranding: 1,
                                showinfo: 0,
                                playsinline: 1,
                                iv_load_policy: 3,
                                disablekb: 1,
                                fs: 0,
                                loop: img.loop ? 1 : 0,
                                playlist: img.loop ? videoId : undefined,
                            },
                            events: {
                                onReady(event) {
                                    if(img.mute) event.target.mute();
                                    if(img.auto && index === self.currentSlideIndex) {
                                        event.target.playVideo();
                                    }
                                },
                                onStateChange(event) {
                                    self.onPlayerStateChange(event, index);
                                }
                            }
                        });
                    });
                },

                onPlayerStateChange(event, index) {
                    const img = this.renderSlideImages[index];

                    if(event.data === YT.PlayerState.ENDED) {
                        if(img.loop) {
                            event.target.seekTo(0);
                            event.target.playVideo();
                        } else {
                            event.target.stopVideo();
                        }
                    }
                },

                extractYouTubeId(url) {
                    if(!url) return null;
                    const match = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|shorts\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
                    return match ? match[1] : null;
                },

                initScrollObserver() {
                    if(this.observer) {
                        this.observer.disconnect();
                        this.observer = null;
                    }

                    if(!this.$refs.slideElement) return;

                    this.observer = new IntersectionObserver((entries) => {
                        entries.forEach(entry => {
                            this.scrollEvent = entry.isIntersecting;
                        });
                    }, {
                        threshold: 0.1
                    });

                    this.observer.observe(this.$refs.slideElement);
                },

                getOriginalSlideIndex(index) {
                    const count = this.slideImages.length;
                    if(!count) return 0;
                    return ((index % count) + count) % count;
                },

                applyPaginationState(swiper) {
                    const el = document.getElementById(`swiper-pagination-${this.component_idx}`);
                    if(!el) return;

                    const originalCount = this.slideImages.length;
                    const hasRenderClones = this.renderSlideImages.length > originalCount;
                    const realIndex = swiper && typeof swiper.realIndex === 'number' ? swiper.realIndex : this.currentSlideIndex;
                    const activeOriginalIndex = this.getOriginalSlideIndex(realIndex);

                    el.querySelectorAll('.swiper-pagination-bullet').forEach((bullet, index) => {
                        const bulletIndex = parseInt(bullet.dataset.slideOriginalIndex ?? index, 10);
                        const isActive = hasRenderClones ? bulletIndex === activeOriginalIndex : bullet.classList.contains('swiper-pagination-bullet-active');

                        bullet.classList.toggle('swiper-pagination-bullet-active', isActive);
                        bullet.style.background = isActive
                            ? (this.row.slide_paging_on_color || '#007aff')
                            : (this.row.slide_paging_off_color || 'rgba(0,0,0,0.2)');
                        bullet.style.width = `${this.row.slide_paging_size}px`;
                        bullet.style.height = `${this.row.slide_paging_size}px`;
                        bullet.style.fontSize = `${Math.round((this.row.slide_paging_size) * 0.6)}px`;
                    });

                    if(this.row.slide_paging === 'fraction') {
                        if(hasRenderClones && originalCount > 0) {
                            const current = el.querySelector('.swiper-pagination-current');
                            const total = el.querySelector('.swiper-pagination-total');
                            if(current) current.textContent = activeOriginalIndex + 1;
                            if(total) total.textContent = originalCount;
                        }
                        this.applyFractionColor();
                    }
                },

                handleImageClick(img) {
                    if(this.row.slide_click == "동작없음") return false;

                    if(this.row.slide_click === '크게 보기') {
                        this.viewImage = img.src;
                    }
                    if(this.row.slide_click == "링크 이동") {
                        window.open(img.option_mouse_click_url, img.option_mouse_click_target || '_self');
                    }
                },

                // ── 스타일 ──
                contentPositionStyle(img) {
                    const pos = (img.content_position || 'middle-center').split('-');
                    const vertical = pos[0] || 'middle';
                    const horizontal = pos[1] || 'center';
                    return {
                        position: 'absolute',
                        display: 'flex',
                        flexDirection: 'column',
                        alignItems: horizontal === 'left' ? 'flex-start' : horizontal === 'right' ? 'flex-end' : 'center',
                        justifyContent: vertical === 'top' ? 'flex-start' : vertical === 'bottom' ? 'flex-end' : 'center',
                        top: (img.content_margin_top || 0) + 'px',
                        left: (img.content_margin_left || 0) + 'px',
                        right: (img.content_margin_right || 0) + 'px',
                        bottom: (img.content_margin_bottom || 0) + 'px',
                        zIndex: 12,
                        pointerEvents: 'none',
                    };
                },

                btnStyle(img) {
                    return {
                        width: (img.btn_width || 150) + 'px',
                        height: (img.btn_height || 40) + 'px',
                        lineHeight: (img.btn_height || 40) + 'px',
                        color: img.btn_color || '#000',
                        backgroundColor: img.btn_bg_color || '#fff',
                        borderRadius: (img.btn_border_radius || 0) + 'px',
                        border: `${img.btn_border_width || 1}px solid ${img.btn_border_color || '#000'}`,
                        fontSize: (img.btn_font_size || 16) + 'px',
                        textAlign: 'center',
                        display: 'inline-block',
                        cursor: 'pointer',
                        pointerEvents: 'auto',
                        textDecoration: 'none',
                    };
                },
            },
            computed: {
                appState() {
                    return window.JAYDREAM_VUE_GLOBAL[this.$app_name];
                },

                slideStyleClass() {
                    const style = this.row.slide_style || 'Default';
                    const map = {
                        'Default': 'jd-slide-style-normal',
                        'Vertical': 'jd-slide-style-vertical',
                        'Slides per view': 'jd-slide-style-grid',
                        'Centered auto': 'jd-slide-style-carousel',
                    };
                    return map[style] || '';
                },

                swiperContainerStyle() {
                    const style = this.row.slide_style || 'Default';
                    const containerStyle = {};

                    if(style === 'Vertical') {
                        const isMobile = window.innerWidth < 768;
                        const height = isMobile
                            ? (this.row.mobile_height || 480)
                            : (this.row.height || 600);
                        containerStyle.height = height + 'px';
                        containerStyle.overflow = 'hidden';
                    }

                    return containerStyle;
                },

                slideImages() {
                    const images = Array.isArray(this.row.images) ? this.row.images : [];
                    const uploads = Array.isArray(this.row.upload_file) ? this.row.upload_file : [];

                    const result = [...images, ...uploads].sort((a, b) => {
                        const aPriority = a.priority ?? Infinity;
                        const bPriority = b.priority ?? Infinity;
                        return aPriority - bPriority;
                    });

                    console.log(result[0]); // 첫번째 이미지 객체 확인
                    return result;
                },

                minimumLoopSlides() {
                    const style = this.row.slide_style || 'Default';

                    if(style === 'Slides per view') {
                        const pcPerView = parseInt(this.row.line_image_pc) || 4;
                        const mobilePerView = parseInt(this.row.line_image_mobile) || 2;
                        return Math.max(pcPerView, mobilePerView) * 2;
                    }

                    if(style === 'Centered auto') {
                        return 6;
                    }

                    return 2;
                },

                renderSlideImages() {
                    const images = this.slideImages.map((img, index) => ({
                        ...img,
                        _originalIndex: index,
                        _renderClone: false,
                    }));

                    if(this.row.slide_loop === false || images.length <= 1 || images.length >= this.minimumLoopSlides) {
                        return images;
                    }

                    const result = [];
                    for(let i = 0; result.length < this.minimumLoopSlides; i++) {
                        const source = images[i % images.length];
                        result.push({
                            ...source,
                            _originalIndex: source._originalIndex,
                            _renderClone: i >= images.length,
                        });
                    }

                    return result;
                },

                slideWidgetStyle() {
                    const opacityValue = parseInt(this.row.box_shadow_opacity, 10);
                    const opacity = Math.min(100, Math.max(0, isNaN(opacityValue) ? 20 : opacityValue)) / 100;
                    const style = {
                        '--jd-slide-shadow-opacity': opacity,
                    };

                    if(this.row.option_scroll !== '효과없음' && this.scrollEvent) {
                        const effect = this.row.option_scroll;
                        let direction = this.row.option_scroll_direction || 'center';
                        const duration = parseInt(this.row.option_scroll_duration, 10) || 1000;
                        const delay = parseInt(this.row.option_scroll_delay, 10) || 0;

                        const animationName = effect === '페이드' ? 'fade' :
                            effect === '줌인' ? 'zoom-in' :
                                effect === '줌아웃' ? 'zoom-out' :
                                    effect === '플립' ? 'flip' : 'fade';

                        if(animationName === 'flip' && direction === 'center') {
                            direction = 'up';
                        }

                        style.animation = `${animationName}-${direction} ${duration}ms ease ${delay}ms both`;
                    }

                    return style;
                },

                slideWrapStyle() {
                    const isMobile = window.innerWidth < 768;
                    const height = isMobile
                        ? (this.row.mobile_height || 480)
                        : (this.row.height || 600);


                    const style = {
                        position: 'relative',
                        width: '100%',
                        height: height + 'px',
                    };
                    return style;
                },

                slideFrameStyle() {
                    const style = {
                        position: 'relative',
                        height: '100%',
                        overflow: 'hidden',
                        boxSizing: 'border-box',
                    };
                    if(this.row.border_use) {
                        style.border = `${this.row.border_width || 1}px solid ${this.row.border_color || '#000'}`;
                    }
                    if(this.row.border_radius_use) {
                        style.borderRadius = (this.row.border_radius_px || 6) + 'px';
                    }
                    return style;
                },

                imageStyle() {
                    const fit = this.row.ratio === '맞추기' ? 'contain' : 'cover';
                    return { width: '100%', height: '100%', objectFit: fit };
                },

                arrowStyle() {
                    return {
                        '--swiper-navigation-color': this.row.arrow_color || '#666',
                        '--swiper-navigation-size': (this.row.arrow_size || 10) * 2 + 'px',
                    };
                },
            },
            watch: {
                appState: {
                    async handler(newVal) {
                        if(newVal.reload) {
                            let row = await this.table.get();
                            if(row.length > 0) {
                                this.row = row[0];
                                this.normalizeScrollOptions();
                                if(this.row.images && typeof this.row.images === 'string') {
                                    try { this.row.images = JSON.parse(this.row.images); } catch(e) {}
                                }
                            }
                            this.appState.reload = false;

                            this.$nextTick(() => {
                                if(this.swiperInstance) this.swiperInstance.destroy(true, true);
                                this.ytPlayers = {};
                                this.initSwiper();
                                this.initArrow();
                                this.initYouTubeAPI();
                                this.initScrollObserver();
                            });
                        }
                    },
                    deep: true
                }
            },
            beforeUnmount() {
                if(this.swiperInstance) this.swiperInstance.destroy(true, true);
                if(this.observer) this.observer.disconnect();
                Object.values(this.ytPlayers).forEach(player => {
                    if(player && player.destroy) player.destroy();
                });
            }
        }});
</script>

<style>
    .jd-slide-widget .swiper {
        width: 100%;
        overflow: hidden;
    }
    .jd-slide-widget .swiper { width: 100%; overflow: hidden; }
    .jd-slide-widget.jd-slide-shadow-enabled .swiper {
        padding-bottom: 18px;
    }

    .jd-slide-image-wrap,
    .jd-slide-video-wrap {
        position: relative;
        width: 100%;
    }
    .jd-slide-shadow-frame {
        position: relative;
        width: auto;
        height: 100%;
        overflow: hidden;
        box-sizing: border-box;
    }
    .jd-slide-widget.jd-slide-shadow-enabled .jd-slide-shadow-frame {
        margin: 0 14px;
        box-shadow: 0 4px 12px rgba(0, 0, 0, var(--jd-slide-shadow-opacity, 0.2));
    }

    .jd-slide-img { display: block; }

    @keyframes fade-up { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
    @keyframes fade-down { from { opacity: 0; transform: translateY(-30px); } to { opacity: 1; transform: translateY(0); } }
    @keyframes fade-left { from { opacity: 0; transform: translateX(30px); } to { opacity: 1; transform: translateX(0); } }
    @keyframes fade-right { from { opacity: 0; transform: translateX(-30px); } to { opacity: 1; transform: translateX(0); } }
    @keyframes fade-center { from { opacity: 0; } to { opacity: 1; } }

    @keyframes zoom-in-up { from { opacity: 0; transform: scale(0.8) translateY(30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-in-down { from { opacity: 0; transform: scale(0.8) translateY(-30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-in-left { from { opacity: 0; transform: scale(0.8) translateX(30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-in-right { from { opacity: 0; transform: scale(0.8) translateX(-30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-in-center { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }

    @keyframes zoom-out-up { from { opacity: 0; transform: scale(1.2) translateY(30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-out-down { from { opacity: 0; transform: scale(1.2) translateY(-30px); } to { opacity: 1; transform: scale(1) translateY(0); } }
    @keyframes zoom-out-left { from { opacity: 0; transform: scale(1.2) translateX(30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-out-right { from { opacity: 0; transform: scale(1.2) translateX(-30px); } to { opacity: 1; transform: scale(1) translateX(0); } }
    @keyframes zoom-out-center { from { opacity: 0; transform: scale(1.2); } to { opacity: 1; transform: scale(1); } }

    @keyframes flip-up { from { opacity: 0; transform: perspective(400px) rotateX(90deg); } to { opacity: 1; transform: perspective(400px) rotateX(0); } }
    @keyframes flip-down { from { opacity: 0; transform: perspective(400px) rotateX(-90deg); } to { opacity: 1; transform: perspective(400px) rotateX(0); } }
    @keyframes flip-left { from { opacity: 0; transform: perspective(400px) rotateY(90deg); } to { opacity: 1; transform: perspective(400px) rotateY(0); } }
    @keyframes flip-right { from { opacity: 0; transform: perspective(400px) rotateY(-90deg); } to { opacity: 1; transform: perspective(400px) rotateY(0); } }

    /* YouTube iframe 확대 - UI 영역 잘라내기 */
    .jd-slide-youtube {
        position: absolute;
        top: 50%; left: 50%;
        width: 120%; height: 120%;
        transform: translate(-50%, -50%);
    }
    .jd-slide-youtube iframe {
        width: 100%;
        height: 100%;
        pointer-events: none;
    }

    /* 마우스 이벤트 차단 */
    .jd-slide-video-shield {
        position: absolute; top: 0; left: 0;
        width: 100%; height: 100%;
        z-index: 10; cursor: default;
        background: transparent;
    }

    .jd-slide-content-overlay { pointer-events: none; }
    .jd-slide-text { pointer-events: auto; }
    .jd-slide-btn { pointer-events: auto; box-sizing: border-box; }

    /* ── 세로 슬라이드 ── */
    .jd-slide-style-vertical {
        overflow: hidden;
    }

    /* ── 멀티 슬라이드 - 중앙 확대 ── */
    .jd-slide-style-multi .swiper-slide {
        transition: transform 0.3s ease;
        transform: scale(0.85);
    }
    .jd-slide-style-multi .swiper-slide-active {
        transform: scale(1);
    }

    /* ── 캐러셀 슬라이드 - 중앙 확대, 간격 없음 ── */
    .jd-slide-style-carousel .swiper-slide {
        transition: transform 0.3s ease, opacity 0.3s ease;
        transform: scale(0.8);
        opacity: 0.6;
    }
    .jd-slide-style-carousel .swiper-slide-active {
        transform: scale(1);
        opacity: 1;
    }

    /* ── 원형 슬라이드 ── */
    .jd-slide-style-circle .jd-slide-image-wrap,
    .jd-slide-style-circle .jd-slide-video-wrap {
        border-radius: 50%;
    }
    .jd-slide-style-circle .jd-slide-image-wrap img {
        border-radius: 50%;
    }


    .swiper-pagination-bullet {
        text-align: center;
        line-height: 20px;
        color: #000;
        opacity: 1;
        background: rgba(0, 0, 0, 0.2);
    }

    .swiper-pagination-bullet-active {
        color: #fff;
        background: #007aff;
    }

    .jd-pagination-outside {
        position: relative !important;
        bottom: auto !important;
        margin-top: 10px;
        text-align: center;
    }

    .swiper-button-prev::after,
    .swiper-button-next::after {
        display: none;
    }
    .swiper-button-prev i,
    .swiper-button-next i {
        font-size: var(--swiper-navigation-size, 20px);
        color: var(--swiper-navigation-color, #666);
    }

    .jd-slide-arrow-outside-wrap {
        display: flex;
        align-items: center;
    }

    .jd-slide-arrow-outside-wrap .swiper {
        flex: 1;
    }

    .jd-arrow-outside-btn {
        position: relative !important;
        top: auto !important;
        left: auto !important;
        right: auto !important;
        flex-shrink: 0;
        margin: 0 5px;
    }

    .jd-arrow-circle {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 40px;
        height: 40px;
        border-radius: 50%;
        background-color: var(--swiper-arrow-bg, #e0e0e0);
        color: var(--swiper-navigation-color, #666);
    }

    .jd-arrow-circle i {
        display: flex;
        align-items: center;
        justify-content: center;
        line-height: 1;
        position: relative;
    }

    .swiper-button-prev .jd-arrow-circle i {
        transform: translateX(-2px);
    }

    .swiper-button-next .jd-arrow-circle i {
        transform: translateX(2px);
    }
</style>
<script type="text/x-template" id="design-widget-imagemap-modal-template">
    <div v-if="load" class="design-widget-imagemap-modal-wrap">

        <!-- 좌: 이미지 캔버스 -->
        <div class="design-widget-imagemap-modal-canvas-wrap">
            <div v-if="!imageSrc" class="design-widget-imagemap-modal-canvas-empty">
                <i class="fa-regular fa-image"></i>
                <span>이미지를 추가해 주세요.</span>
            </div>
            <div v-if="imageSrc" class="design-widget-imagemap-modal-canvas-inner"
                 @mousedown="onDragStart"
                 @mousemove="onMouseMove"
                 @mouseup="onMouseUp"
                 @mouseleave="onMouseUp">
                <img :src="imageSrc" class="design-widget-imagemap-modal-canvas-img" draggable="false" />
                <div v-if="dragging" class="design-widget-imagemap-modal-area-draft" :style="draftStyle"></div>
                <div v-for="(area, idx) in row.areas" :key="area.id"
                     class="design-widget-imagemap-modal-area-box"
                     :style="areaStyle(area)">
                    <span class="design-widget-imagemap-modal-area-label">#{{ idx + 1 }}</span>
                    <div class="design-widget-imagemap-modal-area-handle design-widget-imagemap-modal-area-handle--nw"
                         @mousedown.stop="onResizeStart($event, idx, 'nw')"></div>
                    <div class="design-widget-imagemap-modal-area-handle design-widget-imagemap-modal-area-handle--se"
                         @mousedown.stop="onResizeStart($event, idx, 'se')"></div>
                </div>
            </div>
        </div>

        <!-- 우: 설정 패널 -->
        <div class="design-widget-imagemap-modal-panel">

            <div class="design-widget-imagemap-modal-guide-box">
                <div class="design-widget-imagemap-modal-guide-title">
                    <i class="fa-solid fa-circle-info"></i>
                    이미지맵 사용 방법
                </div>
                <p class="design-widget-imagemap-modal-guide-text">
                    추가한 이미지에서 링크를 연결할 영역을 드래그해 주세요.<br>
                    생성된 영역 번호에 원하는 링크를 입력하면 해당 영역이 클릭 가능해집니다.
                </p>
            </div>

            <div class="design-widget-imagemap-modal-upload-row">
                <span class="design-widget-imagemap-modal-upload-label">이미지 추가</span>
                <button type="button" class="design-widget-imagemap-modal-upload-btn" @click="triggerUpload">
                    이미지 업로드
                </button>
                <span class="design-widget-imagemap-modal-upload-note">※ 이미지는 원본 크기로 첨부됩니다.</span>
                <input ref="fileInput" type="file" accept="image/*" style="display:none"
                       @change="vue.changeFile($event, row, 'upload_file', {'callback': changeEvent})" />
            </div>

            <div class="design-widget-imagemap-modal-area-list">
                <div v-for="(area, idx) in row.areas" :key="area.id" class="design-widget-imagemap-modal-area-item">
                    <div class="design-widget-imagemap-modal-area-item-top">
                        <span class="design-widget-imagemap-modal-area-num">#{{ idx + 1 }}</span>
                        <select class="design-widget-imagemap-modal-area-type" v-model="area.url_type">
                            <option value="URL">URL</option>
                            <option value="페이지">페이지</option>
                        </select>

                        <template v-if="area.url_type == 'URL'">
                            <input type="text" class="design-widget-imagemap-modal-area-url" v-model="area.url" placeholder="https://" />
                        </template>

                        <template v-if="area.url_type == '페이지'">
                            <template v-if="menus.length == 0">
                                <span style="font-size:12px; color:#999;">데이터를 불러들이는 중입니다.</span>
                            </template>
                            <template v-else>
                                <select class="design-widget-imagemap-modal-area-url" v-model="area.url">
                                    <option value="">선택해주세요.</option>
                                    <template v-for="item in menus">
                                        <option :value="item.url">{{ item.name }}</option>
                                    </template>
                                </select>
                            </template>
                        </template>

                        <button type="button" class="design-widget-imagemap-modal-area-del" @click="removeArea(idx)">
                            <i class="fal fa-times"></i>
                        </button>
                    </div>
                    <div class="design-widget-imagemap-modal-area-item-bottom">
                        <label class="design-widget-imagemap-modal-area-radio">
                            <input type="radio" :name="'target_' + area.id" value="_self" v-model="area.target" />
                            기존 창에서 이동
                        </label>
                        <label class="design-widget-imagemap-modal-area-radio">
                            <input type="radio" :name="'target_' + area.id" value="_blank" v-model="area.target" />
                            새 창으로 열기
                        </label>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <div class="modal-footer" style="position:sticky;right:0;bottom:0;">
        <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
        <button type="button" class="btn btn-orange save" @click="save()">저장</button>
    </div>
    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name: "design-widget-imagemap-modal", object: {
            template: "#design-widget-imagemap-modal-template",
            props: {
                primary      : {type: String, default: ""},
                site_code    : {type: String, default: ""},
                widget_code  : {type: String, default: ""},
            },
            data() {
                return {
                    load           : false,
                    component_name : "design-widget-imagemap-modal",
                    component_idx  : "",

                    dragging     : false,
                    drag_start   : { x: 0, y: 0 },
                    drag_current : { x: 0, y: 0 },

                    resizing      : false,
                    resize_idx    : null,
                    resize_dir    : 'se',
                    resize_start  : { x: 0, y: 0 },
                    resize_origin : { x: 0, y: 0, w: 0, h: 0 },

                    row : {
                        widget_code  : this.widget_code,
                        file_idx     : "",
                        areas        : [],
                        upload_file  : "",
                    },

                    menus : [],
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name;
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_imagemap");
                this.table.where('widget_code', this.widget_code);
                this.table.join("jd_file", "file_idx");
                let row = await this.table.get();
                if (row.length > 0) this.row = row[0];

                this.sessions = await this.session.all();
                this.row.user_idx = this.sessions.mem_idx;

                this.load = true;
                this.$nextTick(async () => {
                    this.api.table("menu").where("site_code", this.site_code).get(this.menus);
                });
            },
            computed: {
                imageSrc() {
                    return (this.row.upload_file && this.row.upload_file.src)
                        ? this.row.upload_file.src
                        : (this.row.$jd_file__src || '');
                },
                draftStyle() {
                    const x = Math.min(this.drag_start.x, this.drag_current.x);
                    const y = Math.min(this.drag_start.y, this.drag_current.y);
                    const w = Math.abs(this.drag_current.x - this.drag_start.x);
                    const h = Math.abs(this.drag_current.y - this.drag_start.y);
                    return { left: x + 'px', top: y + 'px', width: w + 'px', height: h + 'px' };
                }
            },
            methods: {
                triggerUpload() {
                    this.$refs.fileInput.click();
                },
                changeEvent() {
                    this.row.areas = [];
                },
                onDragStart(e) {
                    if (this.resizing) return;
                    const rect    = e.currentTarget.getBoundingClientRect();
                    this.dragging     = true;
                    this.drag_start   = { x: e.clientX - rect.left, y: e.clientY - rect.top };
                    this.drag_current = { ...this.drag_start };
                },
                onResizeStart(e, idx, direction) {
                    const canvas  = e.target.closest('.design-widget-imagemap-modal-canvas-inner');
                    const rect    = canvas.getBoundingClientRect();
                    this.resizing      = true;
                    this.resize_idx    = idx;
                    this.resize_dir    = direction;
                    this.resize_start  = { x: e.clientX - rect.left, y: e.clientY - rect.top };
                    this.resize_origin = {
                        x : this.row.areas[idx].x,
                        y : this.row.areas[idx].y,
                        w : this.row.areas[idx].w,
                        h : this.row.areas[idx].h,
                    };
                },
                onMouseMove(e) {
                    const rect = e.currentTarget.getBoundingClientRect();
                    const cx   = e.clientX - rect.left;
                    const cy   = e.clientY - rect.top;
                    const cw   = e.currentTarget.offsetWidth;
                    const ch   = e.currentTarget.offsetHeight;

                    if (this.dragging) {
                        this.drag_current = { x: cx, y: cy };
                    }

                    if (this.resizing && this.resize_idx !== null) {
                        const dx   = (cx - this.resize_start.x) / cw;
                        const dy   = (cy - this.resize_start.y) / ch;
                        const area = this.row.areas[this.resize_idx];
                        const o    = this.resize_origin;

                        if (this.resize_dir === 'se') {
                            area.w = Math.min(Math.max(0.02, o.w + dx), 1 - area.x);
                            area.h = Math.min(Math.max(0.02, o.h + dy), 1 - area.y);
                        } else if (this.resize_dir === 'nw') {
                            const new_x = Math.min(Math.max(0, o.x + dx), o.x + o.w - 0.02);
                            const new_y = Math.min(Math.max(0, o.y + dy), o.y + o.h - 0.02);
                            area.w = o.w - (new_x - o.x);
                            area.h = o.h - (new_y - o.y);
                            area.x = new_x;
                            area.y = new_y;
                        }
                    }
                },
                onMouseUp(e) {
                    if (this.dragging) {
                        this.dragging = false;
                        const cw = e.currentTarget.offsetWidth;
                        const ch = e.currentTarget.offsetHeight;
                        const x  = Math.min(this.drag_start.x, this.drag_current.x);
                        const y  = Math.min(this.drag_start.y, this.drag_current.y);
                        const w  = Math.abs(this.drag_current.x - this.drag_start.x);
                        const h  = Math.abs(this.drag_current.y - this.drag_start.y);
                        if (w < 10 || h < 10) return;
                        this.row.areas.push({
                            id       : this.lib.generateUniqueId(),
                            x        : x / cw,
                            y        : y / ch,
                            w        : w / cw,
                            h        : h / ch,
                            url_type : 'URL',
                            url      : '',
                            target   : '_self'
                        });
                    }

                    if (this.resizing) {
                        this.resizing   = false;
                        this.resize_idx = null;
                    }
                },
                areaStyle(area) {
                    return {
                        left   : (area.x * 100) + '%',
                        top    : (area.y * 100) + '%',
                        width  : (area.w * 100) + '%',
                        height : (area.h * 100) + '%',
                    };
                },
                removeArea(idx) {
                    this.row.areas.splice(idx, 1);
                },
                async save() {
                    window.design.loadingOn();

                    if (this.row.upload_file) {
                        let res = await this.lib.ajax("file_save", {
                            "user_idx"    : this.row.user_idx,
                            "upload_file" : this.row.upload_file,
                            "memo"        : "jd_widget_imagemap",
                            "widget_code" : this.row.widget_code,
                            "site_code"   : this.site_code,
                        });

                        let file = res['file'];
                        this.row.upload_file = null;
                        this.row.file_idx    = file.primary;
                    }

                    await this.table.post(this.row, {return: true});

                    window.design.loadingOff();

                    window.JAYDREAM_VUE_GLOBAL[this.widget_code].reload = true;

                    $(this.$el).closest('.modal').modal('hide');
                }
            }
        }});
</script>

<style>
    .design-widget-imagemap-modal-area-label {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        color: #fff;
        font-size: 12px;
        font-weight: 600;
        text-shadow: 0 1px 2px rgba(0,0,0,.4);
        pointer-events: none;
    }

    .design-widget-imagemap-modal-wrap {
        display: flex;
        gap: 24px;
        height: 100%;
        min-height: 500px;
    }
    .design-widget-imagemap-modal-canvas-wrap {
        flex: 1;
        background: #f2f2f2;
        border-radius: 6px;
        overflow: hidden;
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;
        min-height: 400px;
    }
    .design-widget-imagemap-modal-canvas-empty {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 10px;
        color: #bbb;
        font-size: 14px;
    }
    .design-widget-imagemap-modal-canvas-empty i {
        font-size: 36px;
    }
    .design-widget-imagemap-modal-canvas-inner {
        position: relative;
        display: inline-block;
        cursor: crosshair;
        user-select: none;
    }
    .design-widget-imagemap-modal-canvas-img {
        display: block;
        max-width: 100%;
    }
    .design-widget-imagemap-modal-area-draft,
    .design-widget-imagemap-modal-area-box {
        position: absolute;
        background: rgba(220, 80, 60, 0.25);
        border: 2px solid rgba(220, 80, 60, 0.8);
        box-sizing: border-box;
        pointer-events: none;
    }
    .design-widget-imagemap-modal-area-handle {
        position: absolute;
        width: 10px;
        height: 10px;
        background: #e04a36;
        border: 2px solid #fff;
        border-radius: 2px;
        pointer-events: all;
    }
    .design-widget-imagemap-modal-area-handle--nw {
        top: -4px;
        left: -4px;
        cursor: nw-resize;
    }
    .design-widget-imagemap-modal-area-handle--se {
        bottom: -4px;
        right: -4px;
        cursor: se-resize;
    }
    .design-widget-imagemap-modal-panel {
        width: 380px;
        display: flex;
        flex-direction: column;
        gap: 16px;
    }
    .design-widget-imagemap-modal-guide-box {
        background: #eef4ff;
        border: 1px solid #cfe0ff;
        border-radius: 6px;
        padding: 14px 16px;
    }
    .design-widget-imagemap-modal-guide-title {
        font-size: 13px;
        font-weight: 700;
        color: #2a5cbf;
        margin-bottom: 8px;
        display: flex;
        align-items: center;
        gap: 6px;
    }
    .design-widget-imagemap-modal-guide-text {
        font-size: 12px;
        color: #555;
        line-height: 1.7;
        margin: 0;
    }
    .design-widget-imagemap-modal-upload-row {
        display: flex;
        align-items: center;
        gap: 10px;
        flex-wrap: wrap;
    }
    .design-widget-imagemap-modal-upload-label {
        font-size: 13px;
        font-weight: 600;
        color: #333;
        white-space: nowrap;
    }
    .design-widget-imagemap-modal-upload-btn {
        background: #e04a36;
        color: #fff;
        border: none;
        border-radius: 4px;
        padding: 7px 14px;
        font-size: 13px;
        font-weight: 600;
        cursor: pointer;
        white-space: nowrap;
    }
    .design-widget-imagemap-modal-upload-btn:hover {
        background: #c43c2a;
    }
    .design-widget-imagemap-modal-upload-note {
        font-size: 11px;
        color: #999;
    }
    .design-widget-imagemap-modal-area-list {
        display: flex;
        flex-direction: column;
        gap: 12px;
        overflow-y: auto;
    }
    .design-widget-imagemap-modal-area-item {
        display: flex;
        flex-direction: column;
        gap: 6px;
    }
    .design-widget-imagemap-modal-area-item-top {
        display: flex;
        align-items: center;
        gap: 6px;
    }
    .design-widget-imagemap-modal-area-num {
        font-size: 13px;
        font-weight: 600;
        color: #333;
        white-space: nowrap;
        min-width: 24px;
    }
    .design-widget-imagemap-modal-area-type {
        border: 1px solid #ddd;
        border-radius: 4px;
        padding: 5px 8px;
        font-size: 12px;
        color: #333;
        background: #fff;
        cursor: pointer;
    }
    .design-widget-imagemap-modal-area-url {
        flex: 1;
        border: 1px solid #ddd;
        border-radius: 4px;
        padding: 5px 10px;
        font-size: 12px;
        color: #333;
        outline: none;
    }
    .design-widget-imagemap-modal-area-url:focus {
        border-color: #e04a36;
    }
    .design-widget-imagemap-modal-area-del {
        background: none;
        border: 1px solid #ddd;
        border-radius: 4px;
        width: 28px;
        height: 28px;
        display: flex;
        align-items: center;
        justify-content: center;
        color: #999;
        cursor: pointer;
        flex-shrink: 0;
    }
    .design-widget-imagemap-modal-area-del:hover {
        border-color: #e04a36;
        color: #e04a36;
    }
    .design-widget-imagemap-modal-area-item-bottom {
        display: flex;
        gap: 16px;
        padding-left: 28px;
    }
    .design-widget-imagemap-modal-area-radio {
        display: flex;
        align-items: center;
        gap: 5px;
        font-size: 12px;
        color: #555;
        cursor: pointer;
    }
</style><script type="text/x-template" id="design-widget-imagemap-template">
    <div v-if="load">
        <template v-if="row.idx">
            <div class="design-widget-imagemap-wrap" @dblclick.stop.prevent="openSettingModal">
                <img :src="row.$jd_file__src" class="design-widget-imagemap-img" draggable="false" />
                <a v-for="area in row.areas" :key="area.id"
                   :href="area.url || 'javascript:void(0)'"
                   :target="area.target"
                   class="design-widget-imagemap-area"
                   @click="handleAreaClick"
                   @dblclick.stop.prevent="openSettingModal"
                   :style="{ left: (area.x * 100) + '%', top: (area.y * 100) + '%', width: (area.w * 100) + '%', height: (area.h * 100) + '%' }">
                </a>
            </div>
        </template>

        <template v-else>
            <div class="jd-imageupload-box jd-widget-placeholder" @dblclick.stop.prevent="openSettingModal">
                <img class="jd-widget-placeholder-icon" src="/file/icon/widget-add/imagemap.svg" alt="">
                <p class="jd-imageupload-text">더블 클릭하여 이미지맵을 추가하세요.</p>
            </div>
        </template>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-imagemap", object : {
            template: "#design-widget-imagemap-template",
            props: {
                primary      : {type : String, default : ""},
                widget_code  : {type : String, default : ""},
            },
            data: function () {
                return {
                    load           : false,
                    component_name : "design-widget-imagemap",
                    component_idx  : "",

                    table : null,
                    row   : {},
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name;
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_imagemap");
                this.table.where('widget_code', this.widget_code);
                this.table.join("jd_file", "file_idx");
                let row = await this.table.get();
                if (row.length > 0) this.row = row[0];

                this.load = true;

                this.$nextTick(async () => {
                    if(!this.row.file_src) $('.firewall').addClass('widget_setting_btn');
                });
            },
            methods: {
                getSettingObject() {
                    if (!window.jQuery || !this.widget_code) return null;

                    const setting = window.jQuery('#' + this.widget_code).data('setting');
                    if (!setting || typeof setting.open !== 'function') return null;

                    return setting;
                },
                handleAreaClick(e) {
                    if (!this.getSettingObject()) return true;

                    e.preventDefault();
                    return false;
                },
                openSettingModal(e) {
                    if (e) {
                        e.preventDefault();
                        e.stopPropagation();
                    }

                    const setting = this.getSettingObject();
                    if (!setting) return;

                    setting.open();
                }
            },
            computed: {
                appState() {
                    return window.JAYDREAM_VUE_GLOBAL[this.$app_name];
                }
            },
            watch: {
                appState: {
                    async handler(newVal) {
                        if(newVal.reload) {
                            let row = await this.table.get();
                            if(row.length > 0) this.row = row[0];
                            this.appState.reload = false;
                        }
                    },
                    deep: true
                }
            }
        }});
</script>

<style>
    .design-widget-imagemap-wrap {
        position: relative;
        display: inline-block;
        width: auto;
        max-width: min(100%, 1920px);
    }
    .design-widget-imagemap-img {
        display: block;
        width: 100%;
        height: auto;
    }
    .design-widget-imagemap-area {
        position: absolute;
        display: block;
        cursor: pointer;
    }
</style>
<script type="text/x-template" id="design-widget-text-modal-template">
    <div v-if="load" class="jaydream design-widget-text-modal">

        <!-- 텍스트 프리뷰 -->
        <div class="design-widget-text-modal-section">
            <div class="design-widget-text-modal-preview-wrap">
                <!-- 외부 여백 점선 -->
                <div class="design-widget-text-modal-preview-outer" :style="previewOuterStyle">
                    <!-- 텍스트 박스 점선 -->
                    <div class="design-widget-text-modal-preview-box" :style="previewStyle">
                        <template v-if="isEmptyContent">
                            <div>텍스트를 입력해주세요.</div>
                        </template>
                        <template v-else>
                            <div v-html="row.content" style="pointer-events: none;"></div>
                        </template>
                    </div>
                </div>
            </div>
        </div>

        <hr>

        <!-- 텍스트 박스 설정 -->
        <div class="design-widget-text-modal-section">
            <div class="design-widget-text-modal-section-title">
                <img src="/file/icon/text.svg" style="height: 18px;"> 텍스트 박스 설정
            </div>

            <div class="design-widget-text-modal-option-row">
                <!-- 배경 색상 -->
                <div class="design-widget-text-modal-option-group">
                    <span class="design-widget-text-modal-label">배경 색상</span>
                    <external-picker v-model="row.bg_color"></external-picker>
                </div>

                <!-- 배경 이미지 -->
                <div class="design-widget-text-modal-option-group">
                    <span class="design-widget-text-modal-label">배경 이미지</span>
                    <button class="btn btn-orange btn-sm" @click="$refs.bgImageInput.click()">이미지 업로드</button>
                    <button class="btn btn-orange btn-sm" @click="$openModal(freeImageModal,{row})">무료 이미지</button>
                    <input type="text" class="form-control form-control-sm design-widget-text-modal-image-name" readonly :value="row.upfile ? row.upfile.name : (row.free_image_name || row.$jd_file__name || '')">
                    <button v-if="row.upfile || row.free_image_src || row.$jd_file__name" style="border: none; background: none; cursor: pointer;" @click="clearBgImage()">
                        <i class="far fa-trash-alt" style="font-size: 16px; color: #dc3545;"></i>
                    </button>
                    <input class="hidden" ref="bgImageInput" type="file" @change="changeBgImage($event)">
                </div>

                <!-- 박스 채우기 -->
                <div class="design-widget-text-modal-option-group">
                    <input type="checkbox" v-model="row.box_fill">
                    <span class="design-widget-text-modal-label">박스 채우기</span>
                </div>

                <!-- 텍스트 줄 간격 # 개발 불가능 기획 소통후 삭제예정-->
                <div class="design-widget-text-modal-option-group" v-if="false">
                    <span class="design-widget-text-modal-label">텍스트 줄 간격</span>
                    <select class="form-control form-control-sm design-widget-text-modal-select" v-model="row.line_height">
                        <option value="auto">자동</option>
                        <option value="1">1</option>
                        <option value="1.2">1.2</option>
                        <option value="1.5">1.5</option>
                        <option value="1.8">1.8</option>
                        <option value="2">2</option>
                        <option value="2.5">2.5</option>
                        <option value="3">3</option>
                    </select>
                </div>
            </div>

            <div class="design-widget-text-modal-option-row" style="margin-top: 12px;">
                <!-- 테두리 -->
                <div class="design-widget-text-modal-option-group">
                    <input type="checkbox" v-model="row.border_use">
                    <span class="design-widget-text-modal-label">테두리</span>
                    <external-picker v-model="row.border_color"></external-picker>
                    <input type="number" v-model.number="row.border_width" class="form-control form-control-sm" style="width: 55px;" min="1">
                </div>

                <!-- 모서리 둥글게 -->
                <div class="design-widget-text-modal-option-group">
                    <input type="checkbox" v-model="row.border_radius_use">
                    <span class="design-widget-text-modal-label">모서리 둥글게</span>
                    <div class="design-widget-text-modal-number-input-wrapper design-widget-text-modal-with-icon">
                        <img src="/file/icon/Group 95.svg" class="design-widget-text-modal-input-icon-left">
                        <input type="text" v-model="row.border_radius_px" class="form-control form-control-sm" v-number>
                        <div class="design-widget-text-modal-number-stepper">
                            <button type="button" class="design-widget-text-modal-stepper-up" @click="row.border_radius_px++;">
                                <svg width="10" height="6" viewBox="0 0 10 6">
                                    <path d="M5 0L10 6H0L5 0Z" fill="#666"/>
                                </svg>
                            </button>
                            <button type="button" class="design-widget-text-modal-stepper-down" @click="row.border_radius_px--;">
                                <svg width="10" height="6" viewBox="0 0 10 6">
                                    <path d="M5 6L0 0H10L5 6Z" fill="#666"/>
                                </svg>
                            </button>
                        </div>
                    </div>
                </div>

                <!-- 그림자 -->
                <div class="design-widget-text-modal-option-group">
                    <input type="checkbox" v-model="row.box_shadow_use">
                    <span class="design-widget-text-modal-label">그림자</span>
                    <select class="form-control form-control-sm design-widget-text-modal-select" v-model="row.box_shadow_opacity">
                        <option value="10">10%</option>
                        <option value="20">20%</option>
                        <option value="30">30%</option>
                        <option value="40">40%</option>
                        <option value="50">50%</option>
                        <option value="60">60%</option>
                        <option value="70">70%</option>
                        <option value="80">80%</option>
                        <option value="90">90%</option>
                    </select>
                </div>
            </div>
        </div>

        <hr>

        <!-- 여백 설정 -->
        <div class="design-widget-text-modal-section">
            <div class="design-widget-text-modal-section-title">
                <img src="/file/icon/image_margin.svg" style="height: 18px;"> 여백 설정
            </div>

            <div class="design-widget-text-modal-margin-wrap">
                <!-- 텍스트 박스 내부 여백 -->
                <div class="design-widget-text-modal-margin-block">
                    <div class="design-widget-text-modal-margin-label">텍스트 박스 내부 여백</div>
                    <div class="design-widget-text-modal-margin-grid">
                        <div class="design-widget-text-modal-margin-top">
                            <div class="design-widget-text-modal-number-input-wrapper">
                                <input type="text" class="form-control form-control-sm" v-model="row.box_height_top" placeholder="auto" v-number>
                                <div class="design-widget-text-modal-number-stepper">
                                    <button type="button" class="design-widget-text-modal-stepper-up" @click="row.box_height_top = (parseInt(row.box_height_top) || 0) + 1">
                                        <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 0L10 6H0L5 0Z" fill="#666"/></svg>
                                    </button>
                                    <button type="button" class="design-widget-text-modal-stepper-down" @click="row.box_height_top = Math.max(0, (parseInt(row.box_height_top) || 0) - 1)">
                                        <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 6L0 0H10L5 6Z" fill="#666"/></svg>
                                    </button>
                                </div>
                            </div>
                        </div>
                        <div class="design-widget-text-modal-margin-middle">
                            <input type="text" v-number class="form-control form-control-sm" v-model="row.box_height_left" style="width: 55px;">
                            <div class="design-widget-text-modal-margin-center-box" style="width: 80px; background: #fff3f3; border: 1px solid #e88; color: #c44;">TEXT</div>
                            <input type="text" v-number class="form-control form-control-sm" v-model="row.box_height_right" style="width: 55px;">
                        </div>
                        <div class="design-widget-text-modal-margin-bottom">
                            <div class="design-widget-text-modal-number-input-wrapper">
                                <input type="text" class="form-control form-control-sm" v-model="row.box_height_bottom" placeholder="auto" v-number>
                                <div class="design-widget-text-modal-number-stepper">
                                    <button type="button" class="design-widget-text-modal-stepper-up" @click="row.box_height_bottom = (parseInt(row.box_height_bottom) || 0) + 1">
                                        <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 0L10 6H0L5 0Z" fill="#666"/></svg>
                                    </button>
                                    <button type="button" class="design-widget-text-modal-stepper-down" @click="row.box_height_bottom = Math.max(0, (parseInt(row.box_height_bottom) || 0) - 1)">
                                        <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 6L0 0H10L5 6Z" fill="#666"/></svg>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- 텍스트 박스 외부 여백 -->
                <div class="design-widget-text-modal-margin-block">
                    <div class="design-widget-text-modal-margin-label">텍스트 박스 외부 여백</div>
                    <div class="design-widget-text-modal-margin-grid">
                        <div class="design-widget-text-modal-margin-top">
                            <input type="text" v-number class="form-control form-control-sm" v-model="row.margin_top">
                        </div>
                        <div class="design-widget-text-modal-margin-middle">
                            <input type="text" v-number class="form-control form-control-sm" v-model="row.margin_left" style="width: 55px;">
                            <div class="design-widget-text-modal-margin-center-box">TEXT</div>
                            <input type="text" v-number class="form-control form-control-sm" v-model="row.margin_right" style="width: 55px;">
                        </div>
                        <div class="design-widget-text-modal-margin-bottom">
                            <input type="text" v-number class="form-control form-control-sm" v-model="row.margin_bottom">
                        </div>
                    </div>
                </div>
            </div>

            <p style="color: #888; font-size: 12px; margin-top: 8px;">
                ※ 텍스트 박스 내부 상하 여백 값을 비우면 '자동(auto)'으로 설정이 됩니다.<br>
                ※ 텍스트 박스 좌우 너비는 위젯 배치, 여백 설정 등에 의해 자동으로 조정됩니다.
            </p>
        </div>

        <hr>

        <!-- 스크롤 애니메이션 -->
        <div class="panel panel-default" style="margin-bottom: 10px;">
            <div class="panel-heading" style="cursor: pointer; display: flex; justify-content: space-between; align-items: center;" data-toggle="collapse" data-target="#scrollSection">
                <div>
                    <img src="/file/icon/scroll.svg" style="height: 23px">
                    <span class="jd-effect-text">스크롤 애니메이션</span>
                    <button class="jd-effect-btn">
                        <img :src="`/file/icon/${row.option_scroll}.svg`" style="width: 16px; height: 16px;">
                        <span class="fs-14">{{row.option_scroll}}</span>
                    </button>
                </div>
                <i class="glyphicon glyphicon-chevron-down"></i>
            </div>
            <div id="scrollSection" class="panel-collapse collapse">
                <div class="panel-body">
                    <div class="row" style="display: flex;">
                        <div class="col-md-6" style="border-right: 1px solid #ddd;">
                            <div class="jd-action-button" :class="{active : row.option_scroll == '효과없음'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '효과없음'">
                                <img src="/file/icon/효과없음.svg">
                                <span>효과없음</span>
                            </div>
                            <div class="jd-action-button" :class="{active : row.option_scroll == '페이드'}" @click="row.option_scroll_direction = 'center';  row.option_scroll = '페이드'">
                                <img src="/file/icon/페이드.svg">
                                <span>페이드</span>
                            </div>
                        </div>

                        <div class="col-md-6" style="display : flex;">
                            <template v-if="row.option_scroll == '효과없음'">
                                <div style="flex: 1; text-align: center; align-content: center;">
                                    <span style="font-size: large; color : #959595; ">스크롤 애니메이션을 선택하지 않았습니다.</span>
                                </div>
                            </template>

                            <template v-else>
                                <div style="display: flex; flex-direction: column; gap: 15px; padding: 15px;">
                                    <!-- 방향 -->
                                    <div style="display: flex; align-items: center; gap: 10px;">
                                        <label style="margin: 0; font-weight: 600; font-size: 14px;">방향</label>
                                        <div style="display: flex; gap: 6px;">
                                            <button class="jd-direction-btn" v-if="row.option_scroll != '플립'" @click="row.option_scroll_direction = 'center'" :class="{active : row.option_scroll_direction == 'center'}">
                                                <i class="fas fa-circle" style="font-size: 12px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_scroll_direction = 'up'" :class="{active : row.option_scroll_direction == 'up'}">
                                                <i class="fas fa-arrow-up" style="font-size: 14px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_scroll_direction = 'down'" :class="{active : row.option_scroll_direction == 'down'}">
                                                <i class="fas fa-arrow-down" style="font-size: 14px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_scroll_direction = 'left'" :class="{active : row.option_scroll_direction == 'left'}">
                                                <i class="fas fa-arrow-left" style="font-size: 14px;"></i>
                                            </button>
                                            <button class="jd-direction-btn" @click="row.option_scroll_direction = 'right'" :class="{active : row.option_scroll_direction == 'right'}">
                                                <i class="fas fa-arrow-right" style="font-size: 14px;"></i>
                                            </button>
                                        </div>
                                    </div>

                                    <div style="display: flex; align-items: center; gap: 5px;">
                                        <label style="margin: 0; font-weight: 600; font-size: 14px;">지속시간</label>
                                        <input type="number" v-number v-model="row.option_scroll_duration" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                        <span style="font-size: 14px; color: #666;">ms</span>

                                        <label style="margin: 0; font-weight: 600; font-size: 14px; margin-left : 10px;">지연시간</label>
                                        <input type="number" v-number v-model="row.option_scroll_delay" class="form-control" style="width: 75px; text-align: right; height: 25px;">
                                        <span style="font-size: 14px; color: #666;">ms</span>
                                    </div>

                                </div>
                            </template>

                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div class="modal-footer" style="position: sticky; right: 0; bottom: 0;">
            <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
            <button type="button" class="btn btn-orange" @click="save()">저장</button>
        </div>
        <design-widget-image-free-modal v-model="freeImageModal" :widget_type="'text_bg'"></design-widget-image-free-modal>
    </div>
    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name: "design-widget-text-modal", object: {
            template: "#design-widget-text-modal-template",
            props: {
                primary: {type: String, default: ""},
                widget_code: {type: String, default: ""},
                site_code: {type: String, default: ""},
            },
            data() {
                return {
                    load: false,
                    component_name: "design-widget-text-modal",
                    component_idx: "",

                    table: null,
                    freeImageModal: this.lib.createModalObject(),
                    row: {
                        upfile : null, // 업로드시 임시 처리용,
                        free_image_src : "",
                        free_image_name : "",
                        free_image_width : "",
                        free_image_height : "",
                        file_idx : "", // 있는 데이터시 조인으로 가져올예정
                        widget_code: this.widget_code,
                        content: "",

                        bg_color: "#FFFFFF00",
                        box_fill: false,

                        border_use: false,
                        border_color: "#000000",
                        border_width: 1,

                        border_radius_use: false,
                        border_radius_px: 6,

                        box_shadow_use: false,
                        box_shadow_opacity: "20",

                        box_height_top: "",
                        box_height_right: "",
                        box_height_bottom: "",
                        box_height_left: "",

                        margin_top: "0",
                        margin_right: "0",
                        margin_bottom: "0",
                        margin_left: "0",

                        option_scroll: "효과없음",
                        option_scroll_direction: "center",
                        option_scroll_duration: "1000",
                        option_scroll_delay: "0",
                    },
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name;
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_text");
                this.table.where('widget_code', this.widget_code);
                this.table.join("jd_file", "file_idx");
                let row = await this.table.get();
                if (row.length > 0) this.row = Object.assign(this.row, row[0]);
                this.normalizeRow();

                this.sessions = await this.session.all();
                this.row.user_idx = this.sessions.mem_idx;

                this.load = true;
            },
            methods: {
                normalizeRow() {
                    if (!this.row.primary && this.row.idx) {
                        this.row.primary = this.row.idx;
                    }

                    ["box_height_top", "box_height_right", "box_height_bottom", "box_height_left"].forEach((field) => {
                        if (this.row[field] === null || typeof this.row[field] === "undefined") {
                            this.row[field] = "";
                        }
                    });

                    ["margin_top", "margin_right", "margin_bottom", "margin_left"].forEach((field) => {
                        if (this.row[field] === null || typeof this.row[field] === "undefined") {
                            this.row[field] = "0";
                        }
                    });
                },
                async save() {
                    this.normalizeRow();
                    window.design.loadingOn();

                    if (this.row.upfile) {
                        let res = await this.lib.ajax("file_save", {
                            "user_idx"    : this.row.user_idx,
                            "upfile" : this.row.upfile,
                            "memo"        : "jd_widget_text",
                            "widget_code" : this.row.widget_code,
                            "site_code"   : this.site_code,
                        });

                        let file = res['file'];
                        this.row.upfile      = null;
                        this.row.file_idx    = file.primary;
                        this.row.$jd_file__name = file.name || '';
                        this.row.$jd_file__src  = file.src || '';
                    }

                    if (this.row.free_image_src) {
                        const freeImageName = this.truncateText(this.row.free_image_name || "freepik-image", 240);
                        const freeImageSrc = this.truncateText(this.row.free_image_src, 255);
                        let fileRes = await this.api.table("jd_file").post({
                            table_name: "jd_widget_text",
                            table_primary: this.row.idx || this.row.widget_code,
                            keyword: "text_bg",
                            name: freeImageName,
                            size: 0,
                            ext: this.getImageExt(freeImageSrc),
                            src: freeImageSrc,
                            height: this.row.free_image_height || 0,
                            width: this.row.free_image_width || 0,
                            path: freeImageSrc,
                            rename: freeImageName,
                            save_position: "remote",
                            update_date: "now()",
                            memo: "freepik",
                            widget_code: this.row.widget_code,
                            site_code: this.site_code,
                        }, {return: true});

                        this.row.file_idx = fileRes.primary;
                        this.row.$jd_file__name = freeImageName;
                        this.row.$jd_file__src = freeImageSrc;
                        this.row.free_image_src = "";
                        this.row.free_image_name = "";
                        this.row.free_image_width = "";
                        this.row.free_image_height = "";
                    }

                    await this.table.post(this.row, {return: true});

                    window.design.loadingOff();

                    window.JAYDREAM_VUE_GLOBAL[this.widget_code].reload = true;

                    $(this.$el).closest('.modal').modal('hide');
                },
                async changeBgImage(event) {
                    this.row.$jd_file__name = '';
                    this.row.$jd_file__src = '';
                    this.row.free_image_src = '';
                    this.row.free_image_name = '';
                    this.row.free_image_width = '';
                    this.row.free_image_height = '';
                    await this.vue.changeFile(event, this.row, 'upfile');
                    event.target.value = '';
                },
                clearBgImage() {
                    this.row.upfile = null;
                    this.row.file_idx = '';
                    this.row.$jd_file__name = '';
                    this.row.$jd_file__src = '';
                    this.row.free_image_src = '';
                    this.row.free_image_name = '';
                    this.row.free_image_width = '';
                    this.row.free_image_height = '';
                    if (this.$refs.bgImageInput) this.$refs.bgImageInput.value = '';
                },
                getImageExt(src) {
                    if (!src) return 'jpg';
                    const path = src.split('?')[0].toLowerCase();
                    const match = path.match(/\.([a-z0-9]+)$/);
                    return match ? match[1] : 'jpg';
                },
                truncateText(value, limit) {
                    value = value || '';
                    return value.length > limit ? value.substring(0, limit) : value;
                }
            },
            computed: {
                imageSrc() {
                    return (this.row.upfile && this.row.upfile.src)
                        ? this.row.upfile.src
                        : (this.row.free_image_src || this.row.$jd_file__src || '');
                },
                isEmptyContent() {
                    const content = this.row.content;
                    return !content || content.replace(/<[^>]*>/g, '').trim() === '';
                },
                previewOuterStyle() {
                    return {
                        paddingTop:    (this.row.margin_top    || 0) + 'px',
                        paddingRight:  (this.row.margin_right  || 0) + 'px',
                        paddingBottom: (this.row.margin_bottom || 0) + 'px',
                        paddingLeft:   (this.row.margin_left   || 0) + 'px',
                        outline: '2px dashed rgba(150, 150, 150, 0.5)',
                        outlineOffset: '-1px',
                        width: '100%',
                        boxSizing: 'border-box',
                    };
                },
                previewStyle() {
                    let style = {};

                    style.backgroundColor = this.row.bg_color || '#FFFFFF00';
                    style.paddingTop    = (this.row.box_height_top    || 0) + 'px';
                    style.paddingRight  = (this.row.box_height_right  || 0) + 'px';
                    style.paddingBottom = (this.row.box_height_bottom || 0) + 'px';
                    style.paddingLeft   = (this.row.box_height_left   || 0) + 'px';

                    const hasBorderOrShadow = this.row.border_use || this.row.box_shadow_use;
                    if (!hasBorderOrShadow) {
                        style.outline = '2px dashed rgba(150, 150, 150, 0.8)';
                        style.outlineOffset = '-1px';
                    }

                    if (this.row.border_use) {
                        style.border = `${this.row.border_width}px solid ${this.row.border_color}`;
                    }

                    if (this.row.border_radius_use) {
                        style.borderRadius = `${this.row.border_radius_px}px`;
                    }

                    if (this.row.box_shadow_use) {
                        style.boxShadow = `0px 4px 8px rgba(0,0,0,${this.row.box_shadow_opacity / 100})`;
                    }

                    if (this.imageSrc) {
                        style.backgroundImage = `url(${this.imageSrc})`;
                        style.backgroundSize = this.row.box_fill ? 'cover' : 'auto';
                        style.backgroundRepeat = 'no-repeat';
                        style.backgroundPosition = 'center';
                    }

                    return style;
                },
            },
            watch: {},
        }});
</script>

<style>
    .design-widget-text-modal-preview-outer {
        width: 100%;
        box-sizing: border-box;
        transition: padding 0.15s;
    }

    .design-widget-text-modal-preview-box {
        width: 100%;
        min-height: 50px;
        padding: 12px 16px;
        word-break: break-all;
        font-size: 14px;
        box-sizing: border-box;
        transition: padding 0.15s, background-color 0.15s;
    }

    .design-widget-text-modal-preview-box table {
        width: 100% !important;
        border-collapse: collapse !important;
        border-spacing: 0 !important;
    }

    .design-widget-text-modal-preview-box table,
    .design-widget-text-modal-preview-box th,
    .design-widget-text-modal-preview-box td {
        border: 1px solid #b8b8b8 !important;
    }

    .design-widget-text-modal-preview-box th,
    .design-widget-text-modal-preview-box td {
        min-width: 36px !important;
        min-height: 28px !important;
        height: 28px;
        padding: 6px 8px !important;
        background-clip: padding-box;
    }

    .design-widget-text-modal-preview-box table.table,
    .design-widget-text-modal-preview-box table.table-bordered {
        border: 1px solid #b8b8b8 !important;
    }

    .design-widget-text-modal-preview-box table.table-bordered > thead > tr > th,
    .design-widget-text-modal-preview-box table.table-bordered > tbody > tr > th,
    .design-widget-text-modal-preview-box table.table-bordered > tfoot > tr > th,
    .design-widget-text-modal-preview-box table.table-bordered > thead > tr > td,
    .design-widget-text-modal-preview-box table.table-bordered > tbody > tr > td,
    .design-widget-text-modal-preview-box table.table-bordered > tfoot > tr > td {
        border: 1px solid #b8b8b8 !important;
        min-width: 36px !important;
        height: 28px;
        padding: 6px 8px !important;
    }

    .design-widget-text-modal-section {
        margin-bottom: 16px;
    }

    .design-widget-text-modal-section-title {
        font-weight: bold;
        font-size: 15px;
        margin-bottom: 12px;
        display: flex;
        align-items: center;
        gap: 6px;
    }

    .design-widget-text-modal-option-row {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 20px;
    }

    .design-widget-text-modal-option-group {
        display: flex;
        align-items: center;
        gap: 8px;
    }

    .design-widget-text-modal-label {
        font-size: 14px;
        white-space: nowrap;
    }

    .design-widget-text-modal-select {
        width: auto;
        min-width: 70px;
    }

    .design-widget-text-modal-image-name {
        width: 160px;
    }

    /* 숫자 스테퍼 */
    .design-widget-text-modal-number-input-wrapper {
        position: relative;
        width: 80px;
    }

    .design-widget-text-modal-number-input-wrapper input {
        width: 100% !important;  /* !important 추가 */
        padding-left: 24px !important;
        padding-right: 24px !important;
        text-align: center;
        font-size: 14px;
    }

    .design-widget-text-modal-input-icon-left {
        position: absolute;
        left: 6px;
        top: 50%;
        transform: translateY(-50%);
        width: 14px;
        height: 14px;
        pointer-events: none;
    }

    .design-widget-text-modal-number-stepper {
        position: absolute;
        right: 4px;
        top: 50%;
        transform: translateY(-50%);
        display: flex;
        flex-direction: column;
    }

    .design-widget-text-modal-stepper-up,
    .design-widget-text-modal-stepper-down {
        border: none;
        background: none;
        padding: 0;
        margin: 0;
        cursor: pointer;
        line-height: 1;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 14px;
        height: 10px;
    }

    /* 여백 설정 */
    .design-widget-text-modal-margin-wrap {
        display: flex;
        gap: 40px;
        flex-wrap: wrap;
    }

    .design-widget-text-modal-margin-block {
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: 12px;
    }

    .design-widget-text-modal-margin-label {
        font-size: 13px;
        font-weight: 600;
        color: #333;
    }

    .design-widget-text-modal-margin-grid {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 5px;
    }

    .design-widget-text-modal-margin-top,
    .design-widget-text-modal-margin-bottom {
        display: flex;
        justify-content: center;
    }

    .design-widget-text-modal-margin-top input,
    .design-widget-text-modal-margin-bottom input {
        width: 60px;
        text-align: center;
    }

    .design-widget-text-modal-margin-middle {
        display: flex;
        align-items: center;
        gap: 5px;
    }

    .design-widget-text-modal-margin-middle input {
        text-align: center;
    }

    .design-widget-text-modal-margin-center-box {
        width: 60px;
        height: 30px;
        background: #f5f5f5;
        border: 1px solid #ddd;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 11px;
        color: #999;
    }

    /* 스크롤 애니메이션 패널 */
    .design-widget-text-modal-effect-text {
        font-size: 14px;
        font-weight: 600;
    }

    .design-widget-text-modal-effect-btn {
        border: none;
        background: none;
        padding: 0;
        cursor: default;
    }

    .design-widget-text-modal-badge {
        background: #f0f0f0;
        border-radius: 20px;
        padding: 2px 10px;
        font-size: 13px;
        color: #555;
    }

    .panel-default > .panel-heading {
        background-image: linear-gradient(to bottom, #ffffff 0%, #ffffff 100%) !important;
    }
</style>
<script type="text/x-template" id="design-widget-text-template">
    <div v-if="load" ref="widgetEl">
        <template v-if="row.idx">
            <template v-if="!editor">
                <div class="design-widget-text-content" @dblclick="onEditor" v-html="displayContent" :style="widgetStyle"></div>
            </template>

            <template v-else>
                <div>
                    <external-summernote :row field="content" :editor-style="editorWidgetStyle" :image-size-control="true"></external-summernote>
                </div>
            </template>
        </template>

        <template v-else>
            <template v-if="!editor">
                <div class="design-widget-text-content" @dblclick="onEditor" v-html="displayContent" :style="widgetStyle"></div>
            </template>

            <template v-else>
                <div>
                    <external-summernote :row field="content" :editor-style="editorWidgetStyle" :image-size-control="true"></external-summernote>
                </div>
            </template>
        </template>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name : "design-widget-text",object : {
            template: "#design-widget-text-template",
            props: {
                primary : {type : String, default : ""},
                widget_code : {type : String, default : ""},
            },
            data: function () {
                return {
                    load : false,
                    component_name : "design-widget-text",
                    component_idx: "",

                    table : null,
                    row: {
                        widget_code: this.widget_code,
                        content: "",

                        bg_color: "#FFFFFF00",
                        box_fill: false,

                        border_use: false,
                        border_color: "#000000",
                        border_width: 1,

                        border_radius_use: false,
                        border_radius_px: 6,

                        box_shadow_use: false,
                        box_shadow_opacity: "20",

                        box_height_top: "",
                        box_height_right: "",
                        box_height_bottom: "",
                        box_height_left: "",

                        margin_top: "0",
                        margin_right: "0",
                        margin_bottom: "0",
                        margin_left: "0",

                        option_scroll: "효과없음",
                        option_scroll_direction: "center",
                        option_scroll_duration: "1000",
                        option_scroll_delay: "0",
                    },
                    rows : [],

                    build : false,
                    editor : false,
                    defaultContentHtml: '<p style="text-align:center; font-size:15px; margin:10px 0;"><span>더블 클릭하여 텍스트를 입력하세요.</span></p>',

                    observer: null,
                    bodyClassObserver: null,
                    toolbarResizeObserver: null,
                    toolbarLayoutRaf: null,
                    scrollEvent: false,
                    editorMouseStartedInside: false,
                    mobilePcSectionLocked: false,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_text");
                this.table.where('widget_code', this.widget_code);
                this.table.join("jd_file", "file_idx");

                await this.getRow();

                this.load = true;

                this.$nextTick(async () => {
                    this.initScrollObserver();
                    const wrapper = document.getElementById(this.widget_code);
                    this.wrapper = wrapper;
                    this.syncMobilePcSectionLock();
                    this.bindBodyClassObserver();
                    this.startToolbarLayoutWatcher();
                    const firewall = wrapper?.querySelector('.firewall');
                    if (firewall) {
                        firewall.classList.remove('firewall');
                        this.build = true;

                        this._onOutsideClick = async (e) => {
                            if (!this.editor) return;
                            const mouseStartedInside = this.editorMouseStartedInside;
                            this.editorMouseStartedInside = false;
                            if (this.isSummernoteInteraction(e.target)) return;
                            if (wrapper.contains(e.target)) return;
                            if (mouseStartedInside) return;

                            await this.table.post(this.row, {return: true});
                            if(!this.row.idx) await this.getRow();

                            const draggable = this.wrapper.closest('.ui-draggable');
                            if(draggable) $(draggable).draggable('enable');

                            this.setEditorActive(false);
                            this.editor = false;
                        };
                        this._onEditorMouseDown = (e) => {
                            if (!this.editor) return;
                            this.editorMouseStartedInside = wrapper.contains(e.target) || this.isSummernoteInteraction(e.target);
                        };
                        document.addEventListener('mousedown', this._onEditorMouseDown, true);
                        document.addEventListener('click', this._onOutsideClick);
                    }
                });
            },
            updated() {

            },
            unmounted() {
                if (this._onEditorMouseDown) document.removeEventListener('mousedown', this._onEditorMouseDown, true);
                if (this._onOutsideClick) document.removeEventListener('click', this._onOutsideClick);
                if (this._onToolbarViewportChange) {
                    window.removeEventListener('resize', this._onToolbarViewportChange);
                    window.removeEventListener('scroll', this._onToolbarViewportChange, true);
                }
                if (this.toolbarResizeObserver) this.toolbarResizeObserver.disconnect();
                if (this.bodyClassObserver) this.bodyClassObserver.disconnect();
                if (this.toolbarLayoutRaf) {
                    if (typeof cancelAnimationFrame === 'function') {
                        cancelAnimationFrame(this.toolbarLayoutRaf);
                    } else {
                        clearTimeout(this.toolbarLayoutRaf);
                    }
                }
                if (this.observer) this.observer.disconnect();
            },
            methods: {
                initScrollObserver() {
                    if (this.row.option_scroll !== '페이드') return;
                    let component = this;
                    let time = parseInt(this.row.option_scroll_delay) + parseInt(this.row.option_scroll_duration);
                    this.observer = new IntersectionObserver((entries) => {
                        entries.forEach(entry => {
                            if (entry.isIntersecting) {
                                component.scrollEvent = true;
                                setTimeout(() => {
                                    component.scrollEvent = false;
                                }, time);
                            } else {
                                component.scrollEvent = false;
                            }
                        });
                    }, { threshold: 0.1 });

                    if (this.$refs.widgetEl) this.observer.observe(this.$refs.widgetEl);
                },
                async getRow() {
                    let row = await this.table.get();
                    if (row.length > 0) this.row = Object.assign(this.row, row[0]);
                    this.normalizeRow();
                },
                normalizeRow() {
                    if (!this.row.primary && this.row.idx) {
                        this.row.primary = this.row.idx;
                    }

                    ["box_height_top", "box_height_right", "box_height_bottom", "box_height_left"].forEach((field) => {
                        if (this.row[field] === null || typeof this.row[field] === "undefined") {
                            this.row[field] = "";
                        }
                    });

                    ["margin_top", "margin_right", "margin_bottom", "margin_left"].forEach((field) => {
                        if (this.row[field] === null || typeof this.row[field] === "undefined") {
                            this.row[field] = "0";
                        }
                    });
                },
                isSummernoteInteraction(target) {
                    const element = target && target.nodeType === 3 ? target.parentElement : target;
                    if (!element || typeof element.closest !== 'function') return false;

                    return !!element.closest('.note-editor, .note-toolbar, .note-popover, .note-modal, .dropdown-menu');
                },
                getCookie(name) {
                    const cookies = (`; ${document.cookie}`).split(`; ${name}=`);
                    return cookies.length === 2 ? cookies.pop().split(';').shift() : '';
                },
                isMobileDesignMode() {
                    return document.body.classList.contains('_mbv') || this.getCookie('lc_device') === 'mobile';
                },
                isMobilePcSection() {
                    const wrapper = this.wrapper || document.getElementById(this.widget_code);
                    const section = wrapper && wrapper.closest ? wrapper.closest('section') : null;
                    return this.isMobileDesignMode() && !!section && !section.classList.contains('from-mobile');
                },
                alertMobilePcSectionBlocked() {
                    const message = 'PC에서 만든 섹션 또는 위젯은 모바일 디자인모드에서 설정할 수 없습니다.';
                    if (window.jQuery && jQuery.alert) {
                        jQuery.alert(message);
                    } else {
                        alert(message);
                    }
                },
                syncMobilePcSectionLock() {
                    this.mobilePcSectionLocked = this.isMobilePcSection();

                    if (this.mobilePcSectionLocked && this.editor) {
                        const draggable = this.wrapper && this.wrapper.closest ? this.wrapper.closest('.ui-draggable') : null;
                        if (draggable && window.jQuery) jQuery(draggable).draggable('enable');
                        this.setEditorActive(false);
                        this.editor = false;
                    }
                },
                bindBodyClassObserver() {
                    if (this.bodyClassObserver || typeof MutationObserver === 'undefined') return;

                    this.bodyClassObserver = new MutationObserver(() => {
                        this.syncMobilePcSectionLock();
                    });
                    this.bodyClassObserver.observe(document.body, {
                        attributes: true,
                        attributeFilter: ['class'],
                    });
                },
                guardMobilePcSection(event) {
                    this.syncMobilePcSectionLock();
                    if (!this.mobilePcSectionLocked) return true;

                    if (event) {
                        event.preventDefault();
                        event.stopPropagation();
                    }
                    this.alertMobilePcSectionBlocked();
                    return false;
                },
                onEditor(event) {
                    if (!this.guardMobilePcSection(event)) return false;
                    if(!this.build) return false;
                    const draggable = this.wrapper.closest('.ui-draggable');
                    if(draggable) $(draggable).draggable('disable');
                    this.setEditorActive(true);
                    this.editor = true;
                    this.$nextTick(() => {
                        this.scheduleTextToolbarLayout();
                        setTimeout(() => this.scheduleTextToolbarLayout(), 0);
                        setTimeout(() => this.scheduleTextToolbarLayout(), 80);
                    });
                },
                setEditorActive(active) {
                    if (!this.wrapper) return;
                    this.wrapper.classList.toggle('design-widget-text-editor-active', !!active);
                    if (!active) {
                        this.wrapper.classList.remove('design-widget-text-editor-narrow');
                        this.clearTextToolbarPosition();
                    }
                    if (active) {
                        this.wrapper.querySelectorAll('.widget_setting_box').forEach((el) => el.remove());
                        this.scheduleTextToolbarLayout();
                    }
                },
                startToolbarLayoutWatcher() {
                    if (!this.wrapper) return;

                    if (typeof ResizeObserver === 'function') {
                        this.toolbarResizeObserver = new ResizeObserver(() => {
                            this.scheduleTextToolbarLayout();
                        });
                        this.toolbarResizeObserver.observe(this.wrapper);
                    }

                    this._onToolbarViewportChange = () => this.scheduleTextToolbarLayout();
                    window.addEventListener('resize', this._onToolbarViewportChange);
                    window.addEventListener('scroll', this._onToolbarViewportChange, true);
                },
                scheduleTextToolbarLayout() {
                    if (!this.wrapper) return;

                    if (this.toolbarLayoutRaf) {
                        if (typeof cancelAnimationFrame === 'function') {
                            cancelAnimationFrame(this.toolbarLayoutRaf);
                        } else {
                            clearTimeout(this.toolbarLayoutRaf);
                        }
                    }

                    const callback = () => {
                        this.toolbarLayoutRaf = null;
                        this.updateTextToolbarLayout();
                    };

                    if (typeof requestAnimationFrame === 'function') {
                        this.toolbarLayoutRaf = requestAnimationFrame(callback);
                    } else {
                        this.toolbarLayoutRaf = setTimeout(callback, 0);
                    }
                },
                updateTextToolbarLayout() {
                    if (!this.wrapper) return;

                    const active = this.wrapper.classList.contains('design-widget-text-editor-active');
                    const toolbar = this.wrapper.querySelector('.note-editor.note-frame .note-toolbar');

                    if (!active || !this.editor || !toolbar) {
                        this.wrapper.classList.remove('design-widget-text-editor-narrow');
                        this.clearTextToolbarPosition();
                        return;
                    }

                    const mobileViewport = window.matchMedia && window.matchMedia('(max-width: 767px)').matches;
                    const rect = this.wrapper.getBoundingClientRect();
                    const narrow = !mobileViewport && rect.width > 0 && rect.width < 760;

                    this.wrapper.classList.toggle('design-widget-text-editor-narrow', narrow);

                    if (!narrow) {
                        this.clearTextToolbarPosition();
                        return;
                    }

                    const viewportWidth = window.innerWidth || document.documentElement.clientWidth || 0;
                    const viewportHeight = window.innerHeight || document.documentElement.clientHeight || 0;
                    const padding = 12;
                    const availableWidth = Math.max(0, viewportWidth - (padding * 2));
                    const toolbarWidth = Math.min(720, availableWidth);
                    const toolbarHeight = Math.min(toolbar.offsetHeight || 48, Math.max(0, viewportHeight - (padding * 2)));

                    let left = rect.left + (rect.width / 2) - (toolbarWidth / 2);
                    let top = rect.top - toolbarHeight - 8;

                    left = Math.max(padding, Math.min(left, viewportWidth - toolbarWidth - padding));

                    if (top < padding) {
                        top = rect.bottom + 8;
                    }
                    if (top + toolbarHeight > viewportHeight - padding) {
                        top = viewportHeight - toolbarHeight - padding;
                    }
                    top = Math.max(padding, top);

                    this.wrapper.style.setProperty('--design-widget-text-toolbar-left', left + 'px');
                    this.wrapper.style.setProperty('--design-widget-text-toolbar-top', top + 'px');
                    this.wrapper.style.setProperty('--design-widget-text-toolbar-width', toolbarWidth + 'px');
                },
                clearTextToolbarPosition() {
                    if (!this.wrapper) return;

                    this.wrapper.style.removeProperty('--design-widget-text-toolbar-left');
                    this.wrapper.style.removeProperty('--design-widget-text-toolbar-top');
                    this.wrapper.style.removeProperty('--design-widget-text-toolbar-width');
                }
            },
            computed: {
                isEmptyContent() {
                    const content = this.row.content;
                    return !content || content.replace(/<[^>]*>/g, '').trim() === '';
                },
                displayContent() {
                    return this.isEmptyContent ? this.defaultContentHtml : this.row.content;
                },
                widgetStyle() {
                    let style = {};

                    style.backgroundColor = this.row.bg_color || '#FFFFFF00';
                    style.paddingTop    = (this.row.box_height_top    || 0) + 'px';
                    style.paddingRight  = (this.row.box_height_right  || 0) + 'px';
                    style.paddingBottom = (this.row.box_height_bottom || 0) + 'px';
                    style.paddingLeft   = (this.row.box_height_left   || 0) + 'px';
                    style.marginTop     = (this.row.margin_top    || 0) + 'px';
                    style.marginRight   = (this.row.margin_right  || 0) + 'px';
                    style.marginBottom  = (this.row.margin_bottom || 0) + 'px';
                    style.marginLeft    = (this.row.margin_left   || 0) + 'px';

                    if (this.row.border_use) {
                        style.border = `${this.row.border_width}px solid ${this.row.border_color}`;
                    }

                    if (this.row.border_radius_use) {
                        style.borderRadius = `${this.row.border_radius_px}px`;
                    }

                    if (this.row.box_shadow_use) {
                        style.boxShadow = `0px 4px 8px rgba(0,0,0,${this.row.box_shadow_opacity / 100})`;
                    }

                    if (this.row.$jd_file__src) {
                        style.backgroundImage = `url(${this.row.$jd_file__src})`;
                        style.backgroundSize = this.row.box_fill ? 'cover' : 'auto';
                        style.backgroundRepeat = 'no-repeat';
                        style.backgroundPosition = 'center';
                    }

                    if (this.row.option_scroll === '페이드' && this.scrollEvent) {
                        const direction = this.row.option_scroll_direction || 'center';
                        const duration = parseInt(this.row.option_scroll_duration) || 1000;
                        const delay = parseInt(this.row.option_scroll_delay) || 0;
                        style.animation = `design-widget-text-fade-${direction} ${duration}ms ease ${delay}ms both`;
                    }

                    return style;
                },
                editorWidgetStyle() {
                    const style = this.widgetStyle;

                    return {
                        backgroundColor: style.backgroundColor,
                        backgroundImage: style.backgroundImage || '',
                        backgroundSize: style.backgroundSize || '',
                        backgroundRepeat: style.backgroundRepeat || '',
                        backgroundPosition: style.backgroundPosition || '',
                        borderRadius: style.borderRadius || '',
                        paddingTop: style.paddingTop,
                        paddingRight: style.paddingRight,
                        paddingBottom: style.paddingBottom,
                        paddingLeft: style.paddingLeft,
                    };
                },
                appState() {
                    return window.JAYDREAM_VUE_GLOBAL[this.$app_name];
                }
            },
            watch: {
                appState: {
                    async handler(newVal) {
                        if(newVal.reload) {
                            let row = await this.table.get();
                            if(row.length > 0) this.row = Object.assign(this.row, row[0]);
                            this.normalizeRow();
                            this.appState.reload = false;
                        }
                    },
                    deep: true
                }
            }
        }});
</script>

<style>
    @keyframes design-widget-text-fade-up { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
    @keyframes design-widget-text-fade-down { from { opacity: 0; transform: translateY(-30px); } to { opacity: 1; transform: translateY(0); } }
    @keyframes design-widget-text-fade-left { from { opacity: 0; transform: translateX(30px); } to { opacity: 1; transform: translateX(0); } }
    @keyframes design-widget-text-fade-right { from { opacity: 0; transform: translateX(-30px); } to { opacity: 1; transform: translateX(0); } }
    @keyframes design-widget-text-fade-center { from { opacity: 0; } to { opacity: 1; } }

    .design-widget-text-editor-active .widget_setting_box {
        display: none !important;
    }

    .design-widget-text-editor-active .note-editor.note-frame {
        position: relative;
        overflow: visible;
        border: 0;
        border-radius: 4px;
        font-family: inherit;
        font-size: inherit;
        font-weight: inherit;
        line-height: inherit;
        color: inherit;
        box-shadow: 0 0 0 2px rgba(255, 95, 55, 0.08);
    }

    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar {
        position: absolute;
        left: 0;
        right: auto;
        bottom: calc(100% + 8px);
        z-index: 2600;
        display: flex;
        flex-wrap: nowrap;
        align-items: center;
        gap: 6px;
        width: 100%;
        max-width: none;
        min-height: 0;
        padding: 8px;
        border: 1px solid #d7dde7;
        border-radius: 8px;
        background: #fff;
        box-shadow: 0 10px 28px rgba(15, 23, 42, 0.16);
        overflow: visible;
        white-space: nowrap;
        box-sizing: border-box;
    }

    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar > .note-btn-group {
        display: flex;
        flex-wrap: nowrap;
        align-items: center;
        gap: 2px;
        flex: 0 1 auto;
        max-width: 100%;
        min-width: 0;
        margin: 0;
        padding: 2px;
        border: 1px solid #e8edf3;
        border-radius: 6px;
        background: #f8fafc;
        box-sizing: border-box;
    }

    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-btn:not(.note-color-btn) {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 30px;
        height: 30px;
        padding: 4px 8px;
        border: 0 !important;
        border-radius: 4px !important;
        background: #fff;
        color: #1f2937;
        line-height: 1;
        box-shadow: none !important;
    }

    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-btn:not(.note-color-btn):hover,
    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-btn:not(.note-color-btn):focus,
    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-btn:not(.note-color-btn).active {
        background: #eef2f7;
        color: #111827;
    }

    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-current-fontname {
        display: inline-block;
        max-width: 88px;
        overflow: hidden;
        text-overflow: ellipsis;
        vertical-align: bottom;
        white-space: nowrap;
    }

    .design-widget-text-editor-active.design-widget-text-editor-narrow .note-editor.note-frame .note-toolbar {
        position: fixed;
        left: var(--design-widget-text-toolbar-left, 12px);
        right: auto;
        top: var(--design-widget-text-toolbar-top, 64px);
        bottom: auto;
        z-index: 5000;
        flex-wrap: wrap;
        width: var(--design-widget-text-toolbar-width, calc(100vw - 24px));
        max-width: calc(100vw - 24px);
        padding: 6px;
        gap: 3px;
        overflow: visible;
        white-space: normal;
    }

    .design-widget-text-editor-active.design-widget-text-editor-narrow .note-editor.note-frame .note-toolbar > .note-btn-group {
        flex: 0 1 auto;
        max-width: 100%;
    }

    .design-widget-text-editor-active.design-widget-text-editor-narrow .note-editor.note-frame .note-toolbar .note-fontname {
        flex: 0 1 110px;
    }

    .design-widget-text-editor-active.design-widget-text-editor-narrow .note-editor.note-frame .note-toolbar .note-current-fontname {
        max-width: 72px;
    }

    body._mbv .design-widget-text-editor-active .note-editor.note-frame .note-toolbar {
        flex-wrap: wrap;
        width: min(400px, 100%);
        padding: 5px;
        gap: 2px 0;
        overflow: visible;
        white-space: normal;
    }

    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .dropdown-menu,
    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-dropdown-menu {
        z-index: 5200;
    }

    body._mbv .design-widget-text-editor-active .note-editor.note-frame .note-toolbar > .note-btn-group {
        display: contents;
        gap: 0;
        padding: 0;
    }

    body._mbv .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-btn:not(.note-color-btn) {
        min-width: 28px;
        height: 30px;
        padding-right: 6px;
        padding-left: 6px;
        border: 1px solid #e8edf3 !important;
    }

    body._mbv .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-current-fontname {
        max-width: 80px;
    }

    body._mbv .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-fontname .note-btn {
        width: 86px;
        justify-content: space-between;
    }

    body._mbv .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-color .note-btn:not(.note-color-btn) {
        width: 25px;
        min-width: 25px;
        padding-right: 3px;
        padding-left: 3px;
    }

    body._mbv .design-widget-text-editor-active.design-widget-text-editor-narrow .note-editor.note-frame .note-toolbar {
        position: fixed;
        left: var(--design-widget-text-toolbar-left, 12px);
        right: auto;
        top: var(--design-widget-text-toolbar-top, 64px);
        bottom: auto;
        z-index: 5000;
        flex-wrap: wrap;
        width: var(--design-widget-text-toolbar-width, calc(100vw - 24px));
        max-width: calc(100vw - 24px);
        padding: 6px;
        gap: 3px;
        overflow: visible;
        white-space: normal;
    }

    body._mbv .design-widget-text-editor-active.design-widget-text-editor-narrow .note-editor.note-frame .note-toolbar > .note-btn-group {
        display: flex;
        flex: 0 1 auto;
        max-width: 100%;
        gap: 2px;
        padding: 2px;
    }

    body._mbv .design-widget-text-editor-active.design-widget-text-editor-narrow .note-editor.note-frame .note-toolbar .note-fontname {
        flex: 0 1 110px;
    }

    body._mbv .design-widget-text-editor-active.design-widget-text-editor-narrow .note-editor.note-frame .note-toolbar .note-current-fontname {
        max-width: 72px;
    }

    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-color-palette {
        line-height: 1;
    }

    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-color-palette .note-color-row {
        height: 20px;
    }

    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-color-palette .note-color-btn {
        display: inline-block;
        width: 20px;
        min-width: 0;
        height: 20px;
        padding: 0;
        border: 0 !important;
        border-radius: 0 !important;
        line-height: 20px;
        box-shadow: none !important;
    }

    .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-palette .note-holder-custom .note-color-btn {
        border: 1px solid #eee !important;
    }

    body._mbv > .tooltip,
    body._mbv .tooltip,
    body._mbv .note-editor .tooltip,
    body._mbv .note-popover .tooltip {
        z-index: 7000 !important;
        pointer-events: none;
    }

    .design-widget-text-editor-active .note-tooltip,
    .design-widget-text-editor-active .note-editor.note-frame .note-tooltip {
        display: none !important;
    }

    @media (max-width: 767px) {
        .design-widget-text-editor-active .note-editor.note-frame .note-toolbar {
            position: fixed;
            left: 12px;
            right: 12px;
            top: 56px;
            bottom: auto;
            z-index: 5000;
            width: auto;
            max-width: none;
            max-height: 42vh;
            overflow-x: hidden;
            overflow-y: auto;
        }

        .design-widget-text-editor-active .note-editor.note-frame .note-toolbar > .note-btn-group {
            flex: 0 1 auto;
        }

        .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-fontname {
            flex: 1 1 128px;
        }

        .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-fontname .note-btn {
            width: 100%;
            justify-content: space-between;
        }

        .design-widget-text-editor-active .note-editor.note-frame .note-toolbar .note-btn:not(.note-color-btn) {
            min-width: 32px;
            height: 32px;
        }

    }

    .design-widget-text-editor-active .note-editor.note-frame .note-status-output,
    .design-widget-text-editor-active .note-editor.note-frame .note-statusbar {
        display: none;
    }

    .design-widget-text-editor-active .note-editor.note-frame .note-editing-area,
    .design-widget-text-editor-active .note-editor.note-frame .note-editable {
        border-radius: 4px;
        font-family: inherit;
        font-size: inherit;
        font-weight: inherit;
        line-height: inherit;
        color: inherit;
    }

    .design-widget-text-content,
    .design-widget-text-editor-active .note-editor.note-frame .note-editable {
        line-height: 1.7;
    }

    .design-widget-text-content p,
    .design-widget-text-editor-active .note-editor.note-frame .note-editable p {
        line-height: inherit;
        margin-bottom: 0;
    }

    .design-widget-text-content li,
    .design-widget-text-editor-active .note-editor.note-frame .note-editable li {
        line-height: inherit;
        margin-bottom: 4px;
    }

    .design-widget-text-content ul,
    .design-widget-text-content ol,
    .design-widget-text-editor-active .note-editor.note-frame .note-editable ul,
    .design-widget-text-editor-active .note-editor.note-frame .note-editable ol {
        margin-bottom: 16px;
    }

    .design-widget-text-content table {
        width: 100% !important;
        border-collapse: collapse !important;
        border-spacing: 0 !important;
    }

    .design-widget-text-content table,
    .design-widget-text-content th,
    .design-widget-text-content td {
        border: 1px solid #b8b8b8 !important;
    }

    .design-widget-text-content th,
    .design-widget-text-content td {
        min-width: 36px !important;
        min-height: 28px !important;
        height: 28px;
        padding: 6px 8px !important;
        background-clip: padding-box;
    }

    .design-widget-text-content table.table,
    .design-widget-text-content table.table-bordered {
        border: 1px solid #b8b8b8 !important;
    }

    .design-widget-text-content table.table-bordered > thead > tr > th,
    .design-widget-text-content table.table-bordered > tbody > tr > th,
    .design-widget-text-content table.table-bordered > tfoot > tr > th,
    .design-widget-text-content table.table-bordered > thead > tr > td,
    .design-widget-text-content table.table-bordered > tbody > tr > td,
    .design-widget-text-content table.table-bordered > tfoot > tr > td {
        border: 1px solid #b8b8b8 !important;
        min-width: 36px !important;
        height: 28px;
        padding: 6px 8px !important;
    }
</style>
<script type="text/x-template" id="design-widget-button-modal-template">
    <div v-if="load" class="jaydream design-widget-button-modal" style="font-size:14px;">

        <!-- 버튼 미리보기 -->
        <div class="design-widget-button-modal-preview-section">
            <button
                    class="btn"
                    :class="[previewHoverClass, {
                        'design-widget-button-modal-btn-vertical': !isIconOnlyButton && (row.icon_position === '위' || row.icon_position === '아래'),
                        'design-widget-button-modal-btn-icon-only': isIconOnlyButton,
                    }]"
                    :style="previewButtonStyle"
                    @mouseenter="previewHover = true"
                    @mouseleave="previewHover = false"
            >
                <!-- 아이콘 전용 -->
                <template v-if="isIconOnlyButton">
                    <span v-if="parsedIcon.library === 'feather'" v-html="renderIconHtml(parsedIcon.library, parsedIcon.name, row.icon_size, row.icon_color)" style="display:inline-flex;align-items:center;justify-content:center;"></span>
                    <i v-else-if="parsedIcon.library === 'bootstrap'" :class="'bi bi-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px'}"></i>
                    <i v-else-if="parsedIcon.library === 'phosphor'" :class="'ph ph-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px'}"></i>
                    <i v-else-if="parsedIcon.library === 'simpleline'" :class="'icon-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px'}"></i>
                </template>

                <!-- 아이콘 위 -->
                <template v-if="!isIconOnlyButton && parsedIcon && row.icon_position === '위'">
                    <span v-if="parsedIcon.library === 'feather'" v-html="renderIconHtml(parsedIcon.library, parsedIcon.name, row.icon_size, row.icon_color)" style="display:inline-flex;align-items:center;margin-bottom:4px;"></span>
                    <i v-else-if="parsedIcon.library === 'bootstrap'" :class="'bi bi-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginBottom: '4px'}"></i>
                    <i v-else-if="parsedIcon.library === 'phosphor'" :class="'ph ph-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginBottom: '4px'}"></i>
                    <i v-else-if="parsedIcon.library === 'simpleline'" :class="'icon-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginBottom: '4px'}"></i>
                </template>

                <!-- 아이콘 왼쪽 -->
                <template v-if="!isIconOnlyButton && parsedIcon && row.icon_position === '왼쪽'">
                    <span v-if="parsedIcon.library === 'feather'" v-html="renderIconHtml(parsedIcon.library, parsedIcon.name, row.icon_size, row.icon_color)" style="display:inline-flex;align-items:center;margin-right:4px;"></span>
                    <i v-else-if="parsedIcon.library === 'bootstrap'" :class="'bi bi-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginRight: '4px'}"></i>
                    <i v-else-if="parsedIcon.library === 'phosphor'" :class="'ph ph-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginRight: '4px'}"></i>
                    <i v-else-if="parsedIcon.library === 'simpleline'" :class="'icon-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginRight: '4px'}"></i>
                </template>

                <span v-if="hasLabelText" class="design-widget-button-modal-label-text">{{ row.label }}</span>

                <!-- 아이콘 오른쪽 -->
                <template v-if="!isIconOnlyButton && parsedIcon && row.icon_position === '오른쪽'">
                    <span v-if="parsedIcon.library === 'feather'" v-html="renderIconHtml(parsedIcon.library, parsedIcon.name, row.icon_size, row.icon_color)" style="display:inline-flex;align-items:center;margin-left:4px;"></span>
                    <i v-else-if="parsedIcon.library === 'bootstrap'" :class="'bi bi-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginLeft: '4px'}"></i>
                    <i v-else-if="parsedIcon.library === 'phosphor'" :class="'ph ph-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginLeft: '4px'}"></i>
                    <i v-else-if="parsedIcon.library === 'simpleline'" :class="'icon-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginLeft: '4px'}"></i>
                </template>

                <!-- 아이콘 아래 -->
                <template v-if="!isIconOnlyButton && parsedIcon && row.icon_position === '아래'">
                    <span v-if="parsedIcon.library === 'feather'" v-html="renderIconHtml(parsedIcon.library, parsedIcon.name, row.icon_size, row.icon_color)" style="display:inline-flex;align-items:center;margin-top:4px;"></span>
                    <i v-else-if="parsedIcon.library === 'bootstrap'" :class="'bi bi-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginTop: '4px'}"></i>
                    <i v-else-if="parsedIcon.library === 'phosphor'" :class="'ph ph-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginTop: '4px'}"></i>
                    <i v-else-if="parsedIcon.library === 'simpleline'" :class="'icon-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: row.icon_size + 'px', marginTop: '4px'}"></i>
                </template>
            </button>
        </div>

        <hr>

        <!-- 레이아웃 설정 -->
        <div class="design-widget-button-modal-section">
            <div class="design-widget-button-modal-section-title">
                <img src="/file/icon/레이아웃.svg" style="height: 18px;"> 레이아웃 설정
            </div>
            <div class="design-widget-button-modal-field-row">
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">너비</span>
                    <div class="design-widget-button-modal-option-group">
                        <select class="form-control form-control-sm design-widget-button-modal-select" v-model="row.layout_width">
                            <option value="자동">자동</option>
                            <option value="직접입력">직접입력</option>
                        </select>
                        <template v-if="row.layout_width === '직접입력'">
                            <input type="number" class="form-control form-control-sm" v-model="row.layout_width_px" style="width: 65px;">
                            <span class="design-widget-button-modal-label">px</span>
                        </template>
                    </div>
                </div>
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">높이</span>
                    <div class="design-widget-button-modal-option-group">
                        <select class="form-control form-control-sm design-widget-button-modal-select" v-model="row.layout_height">
                            <option value="자동">자동</option>
                            <option value="직접입력">직접입력</option>
                        </select>
                        <template v-if="row.layout_height === '직접입력'">
                            <input type="number" class="form-control form-control-sm" v-model="row.layout_height_px" style="width: 65px;">
                            <span class="design-widget-button-modal-label">px</span>
                        </template>
                    </div>
                </div>
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">버튼 위치</span>
                    <select class="form-control form-control-sm design-widget-button-modal-select" v-model="row.layout_align">
                        <option value="좌측">좌측</option>
                        <option value="중앙">중앙</option>
                        <option value="우측">우측</option>
                    </select>
                </div>
            </div>
        </div>

        <hr>

        <!-- 버튼 요소 설정 -->
        <div class="design-widget-button-modal-section">
            <div class="design-widget-button-modal-section-title">
                <img src="/file/icon/button/button.svg" style="height: 14px;"> 버튼 요소 설정
            </div>

            <!-- 1행: 버튼 문구 / 폰트 / 폰트 크기 / 버튼 색상 / 폰트 색상 -->
            <div class="design-widget-button-modal-field-row" style="margin-bottom: 14px;">
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">버튼 문구</span>
                    <input type="text" class="form-control form-control-sm" v-model="row.label">
                </div>
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">폰트</span>
                    <select class="form-control form-control-sm" v-model="row.font">
                                                                                <option value="Galmuri14" style="font-family:'Galmuri14','sans-serif';">
                                갈무리14                            </option>
                                                                                <option value="GamjaFlower" style="font-family:'GamjaFlower','sans-serif';">
                                감자꽃마을                            </option>
                                                                                <option value="KNUTruth" style="font-family:'KNUTruth','sans-serif';">
                                경북대진리체                            </option>
                                                                                <option value="GowunDodum" style="font-family:'GowunDodum','sans-serif';">
                                고운돋움                            </option>
                                                                                <option value="GowunBatang" style="font-family:'GowunBatang','sans-serif';">
                                고운바탕                            </option>
                                                                                <option value="Gulim" style="font-family:'Gulim','sans-serif';">
                                굴림                            </option>
                                                                                <option value="GulimChe" style="font-family:'GulimChe','sans-serif';">
                                굴림체                            </option>
                                                                                <option value="Gungsuh" style="font-family:'Gungsuh','sans-serif';">
                                궁서체                            </option>
                                                                                <option value="GungsuhChe" style="font-family:'GungsuhChe','sans-serif';">
                                궁서체                            </option>
                                                                                <option value="Malgun Gothic" style="font-family:'Malgun Gothic','sans-serif';">
                                맑은 고딕                            </option>
                                                                                <option value="NanumGothic" style="font-family:'NanumGothic','sans-serif';">
                                나눔고딕                            </option>
                                                                                <option value="NanumMyeongjo" style="font-family:'NanumMyeongjo','sans-serif';">
                                나눔명조                            </option>
                                                                                <option value="NanumBarunGothic" style="font-family:'NanumBarunGothic','sans-serif';">
                                나눔바른고딕                            </option>
                                                                                <option value="NanumBarunpen" style="font-family:'NanumBarunpen','sans-serif';">
                                나눔바른펜                            </option>
                                                                                <option value="NanumSquare" style="font-family:'NanumSquare','sans-serif';">
                                나눔스퀘어                            </option>
                                                                                <option value="NanumSquareRound" style="font-family:'NanumSquareRound','sans-serif';">
                                나눔스퀘어라운드                            </option>
                                                                                <option value="NanumPenScript" style="font-family:'NanumPenScript','sans-serif';">
                                나눔펜스크립트                            </option>
                                                                                <option value="Dokdo" style="font-family:'Dokdo','sans-serif';">
                                독도체                            </option>
                                                                                <option value="Dotum" style="font-family:'Dotum','sans-serif';">
                                돋움                            </option>
                                                                                <option value="DotumChe" style="font-family:'DotumChe','sans-serif';">
                                돋움체                            </option>
                                                                                <option value="Dongle" style="font-family:'Dongle','sans-serif';">
                                동글체                            </option>
                                                                                <option value="EastSeaDokdo" style="font-family:'EastSeaDokdo','sans-serif';">
                                동해독도체                            </option>
                                                                                <option value="Batang" style="font-family:'Batang','sans-serif';">
                                바탕                            </option>
                                                                                <option value="BatangChe" style="font-family:'BatangChe','sans-serif';">
                                바탕체                            </option>
                                                                                <option value="New Gulim" style="font-family:'New Gulim','sans-serif';">
                                새굴림                            </option>
                                                                                <option value="Sogang_university" style="font-family:'Sogang_University','sans-serif';">
                                서강체                            </option>
                                                                                <option value="SeolleimCool" style="font-family:'SeolleimCool','sans-serif';">
                                시원한 설레임체                            </option>
                                                                                <option value="SBAggro" style="font-family:'SBAggro','sans-serif';">
                                어그로체                            </option>
                                                                                <option value="PyeongChang" style="font-family:'PyeongChang','sans-serif';">
                                평창체                            </option>
                                                                                <option value="PyeongChangPeace" style="font-family:'PyeongChangPeace','sans-serif';">
                                평창평화체                            </option>
                                                                                <option value="Pretendard" style="font-family:'Pretendard','sans-serif';">
                                프리텐다드                            </option>
                                                                                <option value="Skybori" style="font-family:'Skybori','sans-serif';">
                                하늘보리체                            </option>
                                                                                <option value="HiMelody" style="font-family:'HiMelody','sans-serif';">
                                하이멜로디                            </option>
                                                                                <option value="휴먼엽서체" style="font-family:'휴먼엽서체','sans-serif';">
                                휴먼엽서체                            </option>
                                                                                <option value="Arial" style="font-family:'Arial','sans-serif';">
                                Arial                            </option>
                                                                                <option value="Arial Black" style="font-family:'Arial Black','sans-serif';">
                                Arial Black                            </option>
                                                                                <option value="BlackHanSans" style="font-family:'BlackHanSans','sans-serif';">
                                BlackHanSans                            </option>
                                                                                <option value="Comic Sans MS" style="font-family:'Comic Sans MS','sans-serif';">
                                Comic Sans MS                            </option>
                                                                                <option value="Courier New" style="font-family:'Courier New','sans-serif';">
                                Courier New                            </option>
                                                                                <option value="CuteFont" style="font-family:'CuteFont','sans-serif';">
                                Cute Font                            </option>
                                                                                <option value="GmarketSans" style="font-family:'GmarketSans','sans-serif';">
                                Gmarket Sans                            </option>
                                                                                <option value="IBMPlexSansKR" style="font-family:'IBMPlexSansKR','sans-serif';">
                                IBM Plex Sans KR                            </option>
                                                                                <option value="SongMyung" style="font-family:'SongMyung','sans-serif';">
                                J송명                            </option>
                                                                                <option value="NotoSans-Regular" style="font-family:'NotoSans-Regular','sans-serif';">
                                NotoSans                            </option>
                                                                                <option value="Orbit" style="font-family:'Orbit','sans-serif';">
                                Orbit                            </option>
                                                                                <option value="PoorStory" style="font-family:'PoorStory','sans-serif';">
                                PoorStory                            </option>
                                                                                <option value="SingleDay" style="font-family:'SingleDay','sans-serif';">
                                Single Day                            </option>
                                                                                <option value="Tahoma" style="font-family:'Tahoma','sans-serif';">
                                Tahoma                            </option>
                                                                                <option value="Times New Roman" style="font-family:'Times New Roman','sans-serif';">
                                Times New Roman                            </option>
                                                                                <option value="Verdana" style="font-family:'Verdana','sans-serif';">
                                Verdana                            </option>
                                            </select>
                </div>
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">폰트 크기</span>
                    <select class="form-control form-control-sm" v-model="row.font_size">
                        <option v-for="n in [10,11,12,13,14,15,16,18,20,22,24,28,32,36]" :key="n" :value="n">{{ n }}</option>
                    </select>
                </div>
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">버튼 색상</span>
                    <external-picker v-model="row.bg_color"></external-picker>
                </div>
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">폰트 색상</span>
                    <external-picker v-model="row.font_color"></external-picker>
                </div>
            </div>

            <!-- 2행: 아이콘 추가 / 미리보기 / 색상 / 위치 / 크기 -->
            <div class="design-widget-button-modal-option-row" style="margin-bottom: 14px;">
                <div class="design-widget-button-modal-option-group">
                    <span class="design-widget-button-modal-label">아이콘 추가</span>
                    <button class="btn btn-orange btn-sm" @click="addIcon">아이콘 추가</button>

                    <!-- 선택된 아이콘 미리보기 -->
                    <template v-if="parsedIcon">
                        <div class="design-widget-button-modal-icon-preview">
                            <span v-if="parsedIcon.library === 'feather'" v-html="renderIconHtml(parsedIcon.library, parsedIcon.name, 20, row.icon_color)"></span>
                            <i v-else-if="parsedIcon.library === 'bootstrap'" :class="'bi bi-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: '20px'}"></i>
                            <i v-else-if="parsedIcon.library === 'phosphor'" :class="'ph ph-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: '20px'}"></i>
                            <i v-else-if="parsedIcon.library === 'simpleline'" :class="'icon-' + parsedIcon.name" :style="{color: row.icon_color, fontSize: '20px'}"></i>
                        </div>
                        <button class="btn btn-default btn-sm" @click="row.icon_src = ''" title="아이콘 제거">
                            <i class="glyphicon glyphicon-remove"></i>
                        </button>
                    </template>
                </div>

                <div class="design-widget-button-modal-option-group">
                    <span class="design-widget-button-modal-label">버튼 내 아이콘 위치</span>
                    <select class="form-control form-control-sm design-widget-button-modal-select" v-model="row.icon_position">
                        <option value="왼쪽">왼쪽</option>
                        <option value="오른쪽">오른쪽</option>
                        <option value="위">위</option>
                        <option value="아래">아래</option>
                    </select>
                </div>
                <div class="design-widget-button-modal-option-group">
                    <span class="design-widget-button-modal-label">아이콘 크기</span>
                    <div class="design-widget-button-modal-number-input-wrapper">
                        <input type="text" class="form-control form-control-sm" v-model="row.icon_size" v-number style="width: 55px;">
                        <div class="design-widget-button-modal-number-stepper">
                            <button type="button" class="design-widget-button-modal-stepper-up" @click="row.icon_size = (parseInt(row.icon_size)||0)+1">
                                <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 0L10 6H0L5 0Z" fill="#666"/></svg>
                            </button>
                            <button type="button" class="design-widget-button-modal-stepper-down" @click="row.icon_size = Math.max(1,(parseInt(row.icon_size)||0)-1)">
                                <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 6L0 0H10L5 6Z" fill="#666"/></svg>
                            </button>
                        </div>
                    </div>
                    <span class="design-widget-button-modal-label">px</span>
                </div>
            </div>

            <!-- 3행: 테두리 / 모서리 둥글게 / 그림자 -->
            <div class="design-widget-button-modal-option-row">
                <!-- 테두리 -->
                <div class="design-widget-button-modal-option-group">
                    <input type="checkbox" v-model="row.border_use">
                    <span class="design-widget-button-modal-label">테두리</span>
                    <external-picker v-model="row.border_color"></external-picker>
                    <div class="design-widget-button-modal-number-input-wrapper">
                        <input type="text" class="form-control form-control-sm" v-model="row.border_width" v-number style="width: 50px;">
                        <div class="design-widget-button-modal-number-stepper">
                            <button type="button" class="design-widget-button-modal-stepper-up" @click="row.border_width = (parseInt(row.border_width)||0)+1">
                                <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 0L10 6H0L5 0Z" fill="#666"/></svg>
                            </button>
                            <button type="button" class="design-widget-button-modal-stepper-down" @click="row.border_width = Math.max(0,(parseInt(row.border_width)||0)-1)">
                                <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 6L0 0H10L5 6Z" fill="#666"/></svg>
                            </button>
                        </div>
                    </div>
                </div>

                <!-- 모서리 둥글게 -->
                <div class="design-widget-button-modal-option-group">
                    <input type="checkbox" v-model="row.border_radius_use">
                    <span class="design-widget-button-modal-label">모서리 둥글게</span>
                    <div class="design-widget-button-modal-number-input-wrapper design-widget-button-modal-with-icon">
                        <img src="/file/icon/Group 95.svg" class="design-widget-button-modal-input-icon-left">
                        <input type="text" v-model="row.border_radius_px" class="form-control form-control-sm" v-number style="width: 65px;">
                        <div class="design-widget-button-modal-number-stepper">
                            <button type="button" class="design-widget-button-modal-stepper-up" @click="row.border_radius_px++">
                                <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 0L10 6H0L5 0Z" fill="#666"/></svg>
                            </button>
                            <button type="button" class="design-widget-button-modal-stepper-down" @click="row.border_radius_px--">
                                <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 6L0 0H10L5 6Z" fill="#666"/></svg>
                            </button>
                        </div>
                    </div>
                </div>

                <!-- 그림자 -->
                <div class="design-widget-button-modal-option-group">
                    <input type="checkbox" v-model="row.box_shadow_use">
                    <span class="design-widget-button-modal-label">그림자</span>
                    <select class="form-control form-control-sm design-widget-button-modal-select" v-model="row.box_shadow_opacity">
                        <option value="10">10%</option>
                        <option value="20">20%</option>
                        <option value="30">30%</option>
                        <option value="40">40%</option>
                        <option value="50">50%</option>
                        <option value="60">60%</option>
                        <option value="70">70%</option>
                        <option value="80">80%</option>
                        <option value="90">90%</option>
                    </select>
                </div>
            </div>
        </div>

        <hr>

        <!-- 마우스 클릭 동작 -->
        <div class="design-widget-button-modal-section">
            <div class="design-widget-button-modal-section-title">
                <img src="/file/icon/mouse_click.svg" style="height: 18px;"> 마우스 클릭 동작
            </div>
            <div class="design-widget-button-modal-option-row" style="margin-bottom: 10px;">
                <div class="design-widget-button-modal-option-group">
                    <select class="form-control form-control-sm design-widget-button-modal-select" v-model="row.mouse_click_type" @change="onMouseClickTypeChange">
                        <option value="URL">URL</option>
                        <option value="페이지">페이지</option>
                        <option value="섹션 스크롤">섹션 스크롤</option>
                        <option value="전화걸기">전화걸기</option>
                        <option value="메일 보내기">메일 보내기</option>
                        <option value="파일 다운로드">파일 다운로드</option>
                    </select>

                    <template v-if="row.mouse_click_type === 'URL'">
                        <input type="text" class="form-control form-control-sm" v-model="row.mouse_click_url" placeholder="https://" style="width: 320px;">
                    </template>

                    <template v-if="row.mouse_click_type === '페이지'">
                        <template v-if="menus.length === 0">
                            <span style="font-size:12px; color:#999;">데이터를 불러들이는 중입니다.</span>
                        </template>
                        <template v-else>
                            <select class="form-control form-control-sm" v-model="row.mouse_click_url" style="width: 320px;">
                                <option value="">선택해주세요.</option>
                                <template v-for="item in menus">
                                    <option :value="item.url">{{ item.name }}</option>
                                </template>
                            </select>
                        </template>
                    </template>

                    <template v-if="row.mouse_click_type === '섹션 스크롤'">
                        <input type="text" class="form-control form-control-sm" v-model="row.mouse_click_url" placeholder="현재 페이지 내 섹션 ID 입력(#SC)" style="width: 320px;">
                    </template>

                    <template v-if="row.mouse_click_type === '전화걸기'">
                        <input type="text" class="form-control form-control-sm" v-model="row.mouse_click_url" placeholder="전화번호 입력(-제외)" style="width: 320px;">
                    </template>

                    <template v-if="row.mouse_click_type === '메일 보내기'">
                        <input type="text" class="form-control form-control-sm" v-model="row.mouse_click_url" placeholder="이메일 입력(example@email.com)" style="width: 320px;">
                    </template>

                    <template v-if="row.mouse_click_type === '파일 다운로드'">
                        <div class="design-widget-button-modal-file-row">
                            <input type="text" class="form-control form-control-sm design-widget-button-modal-file-name" readonly :value="downloadFileName" placeholder="첨부 파일을 선택하세요.">
                            <button type="button" class="btn btn-orange btn-sm" @click="$refs.downloadFileInput.click()">파일 첨부</button>
                            <button v-if="downloadFileName" type="button" class="design-widget-button-modal-file-clear" @click="clearDownloadFile">
                                <i class="far fa-times-circle"></i>
                            </button>
                            <input class="hidden" ref="downloadFileInput" type="file" @change="vue.changeFile($event, row, 'mouse_click_upfile')">
                        </div>
                    </template>
                </div>
            </div>
            <div v-if="showTargetOptions" class="design-widget-button-modal-option-row">
                <label class="design-widget-button-modal-radio-label">
                    <input type="radio" v-model="row.mouse_click_target" value="_self"> 기존 창에서 이동
                </label>
                <label class="design-widget-button-modal-radio-label">
                    <input type="radio" v-model="row.mouse_click_target" value="_blank"> 새 창으로 열기
                </label>
            </div>
            <div v-if="row.mouse_click_type === '메일 보내기'" class="design-widget-button-modal-option-row design-widget-button-modal-help-row">
                <p class="design-widget-button-modal-help-text">
                    기본 메일 앱이 설정된 기기에서 실행됩니다. 실행되지 않으면 이메일 주소를 자동으로 복사합니다.
                </p>
            </div>
        </div>

        <hr>

        <!-- 마우스 오버 애니메이션 -->
        <div class="design-widget-button-modal-section">
            <div class="design-widget-button-modal-section-title">
                <img src="/file/icon/mouse_over.svg" style="height: 18px;"> 마우스 오버 애니메이션
            </div>
            <div class="design-widget-button-modal-field-row">
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">마우스 오버 효과</span>
                    <select class="form-control form-control-sm design-widget-button-modal-select" v-model="row.hover_effect">
                        <option value="사용안함">사용안함</option>
                        <option value="사용함">사용함</option>
                    </select>
                </div>
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">버튼 색상</span>
                    <external-picker v-model="row.hover_bg_color"></external-picker>
                </div>
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">폰트 색상</span>
                    <external-picker v-model="row.hover_font_color"></external-picker>
                </div>
                <div class="design-widget-button-modal-option-group">
                    <input type="checkbox" v-model="row.hover_border_use">
                    <span class="design-widget-button-modal-label">테두리</span>
                    <external-picker v-model="row.hover_border_color"></external-picker>
                    <div class="design-widget-button-modal-number-input-wrapper">
                        <input type="text" class="form-control form-control-sm" v-model="row.hover_border_width" v-number style="width: 50px;">
                        <div class="design-widget-button-modal-number-stepper">
                            <button type="button" class="design-widget-button-modal-stepper-up" @click="row.hover_border_width = (parseInt(row.hover_border_width)||0)+1">
                                <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 0L10 6H0L5 0Z" fill="#666"/></svg>
                            </button>
                            <button type="button" class="design-widget-button-modal-stepper-down" @click="row.hover_border_width = Math.max(0,(parseInt(row.hover_border_width)||0)-1)">
                                <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 6L0 0H10L5 6Z" fill="#666"/></svg>
                            </button>
                        </div>
                    </div>
                </div>
                <div class="design-widget-button-modal-option-group">
                    <input type="checkbox" v-model="row.hover_box_shadow_use">
                    <span class="design-widget-button-modal-label">그림자</span>
                    <select class="form-control form-control-sm design-widget-button-modal-select" v-model="row.hover_box_shadow_opacity">
                        <option value="10">10%</option>
                        <option value="20">20%</option>
                        <option value="30">30%</option>
                        <option value="40">40%</option>
                        <option value="50">50%</option>
                        <option value="60">60%</option>
                        <option value="70">70%</option>
                        <option value="80">80%</option>
                        <option value="90">90%</option>
                    </select>
                </div>
                <div class="design-widget-button-modal-field-group">
                    <span class="design-widget-button-modal-label">전환 애니메이션</span>
                    <select class="form-control form-control-sm design-widget-button-modal-select" v-model="row.hover_transition">
                        <option value="없음">없음</option>
                        <option value="페이드">페이드</option>
                        <option value="줌인">줌인</option>
                        <option value="줌아웃">줌아웃</option>
                        <option value="떠오르기">떠오르기</option>
                    </select>
                </div>
            </div>
        </div>

        <div class="modal-footer" style="position:sticky;right:0;bottom:0;">
            <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
            <button type="button" class="btn btn-orange save" @click="save()">저장</button>
        </div>

        <external-svg v-model="iconModal" @select="onIconSelect"></external-svg>

    </div>
    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name: "design-widget-button-modal", object: {
            template: "#design-widget-button-modal-template",
            props: {
                primary    : {type: String, default: ""},
                widget_code: {type: String, default: ""},
                site_code  : {type: String, default: ""},
            },
            data() {
                return {
                    load          : false,
                    component_name: "design-widget-button-modal",
                    component_idx : "",
                    iconModal     : this.lib.createModalObject(),
                    injectUrls    : ["https://cdnjs.cloudflare.com/ajax/libs/hover.css/2.3.1/css/hover-min.css"],

                    table: null,
                    fontFamilyMap: {"Galmuri14":"Galmuri14","GamjaFlower":"GamjaFlower","KNUTruth":"KNUTruth","GowunDodum":"GowunDodum","GowunBatang":"GowunBatang","Gulim":"Gulim","GulimChe":"GulimChe","Gungsuh":"Gungsuh","GungsuhChe":"GungsuhChe","Malgun Gothic":"Malgun Gothic","NanumGothic":"NanumGothic","NanumMyeongjo":"NanumMyeongjo","NanumBarunGothic":"NanumBarunGothic","NanumBarunpen":"NanumBarunpen","NanumSquare":"NanumSquare","NanumSquareRound":"NanumSquareRound","NanumPenScript":"NanumPenScript","Dokdo":"Dokdo","Dotum":"Dotum","DotumChe":"DotumChe","Dongle":"Dongle","EastSeaDokdo":"EastSeaDokdo","Batang":"Batang","BatangChe":"BatangChe","New Gulim":"New Gulim","Sogang_university":"Sogang_University","SeolleimCool":"SeolleimCool","SBAggro":"SBAggro","PyeongChang":"PyeongChang","PyeongChangPeace":"PyeongChangPeace","Pretendard":"Pretendard","Skybori":"Skybori","HiMelody":"HiMelody","휴먼엽서체":"휴먼엽서체","Arial":"Arial","Arial Black":"Arial Black","BlackHanSans":"BlackHanSans","Comic Sans MS":"Comic Sans MS","Courier New":"Courier New","CuteFont":"CuteFont","GmarketSans":"GmarketSans","IBMPlexSansKR":"IBMPlexSansKR","SongMyung":"SongMyung","NotoSans-Regular":"NotoSans-Regular","Orbit":"Orbit","PoorStory":"PoorStory","SingleDay":"SingleDay","Tahoma":"Tahoma","Times New Roman":"Times New Roman","Verdana":"Verdana"},
                    fontAliasMap : {"Noto Sans KR":"NotoSans-Regular","Nanum Gothic":"NanumGothic","Nanum Myeongjo":"NanumMyeongjo","맑은 고딕":"Malgun Gothic","궁서":"Gungsuh","굴림체":"GulimChe","돋음체":"DotumChe","돋움체":"DotumChe","바탕체":"BatangChe"},
                    row: {
                        mouse_click_upfile       : null,
                        widget_code             : this.widget_code,
                        label                   : 'BUTTON',
                        font                    : 'Pretendard',
                        font_size               : 14,
                        bg_color                : '#ffffff',
                        font_color              : '#000000',
                        icon_src                : '',
                        icon_color              : '#000000',
                        icon_position           : '왼쪽',
                        icon_size               : 16,
                        border_use              : true,
                        border_color            : '#000000',
                        border_width            : 1,
                        border_radius_use       : false,
                        border_radius_px        : 6,
                        box_shadow_use          : false,
                        box_shadow_opacity      : 20,
                        mouse_click_type        : 'URL',
                        mouse_click_url         : '',
                        mouse_click_target      : '_self',
                        hover_effect            : '사용안함',
                        hover_bg_color          : '#000000',
                        hover_font_color        : '#ffffff',
                        hover_border_use        : false,
                        hover_border_color      : '#000000',
                        hover_border_width      : 1,
                        hover_box_shadow_use    : false,
                        hover_box_shadow_opacity: 20,
                        hover_transition        : '없음',
                        layout_width            : '자동',
                        layout_width_px         : 150,
                        layout_height           : '자동',
                        layout_height_px        : 40,
                        layout_align            : '중앙',
                    },

                    user_idx    : "",
                    previewHover: false,
                    menus       : [],
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name;
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_button");
                this.table.where('widget_code', this.widget_code);
                let row = await this.table.get();
                if (row.length > 0) this.row = Object.assign({}, this.row, row[0]);
                this.row.font = this.normalizeFontValue(this.row.font);
                this.row.hover_transition = this.normalizeHoverTransition(this.row.hover_transition);

                const sessions = await this.session.all();
                this.user_idx = sessions.mem_idx || "";

                this.load = true;
                this.$nextTick(async () => {
                    this.api.table("menu").where("site_code", this.site_code).get(this.menus);
                });
            },
            methods: {
                async save() {
                    window.design.loadingOn();
                    this.row.font = this.normalizeFontValue(this.row.font);
                    this.row.hover_transition = this.normalizeHoverTransition(this.row.hover_transition);

                    if (this.row.mouse_click_type === '파일 다운로드' && this.row.mouse_click_upfile) {
                        const res = await this.lib.ajax("file_save", {
                            user_idx    : this.user_idx,
                            upfile      : this.row.mouse_click_upfile,
                            memo        : "jd_widget_button",
                            widget_code : this.row.widget_code,
                            site_code   : this.site_code,
                            save_position: "local",
                        });

                        const file = res.file || {};
                        this.row.mouse_click_url = file.src || "";
                        this.row.mouse_click_target = file.name || "";
                        this.row.mouse_click_upfile = null;
                    }

                    if (this.showTargetOptions && this.row.mouse_click_target !== '_blank') {
                        this.row.mouse_click_target = '_self';
                    }

                    await this.table.post(this.row, {return: true});

                    window.design.loadingOff();

                    window.JAYDREAM_VUE_GLOBAL[this.widget_code].reload = true;

                    $(this.$el).closest('.modal').modal('hide');
                },
                addIcon() {
                    this.$openModal(this.iconModal);
                },
                onMouseClickTypeChange() {
                    this.row.mouse_click_url = '';
                    this.row.mouse_click_upfile = null;

                    if (this.row.mouse_click_type === 'URL' || this.row.mouse_click_type === '페이지') {
                        this.row.mouse_click_target = '_self';
                        return;
                    }

                    this.row.mouse_click_target = this.row.mouse_click_type === '파일 다운로드' ? '' : '';
                },
                onIconSelect(icon) {
                    this.row.icon_src   = icon.src;
                    this.row.icon_color = icon.color;
                    this.row.icon_size  = icon.size;
                },
                clearDownloadFile() {
                    this.row.mouse_click_upfile = null;
                    this.row.mouse_click_url = '';
                    this.row.mouse_click_target = '';
                    if (this.$refs.downloadFileInput) this.$refs.downloadFileInput.value = '';
                },
                renderIconHtml(library, name, size, color) {
                    if (library === 'feather') {
                        if (typeof feather !== 'undefined' && feather.icons[name]) {
                            return feather.icons[name].toSvg({width: size, height: size, color});
                        }
                        return '';
                    }
                    return '';
                },
                normalizeFontValue(font) {
                    return this.fontAliasMap[font] || font || 'Pretendard';
                },
                getFontFamily(font) {
                    const normalized = this.normalizeFontValue(font);
                    const family = this.fontFamilyMap[normalized] || normalized || 'Pretendard';
                    return "'" + String(family).replace(/'/g, "\\'") + "', sans-serif";
                },
                normalizeHoverTransition(transition) {
                    return transition === '슬라이드' ? '없음' : (transition || '없음');
                },
            },
            computed: {
                showTargetOptions() {
                    return this.row.mouse_click_type === 'URL' || this.row.mouse_click_type === '페이지';
                },
                downloadFileName() {
                    if (this.row.mouse_click_upfile && this.row.mouse_click_upfile.name) return this.row.mouse_click_upfile.name;
                    if (this.row.mouse_click_type !== '파일 다운로드') return '';
                    if (this.row.mouse_click_target && this.row.mouse_click_target !== '_self' && this.row.mouse_click_target !== '_blank') {
                        return this.row.mouse_click_target;
                    }
                    if (!this.row.mouse_click_url) return '';
                    return this.row.mouse_click_url.split('/').pop();
                },
                parsedIcon() {
                    if (!this.row.icon_src) return null;
                    const parts = this.row.icon_src.split(':');
                    if (parts.length < 2) return null;
                    return {library: parts[0], name: parts[1]};
                },
                hasLabelText() {
                    return String(this.row.label || '').trim().length > 0;
                },
                isIconOnlyButton() {
                    return !!this.parsedIcon && !this.hasLabelText;
                },
                previewHoverClass() {
                    const classMap = {
                        '페이드'  : 'hvr-fade',
                        '줌인'   : 'hvr-grow',
                        '줌아웃'  : 'hvr-sink',
                        '떠오르기': 'hvr-float',
                    };
                    return this.row.hover_transition !== '없음' ? (classMap[this.row.hover_transition] || '') : '';
                },
                previewButtonStyle() {
                    const base = {
                        backgroundColor: this.row.bg_color,
                        color          : this.row.font_color,
                        border         : this.row.border_use ? `${this.row.border_width}px solid ${this.row.border_color}` : '1px solid #ddd',
                        borderRadius   : this.row.border_radius_use ? `${this.row.border_radius_px}px` : '0px',
                        boxShadow      : this.row.box_shadow_use ? `0 4px 12px rgba(0,0,0,${this.row.box_shadow_opacity / 100})` : '',
                        fontFamily     : this.getFontFamily(this.row.font),
                        fontSize       : this.row.font_size + 'px',
                        width          : this.row.layout_width === '직접입력' && this.row.layout_width_px ? this.row.layout_width_px + 'px' : 'auto',
                        height         : this.row.layout_height === '직접입력' && this.row.layout_height_px ? this.row.layout_height_px + 'px' : 'auto',
                        display        : 'inline-flex',
                        alignItems     : 'center',
                        justifyContent : 'center',
                    };

                    if (this.previewHover && this.row.hover_effect !== '사용안함') {
                        return {
                            ...base,
                            backgroundColor: this.row.hover_bg_color,
                            color          : this.row.hover_font_color,
                            border         : this.row.hover_border_use ? `${this.row.hover_border_width}px solid ${this.row.hover_border_color}` : base.border,
                            boxShadow      : this.row.hover_box_shadow_use ? `0 4px 12px rgba(0,0,0,${this.row.hover_box_shadow_opacity / 100})` : base.boxShadow,
                        };
                    }

                    return base;
                },
            },
            watch: {},
        }});
</script>

<style>
    .design-widget-button-modal-btn-vertical {
        display: inline-flex !important;
        flex-direction: column !important;
        align-items: center;
        justify-content: center;
    }

    .design-widget-button-modal-btn-icon-only {
        align-items: center !important;
        justify-content: center !important;
        padding: 8px !important;
    }

    .design-widget-button-modal-preview-section {
        padding: 16px 0;
        display: flex;
        justify-content: center;
        min-height: 80px;
    }

    .design-widget-button-modal-section {
        margin-bottom: 16px;
    }

    .design-widget-button-modal-section-title {
        font-weight: bold;
        font-size: 15px;
        margin-bottom: 12px;
        display: flex;
        align-items: center;
        gap: 6px;
    }

    .design-widget-button-modal-option-row {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 20px;
    }

    .design-widget-button-modal-option-group {
        display: flex;
        align-items: center;
        gap: 8px;
    }

    .design-widget-button-modal-label {
        font-size: 14px;
        white-space: nowrap;
    }

    .design-widget-button-modal-select {
        width: auto;
        min-width: 70px;
    }

    /* 라벨 위 / 인풋 아래 세로 배치 */
    .design-widget-button-modal-field-row {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-end;
        gap: 12px;
    }

    .design-widget-button-modal-field-group {
        display: flex;
        flex-direction: column;
        gap: 4px;
    }

    .design-widget-button-modal-radio-label {
        display: flex;
        align-items: center;
        gap: 4px;
        font-weight: normal;
        margin: 0;
        cursor: pointer;
    }

    .design-widget-button-modal-file-row {
        display: inline-flex;
        align-items: center;
        gap: 8px;
    }

    .design-widget-button-modal-file-name {
        width: 320px;
    }

    .design-widget-button-modal-file-clear {
        border: none;
        background: none;
        cursor: pointer;
        color: #888;
        font-size: 16px;
        padding: 0 2px;
    }

    .design-widget-button-modal-help-text {
        margin: 0;
        font-size: 12px;
        color: #777;
        line-height: 1.5;
    }

    .design-widget-button-modal-help-row {
        margin-top: 6px;
    }

    /* 아이콘 미리보기 */
    .design-widget-button-modal-icon-preview {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 32px;
        height: 32px;
        border: 1px solid #ddd;
        border-radius: 4px;
        background: #fafafa;
    }

    /* 스테퍼 */
    .design-widget-button-modal-number-input-wrapper {
        position: relative;
        display: inline-flex;
        align-items: center;
    }

    .design-widget-button-modal-number-input-wrapper input {
        padding-right: 20px !important;
    }

    .design-widget-button-modal-number-stepper {
        position: absolute;
        right: 2px;
        top: 50%;
        transform: translateY(-50%);
        display: flex;
        flex-direction: column;
        gap: 1px;
    }

    .design-widget-button-modal-stepper-up,
    .design-widget-button-modal-stepper-down {
        background: none;
        border: none;
        padding: 0 2px;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        height: 10px;
        line-height: 1;
    }

    .design-widget-button-modal-stepper-up:hover svg path,
    .design-widget-button-modal-stepper-down:hover svg path {
        fill: #e64d11;
    }

    /* 아이콘 인풋 */
    .design-widget-button-modal-with-icon {
        position: relative;
    }

    .design-widget-button-modal-with-icon input {
        padding-left: 24px !important;
    }

    .design-widget-button-modal-input-icon-left {
        position: absolute;
        left: 6px;
        width: 14px;
        height: 14px;
        pointer-events: none;
        z-index: 1;
    }
</style>
<script type="text/x-template" id="design-widget-button-template">
    <div v-if="load" class="design-widget-button" :style="wrapperStyle">
        <button
                v-if="row.widget_code"
                type="button"
                class="design-widget-button-btn"
                :class="[hoverClass, {
                    'design-widget-button-btn-vertical': !isIconOnlyButton && (row.icon_position === '위' || row.icon_position === '아래'),
                    'design-widget-button-btn-icon-only': isIconOnlyButton,
                }]"
                :style="buttonStyle"
                @click.prevent.stop="handleClick"
                @mouseenter="hover = true"
                @mouseleave="hover = false"
        >
            <!-- 아이콘 전용 -->
            <template v-if="isIconOnlyButton">
                <span v-if="parsedIcon.library === 'feather'" v-html="renderIconHtml(parsedIcon.name, row.icon_size, row.icon_color)" style="display:inline-flex;align-items:center;justify-content:center;"></span>
                <i v-else :class="iconClass" :style="iconStyle('')"></i>
            </template>

            <!-- 아이콘 위 -->
            <template v-if="!isIconOnlyButton && parsedIcon && row.icon_position === '위'">
                <span v-if="parsedIcon.library === 'feather'" v-html="renderIconHtml(parsedIcon.name, row.icon_size, row.icon_color)" style="display:inline-flex;align-items:center;margin-bottom:4px;"></span>
                <i v-else :class="iconClass" :style="iconStyle('margin-bottom:4px')"></i>
            </template>

            <!-- 아이콘 왼쪽 -->
            <template v-if="!isIconOnlyButton && parsedIcon && row.icon_position === '왼쪽'">
                <span v-if="parsedIcon.library === 'feather'" v-html="renderIconHtml(parsedIcon.name, row.icon_size, row.icon_color)" style="display:inline-flex;align-items:center;margin-right:4px;"></span>
                <i v-else :class="iconClass" :style="iconStyle('margin-right:4px')"></i>
            </template>

            <span v-if="hasLabelText" class="design-widget-button-label">{{ displayLabel }}</span>

            <!-- 아이콘 오른쪽 -->
            <template v-if="!isIconOnlyButton && parsedIcon && row.icon_position === '오른쪽'">
                <span v-if="parsedIcon.library === 'feather'" v-html="renderIconHtml(parsedIcon.name, row.icon_size, row.icon_color)" style="display:inline-flex;align-items:center;margin-left:4px;"></span>
                <i v-else :class="iconClass" :style="iconStyle('margin-left:4px')"></i>
            </template>

            <!-- 아이콘 아래 -->
            <template v-if="!isIconOnlyButton && parsedIcon && row.icon_position === '아래'">
                <span v-if="parsedIcon.library === 'feather'" v-html="renderIconHtml(parsedIcon.name, row.icon_size, row.icon_color)" style="display:inline-flex;align-items:center;margin-top:4px;"></span>
                <i v-else :class="iconClass" :style="iconStyle('margin-top:4px')"></i>
            </template>
        </button>
    </div>
    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name: "design-widget-button", object: {
            template: "#design-widget-button-template",
            props: {
                primary    : {type: String, default: ""},
                widget_code: {type: String, default: ""},
            },
            data() {
                const defaultRow = {
                    widget_code             : this.widget_code,
                    label                   : 'BUTTON',
                    font                    : 'Pretendard',
                    font_size               : 14,
                    bg_color                : '#ffffff',
                    font_color              : '#000000',
                    icon_src                : '',
                    icon_color              : '#000000',
                    icon_position           : '왼쪽',
                    icon_size               : 16,
                    border_use              : true,
                    border_color            : '#000000',
                    border_width            : 1,
                    border_radius_use       : false,
                    border_radius_px        : 6,
                    box_shadow_use          : false,
                    box_shadow_opacity      : 20,
                    mouse_click_type        : 'URL',
                    mouse_click_url         : '',
                    mouse_click_target      : '_self',
                    hover_effect            : '사용안함',
                    hover_bg_color          : '#ffffff',
                    hover_font_color        : '#000000',
                    hover_border_use        : false,
                    hover_border_color      : '#000000',
                    hover_border_width      : 1,
                    hover_box_shadow_use    : false,
                    hover_box_shadow_opacity: 20,
                    hover_transition        : '없음',
                    layout_width            : '자동',
                    layout_width_px         : 150,
                    layout_height           : '자동',
                    layout_height_px        : 40,
                    layout_align            : '중앙',
                };

                return {
                    load          : false,
                    component_name: "design-widget-button",
                    component_idx : "",
                    injectUrls    : [
                        "https://cdnjs.cloudflare.com/ajax/libs/hover.css/2.3.1/css/hover-min.css",
                        "https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css",
                        "https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/style.css",
                        "https://cdnjs.cloudflare.com/ajax/libs/simple-line-icons/2.5.5/css/simple-line-icons.min.css",
                        "https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js",
                    ],

                    table: null,
                    fontFamilyMap: {"Galmuri14":"Galmuri14","GamjaFlower":"GamjaFlower","KNUTruth":"KNUTruth","GowunDodum":"GowunDodum","GowunBatang":"GowunBatang","Gulim":"Gulim","GulimChe":"GulimChe","Gungsuh":"Gungsuh","GungsuhChe":"GungsuhChe","Malgun Gothic":"Malgun Gothic","NanumGothic":"NanumGothic","NanumMyeongjo":"NanumMyeongjo","NanumBarunGothic":"NanumBarunGothic","NanumBarunpen":"NanumBarunpen","NanumSquare":"NanumSquare","NanumSquareRound":"NanumSquareRound","NanumPenScript":"NanumPenScript","Dokdo":"Dokdo","Dotum":"Dotum","DotumChe":"DotumChe","Dongle":"Dongle","EastSeaDokdo":"EastSeaDokdo","Batang":"Batang","BatangChe":"BatangChe","New Gulim":"New Gulim","Sogang_university":"Sogang_University","SeolleimCool":"SeolleimCool","SBAggro":"SBAggro","PyeongChang":"PyeongChang","PyeongChangPeace":"PyeongChangPeace","Pretendard":"Pretendard","Skybori":"Skybori","HiMelody":"HiMelody","휴먼엽서체":"휴먼엽서체","Arial":"Arial","Arial Black":"Arial Black","BlackHanSans":"BlackHanSans","Comic Sans MS":"Comic Sans MS","Courier New":"Courier New","CuteFont":"CuteFont","GmarketSans":"GmarketSans","IBMPlexSansKR":"IBMPlexSansKR","SongMyung":"SongMyung","NotoSans-Regular":"NotoSans-Regular","Orbit":"Orbit","PoorStory":"PoorStory","SingleDay":"SingleDay","Tahoma":"Tahoma","Times New Roman":"Times New Roman","Verdana":"Verdana"},
                    fontAliasMap : {"Noto Sans KR":"NotoSans-Regular","Nanum Gothic":"NanumGothic","Nanum Myeongjo":"NanumMyeongjo","맑은 고딕":"Malgun Gothic","궁서":"Gungsuh","굴림체":"GulimChe","돋음체":"DotumChe","돋움체":"DotumChe","바탕체":"BatangChe"},
                    defaultRow,
                    row  : Object.assign({}, defaultRow),
                    hover: false,
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name;
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_button");
                this.table.where('widget_code', this.widget_code);
                await this.getRow();

                this.load = true;
                this.$nextTick(() => {
                });
            },
            methods: {
                async getRow() {
                    let row = await this.table.get();
                    if (row.length > 0) this.row = Object.assign({}, this.defaultRow, row[0]);
                    this.row.font = this.normalizeFontValue(this.row.font);
                    this.row.hover_transition = this.normalizeHoverTransition(this.row.hover_transition);
                },
                async handleClick() {
                    const type = this.row.mouse_click_type || 'URL';
                    const value = (this.row.mouse_click_url || '').trim();

                    if (!value) return;

                    if (type === '섹션 스크롤') {
                        const selector = value.startsWith('#') ? value : `#${value}`;
                        const target = document.querySelector(selector);
                        if (target && typeof target.scrollIntoView === 'function') {
                            target.scrollIntoView({behavior: 'smooth', block: 'start'});
                        }
                        return;
                    }

                    if (type === '전화걸기') {
                        const tel = value.startsWith('tel:') ? value : `tel:${value.replace(/-/g, '')}`;
                        window.location.href = tel;
                        return;
                    }

                    if (type === '메일 보내기') {
                        await this.openMailApp(value);
                        return;
                    }

                    if (type === '파일 다운로드') {
                        await this.lib.download({
                            src : value,
                            name: this.downloadFileName,
                        });
                        return;
                    }

                    this.lib.href(value, this.linkTarget);
                },
                extractFileName(src) {
                    try {
                        return decodeURIComponent((src || '').split('/').pop() || 'download');
                    } catch (e) {
                        return (src || '').split('/').pop() || 'download';
                    }
                },
                async openMailApp(email) {
                    const emailAddress = email.replace(/^mailto:/i, '').trim();
                    const mailtoUrl = `mailto:${emailAddress}`;
                    let appOpened = false;

                    const onVisibilityChange = () => {
                        if (document.hidden) appOpened = true;
                    };
                    const onBlur = () => {
                        appOpened = true;
                    };

                    document.addEventListener('visibilitychange', onVisibilityChange);
                    window.addEventListener('blur', onBlur);

                    window.location.href = mailtoUrl;

                    await new Promise(resolve => setTimeout(resolve, 1200));

                    document.removeEventListener('visibilitychange', onVisibilityChange);
                    window.removeEventListener('blur', onBlur);

                    if (appOpened) return;

                    const copied = await this.copyToClipboard(emailAddress);
                    if (copied) {
                        alert(`기본 메일 앱을 실행할 수 없어 이메일 주소를 복사했습니다.\n${emailAddress}`);
                        return;
                    }

                    alert(`기본 메일 앱을 실행할 수 없습니다.\n이메일 주소를 직접 복사해주세요.\n${emailAddress}`);
                },
                async copyToClipboard(text) {
                    try {
                        if (navigator.clipboard && window.isSecureContext) {
                            await navigator.clipboard.writeText(text);
                            return true;
                        }
                    } catch (e) {
                    }

                    const textarea = document.createElement('textarea');
                    textarea.value = text;
                    textarea.setAttribute('readonly', '');
                    textarea.style.position = 'fixed';
                    textarea.style.left = '-9999px';
                    document.body.appendChild(textarea);
                    textarea.select();

                    let copied = false;
                    try {
                        copied = document.execCommand('copy');
                    } catch (e) {
                        copied = false;
                    }

                    document.body.removeChild(textarea);
                    return copied;
                },
                renderIconHtml(name, size, color) {
                    if (typeof feather !== 'undefined' && feather.icons[name]) {
                        return feather.icons[name].toSvg({width: size, height: size, color});
                    }
                    return '';
                },
                iconStyle(extra) {
                    return `color:${this.row.icon_color};font-size:${this.row.icon_size}px;${extra}`;
                },
                normalizeFontValue(font) {
                    return this.fontAliasMap[font] || font || 'Pretendard';
                },
                getFontFamily(font) {
                    const normalized = this.normalizeFontValue(font);
                    const family = this.fontFamilyMap[normalized] || normalized || 'Pretendard';
                    return "'" + String(family).replace(/'/g, "\\'") + "', sans-serif";
                },
                normalizeHoverTransition(transition) {
                    return transition === '슬라이드' ? '없음' : (transition || '없음');
                },
            },
            computed: {
                appState() {
                    return window.JAYDREAM_VUE_GLOBAL[this.$app_name];
                },
                hasLabelText() {
                    return String(this.row.label || '').trim().length > 0;
                },
                isIconOnlyButton() {
                    return !!this.parsedIcon && !this.hasLabelText;
                },
                displayLabel() {
                    return this.hasLabelText ? this.row.label : '';
                },
                downloadFileName() {
                    if (this.row.mouse_click_target && this.row.mouse_click_target !== '_self' && this.row.mouse_click_target !== '_blank') {
                        return this.row.mouse_click_target;
                    }
                    return this.extractFileName(this.row.mouse_click_url);
                },
                linkTarget() {
                    return this.row.mouse_click_target === '_blank' ? '_blank' : '_self';
                },
                parsedIcon() {
                    if (!this.row.icon_src) return null;
                    const parts = this.row.icon_src.split(':');
                    if (parts.length < 2) return null;
                    return {library: parts[0], name: parts[1]};
                },
                iconClass() {
                    if (!this.parsedIcon) return '';
                    const {library, name} = this.parsedIcon;
                    if (library === 'bootstrap')  return `bi bi-${name}`;
                    if (library === 'phosphor')   return `ph ph-${name}`;
                    if (library === 'simpleline') return `icon-${name}`;
                    return '';
                },
                hoverClass() {
                    const classMap = {
                        '페이드'  : 'hvr-fade',
                        '줌인'   : 'hvr-grow',
                        '줌아웃'  : 'hvr-sink',
                        '떠오르기': 'hvr-float',
                    };
                    return this.row.hover_transition && this.row.hover_transition !== '없음'
                        ? (classMap[this.row.hover_transition] || '') : '';
                },
                wrapperStyle() {
                    const align = this.row.layout_align;
                    return {
                        display       : 'flex',
                        justifyContent: align === '좌측' ? 'flex-start' : align === '우측' ? 'flex-end' : 'center',
                    };
                },
                buttonStyle() {
                    const base = {
                        fontFamily    : this.getFontFamily(this.row.font),
                        fontSize      : (this.row.font_size || 14) + 'px',
                        backgroundColor: this.row.bg_color || '',
                        color         : this.row.font_color || '',
                        border        : this.row.border_use ? `${this.row.border_width}px solid ${this.row.border_color}` : 'none',
                        borderRadius  : this.row.border_radius_use ? `${this.row.border_radius_px}px` : '0px',
                        boxShadow     : this.row.box_shadow_use ? `0 4px 12px rgba(0,0,0,${this.row.box_shadow_opacity / 100})` : '',
                        width         : this.row.layout_width === '직접입력' && this.row.layout_width_px ? this.row.layout_width_px + 'px' : 'auto',
                        height        : this.row.layout_height === '직접입력' && this.row.layout_height_px ? this.row.layout_height_px + 'px' : 'auto',
                        cursor        : this.row.mouse_click_url ? 'pointer' : 'default',
                        display       : 'inline-flex',
                        alignItems    : 'center',
                        justifyContent: 'center',
                        transition    : 'all 0.2s',
                    };

                    if (this.hover && this.row.hover_effect === '사용함') {
                        return {
                            ...base,
                            backgroundColor: this.row.hover_bg_color || base.backgroundColor,
                            color          : this.row.hover_font_color || base.color,
                            border         : this.row.hover_border_use ? `${this.row.hover_border_width}px solid ${this.row.hover_border_color}` : base.border,
                            boxShadow      : this.row.hover_box_shadow_use ? `0 4px 12px rgba(0,0,0,${this.row.hover_box_shadow_opacity / 100})` : base.boxShadow,
                        };
                    }

                    return base;
                },
            },
            watch: {
                appState: {
                    async handler(newVal) {
                        if (newVal.reload) {
                            await this.getRow();
                            this.appState.reload = false;
                        }
                    },
                    deep: true,
                },
            },
        }});
</script>

<style>
    .design-widget-button-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        padding: 8px 16px;
        box-sizing: border-box;
        text-decoration: none;
        outline: none;
    }

    .design-widget-button-btn:focus {
        outline: none;
    }

    .design-widget-button-btn-vertical {
        display: inline-flex !important;
        flex-direction: column !important;
        align-items: center !important;
        justify-content: center !important;
    }

    .design-widget-button-btn-icon-only {
        align-items: center !important;
        justify-content: center !important;
        padding: 8px !important;
    }
</style>
<script type="text/x-template" id="design-widget-blank-modal-template">
    <div v-if="load" class="design-widget-blank-modal">
        <div class="design-widget-blank-modal-section">
            <div class="design-widget-blank-modal-section-title">
                <img src="/file/icon/image_margin.svg" style="height: 18px;">
                <span>여백 설정</span>
            </div>

            <div class="design-widget-blank-modal-fields">
                <div class="design-widget-blank-modal-field">
                    <div class="design-widget-blank-modal-field-title">여백 높이</div>
                    <div class="design-widget-blank-modal-field-body">
                        <div class="design-widget-blank-modal-number-input">
                            <input type="text" class="form-control form-control-sm" v-model="row.pc_height" v-number>
                            <div class="design-widget-blank-modal-number-stepper">
                                <button type="button" class="design-widget-blank-modal-stepper-btn" @click="step('pc_height', 1)">
                                    <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 0L10 6H0L5 0Z" fill="#b3b3b3"/></svg>
                                </button>
                                <button type="button" class="design-widget-blank-modal-stepper-btn" @click="step('pc_height', -1)">
                                    <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 6L0 0H10L5 6Z" fill="#b3b3b3"/></svg>
                                </button>
                            </div>
                        </div>
                        <span class="design-widget-blank-modal-unit">px</span>
                    </div>
                </div>

                <div class="design-widget-blank-modal-field design-widget-blank-modal-field--mobile">
                    <div class="design-widget-blank-modal-field-title">모바일 설정</div>
                    <div class="design-widget-blank-modal-mobile-options">
                        <label class="design-widget-blank-modal-radio-option">
                            <input type="radio" value="auto" v-model="row.mobile_height_type">
                            <span class="design-widget-blank-modal-radio-mark"></span>
                            <span class="design-widget-blank-modal-radio-label">자동</span>
                        </label>

                        <div class="design-widget-blank-modal-custom-option">
                            <label class="design-widget-blank-modal-radio-option">
                                <input type="radio" value="custom" v-model="row.mobile_height_type">
                                <span class="design-widget-blank-modal-radio-mark"></span>
                                <span class="design-widget-blank-modal-radio-label">개별 높이 설정</span>
                            </label>

                            <div class="design-widget-blank-modal-number-input" :class="{'design-widget-blank-modal-number-input--disabled': row.mobile_height_type !== 'custom'}">
                                <input type="text" class="form-control form-control-sm" v-model="row.mobile_height" v-number :disabled="row.mobile_height_type !== 'custom'">
                                <div class="design-widget-blank-modal-number-stepper">
                                    <button type="button" class="design-widget-blank-modal-stepper-btn" @click="step('mobile_height', 1)" :disabled="row.mobile_height_type !== 'custom'">
                                        <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 0L10 6H0L5 0Z" fill="#b3b3b3"/></svg>
                                    </button>
                                    <button type="button" class="design-widget-blank-modal-stepper-btn" @click="step('mobile_height', -1)" :disabled="row.mobile_height_type !== 'custom'">
                                        <svg width="10" height="6" viewBox="0 0 10 6"><path d="M5 6L0 0H10L5 6Z" fill="#b3b3b3"/></svg>
                                    </button>
                                </div>
                            </div>
                            <span class="design-widget-blank-modal-unit">px</span>
                        </div>

                        <label class="design-widget-blank-modal-radio-option">
                            <input type="radio" value="hide" v-model="row.mobile_height_type">
                            <span class="design-widget-blank-modal-radio-mark"></span>
                            <span class="design-widget-blank-modal-radio-label">모바일에서 감추기</span>
                        </label>
                    </div>
                </div>
            </div>
        </div>

        <div class="modal-footer" style="position:sticky;right:0;bottom:0;">
            <button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
            <button type="button" class="btn btn-orange" @click="save()">저장</button>
        </div>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name: "design-widget-blank-modal", object: {
            template: "#design-widget-blank-modal-template",
            props: {
                primary    : {type: String, default: ""},
                widget_code: {type: String, default: ""},
                site_code  : {type: String, default: ""},
            },
            data() {
                return {
                    load          : false,
                    component_name: "design-widget-blank-modal",
                    component_idx : "",
                    table         : null,
                    row           : {
                        widget_code        : this.widget_code,
                        pc_height          : 75,
                        mobile_height_type : "auto",
                        mobile_height      : 75,
                    },
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name;
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_blank");
                this.table.where("widget_code", this.widget_code);
                await this.getRow();
                this.load = true;
                window.addEventListener("design-widget-blank-height-change", this.onExternalHeightChange);
            },
            beforeUnmount() {
                window.removeEventListener("design-widget-blank-height-change", this.onExternalHeightChange);
            },
            methods: {
                getDefaultRow() {
                    return {
                        widget_code        : this.widget_code,
                        pc_height          : 75,
                        mobile_height_type : "auto",
                        mobile_height      : 75,
                    };
                },
                async getRow() {
                    const rows = await this.table.get();
                    this.row = Object.assign(this.getDefaultRow(), rows.length > 0 ? rows[0] : {});
                    this.normalizeRow();
                },
                normalizeRow() {
                    this.row.pc_height = Math.max(0, parseInt(this.row.pc_height, 10) || 0);
                    this.row.mobile_height = Math.max(0, parseInt(this.row.mobile_height, 10) || this.row.pc_height);

                    if (!["auto", "custom", "hide"].includes(this.row.mobile_height_type)) {
                        this.row.mobile_height_type = "auto";
                    }
                },
                onExternalHeightChange(event) {
                    const detail = event.detail || {};
                    if (detail.widget_code !== this.widget_code) return;

                    if (detail.row) {
                        this.row = Object.assign(this.getDefaultRow(), this.row, detail.row);
                        this.normalizeRow();
                        return;
                    }

                    this.row.pc_height = Math.max(0, parseInt(detail.pc_height, 10) || 0);
                },
                step(field, amount) {
                    if (field === "mobile_height" && this.row.mobile_height_type !== "custom") {
                        return;
                    }

                    const current = Math.max(0, parseInt(this.row[field], 10) || 0);
                    this.row[field] = Math.max(0, current + amount);
                },
                async save() {
                    this.normalizeRow();

                    window.design.loadingOn();
                    await this.table.post(this.row, {return: true});
                    window.design.loadingOff();

                    if (window.JAYDREAM_VUE_GLOBAL[this.widget_code]) {
                        window.JAYDREAM_VUE_GLOBAL[this.widget_code].reload = true;
                    }

                    if (window.design && window.design.menu && window.design.menu.grid) {
                        window.design.menu.grid.reset();
                    }

                    $(this.$el).closest(".modal").modal("hide");
                },
            },
            computed: {},
        }});
</script>

<style>
    .design-widget-blank-modal {
        padding-top: 2px;
    }

    .design-widget-blank-modal-section {
        padding: 6px 0 0;
    }

    .design-widget-blank-modal-section-title {
        display: flex;
        align-items: center;
        gap: 6px;
        margin-bottom: 20px;
        font-size: 15px;
        font-weight: 700;
        color: #2f2f2f;
    }

    .design-widget-blank-modal-fields {
        display: grid;
        grid-template-columns: 120px minmax(0, 1fr);
        gap: 22px 30px;
        align-items: start;
    }

    .design-widget-blank-modal-field {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 12px;
    }

    .design-widget-blank-modal-field--mobile {
        min-width: 0;
    }

    .design-widget-blank-modal-field-title {
        font-size: 14px;
        line-height: 1.2;
        color: #2f2f2f;
        margin: 0;
        font-weight: 600;
    }

    .design-widget-blank-modal-field-body,
    .design-widget-blank-modal-mobile-options,
    .design-widget-blank-modal-custom-option {
        display: flex;
        align-items: center;
        gap: 8px;
        flex-wrap: wrap;
    }

    .design-widget-blank-modal-radio-option {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        cursor: pointer;
        font-weight: 400;
        margin: 0;
        position: relative;
    }

    .design-widget-blank-modal-radio-option input {
        position: absolute;
        opacity: 0;
        pointer-events: none;
    }

    .design-widget-blank-modal-radio-mark {
        width: 16px;
        height: 16px;
        border-radius: 50%;
        border: 1px solid #b9b9b9;
        background: #fff;
        position: relative;
        flex: 0 0 auto;
        transition: border-color .15s ease;
    }

    .design-widget-blank-modal-radio-mark::after {
        content: "";
        position: absolute;
        inset: 3px;
        border-radius: 50%;
        background: #ff5f37;
        opacity: 0;
        transform: scale(.6);
        transition: opacity .15s ease, transform .15s ease;
    }

    .design-widget-blank-modal-radio-option input:checked + .design-widget-blank-modal-radio-mark {
        border-color: #ff5f37;
    }

    .design-widget-blank-modal-radio-option input:checked + .design-widget-blank-modal-radio-mark::after {
        opacity: 1;
        transform: scale(1);
    }

    .design-widget-blank-modal-radio-label,
    .design-widget-blank-modal-unit {
        font-size: 14px;
        line-height: 1.2;
        color: #2f2f2f;
        margin: 0;
        font-weight: 400;
        white-space: nowrap;
    }

    .design-widget-blank-modal-number-input {
        position: relative;
        width: 92px;
    }

    .design-widget-blank-modal-number-input input {
        width: 100%;
        height: 34px;
        border-radius: 8px;
        border: 1px solid #d8d8d8;
        box-shadow: none;
        padding: 0 22px 0 10px;
        font-size: 14px;
        color: #2f2f2f;
        text-align: center;
        font-weight: 400;
    }

    .design-widget-blank-modal-number-input input:focus {
        border-color: #ff5f37;
        box-shadow: none;
    }

    .design-widget-blank-modal-number-input--disabled input {
        background: #f8f8f8;
        color: #999;
    }

    .design-widget-blank-modal-number-stepper {
        position: absolute;
        top: 50%;
        right: 6px;
        transform: translateY(-50%);
        display: flex;
        flex-direction: column;
        gap: 1px;
    }

    .design-widget-blank-modal-stepper-btn {
        border: none;
        background: none;
        padding: 0;
        width: 10px;
        height: 9px;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
    }

    .design-widget-blank-modal-stepper-btn:disabled {
        cursor: default;
        opacity: 0.4;
    }

    .design-widget-blank-modal .modal-footer {
        margin: 24px 0 0;
        padding: 16px 0 0;
        border-top: 1px solid #e3e3e3;
    }

    @media (max-width: 991px) {
        .design-widget-blank-modal-section {
            padding: 6px 0 0;
        }

        .design-widget-blank-modal-fields {
            grid-template-columns: 1fr;
            gap: 18px;
        }

        .design-widget-blank-modal .modal-footer {
            margin: 20px 0 0;
            padding: 16px 0 0;
        }
    }
</style>
<script type="text/x-template" id="design-widget-blank-template">
    <div
        v-if="load"
        class="design-widget-blank"
        :class="[mobileClass, {'design-widget-blank--editable': resizeEnabled, 'design-widget-blank--dragging': resizing}]"
        :style="blankStyle"
    >
        <div class="design-widget-blank-spacer">
            <button
                v-if="resizeEnabled"
                type="button"
                class="design-widget-blank-resize-handle"
                title="드래그하여 여백 높이 조절"
                aria-label="여백 높이 조절"
                @mousedown.stop.prevent="startResize"
                @touchstart.stop.prevent="startResize"
            ></button>
        </div>
    </div>

    <div v-if="!load"><div class="loader"></div></div>
</script>

<script>
    JayDream_components.push({name: "design-widget-blank", object: {
            template: "#design-widget-blank-template",
            props: {
                primary    : {type: String, default: ""},
                widget_code: {type: String, default: ""},
                site_code  : {type: String, default: ""},
            },
            data() {
                return {
                    load          : false,
                    component_name: "design-widget-blank",
                    component_idx : "",
                    table         : null,
                    resizing      : false,
                    deviceMode    : "desktop",
                    savingResize  : false,
                    mobilePcSectionLocked: false,
                    row           : {
                        widget_code        : this.widget_code,
                        pc_height          : 75,
                        mobile_height_type : "auto",
                        mobile_height      : 75,
                    },
                };
            },
            async created() {
                this.component_idx = this.lib.generateUniqueId();
                this.api.component_name = this.component_name;
                this._resizeStartY = 0;
                this._resizeStartHeight = 75;
                this._resizeField = "pc_height";
                this._bodyClassObserver = null;
                this._resizeDraggable = null;
            },
            async mounted() {
                this.table = await this.api.table("jd_widget_blank");
                this.table.where("widget_code", this.widget_code);

                await this.getRow();

                this.load = true;
                this.$nextTick(() => {
                    this.syncDeviceMode();
                    this.syncMobilePcSectionLock();
                    this.syncFirewallSettingButton();
                    this.bindBodyClassObserver();
                });
            },
            beforeUnmount() {
                this.unbindResizeEvents();

                if (this._bodyClassObserver) {
                    this._bodyClassObserver.disconnect();
                    this._bodyClassObserver = null;
                }
            },
            methods: {
                getDefaultRow() {
                    return {
                        widget_code        : this.widget_code,
                        pc_height          : 75,
                        mobile_height_type : "auto",
                        mobile_height      : 75,
                    };
                },
                async getRow() {
                    const rows = await this.table.get();
                    this.row = Object.assign(this.getDefaultRow(), rows.length > 0 ? rows[0] : {});
                },
                syncDeviceMode() {
                    this.deviceMode = document.body.classList.contains("_mbv") ? "mobile" : "desktop";
                },
                bindBodyClassObserver() {
                    if (this._bodyClassObserver || typeof MutationObserver === "undefined") return;

                    this._bodyClassObserver = new MutationObserver(() => {
                        this.syncDeviceMode();
                        this.syncMobilePcSectionLock();
                        this.syncFirewallSettingButton();
                    });
                    this._bodyClassObserver.observe(document.body, {
                        attributes: true,
                        attributeFilter: ["class"],
                    });
                },
                getCookie(name) {
                    const cookies = (`; ${document.cookie}`).split(`; ${name}=`);
                    return cookies.length === 2 ? cookies.pop().split(";").shift() : "";
                },
                isMobileDesignMode() {
                    return document.body.classList.contains("_mbv") || this.getCookie("lc_device") === "mobile";
                },
                isMobilePcSection() {
                    const widget = document.getElementById(this.widget_code);
                    const section = widget && widget.closest ? widget.closest("section") : null;
                    return this.isMobileDesignMode() && !!section && !section.classList.contains("from-mobile");
                },
                alertMobilePcSectionBlocked() {
                    const message = "PC에서 만든 섹션 또는 위젯은 모바일 디자인모드에서 설정할 수 없습니다.";
                    if (window.jQuery && jQuery.alert) {
                        jQuery.alert(message);
                    } else {
                        alert(message);
                    }
                },
                syncMobilePcSectionLock() {
                    this.mobilePcSectionLocked = this.isMobilePcSection();
                    if (this.mobilePcSectionLocked && this.resizing) {
                        this.resizing = false;
                        this.unbindResizeEvents();
                    }
                },
                syncFirewallSettingButton() {
                    const widget = document.getElementById(this.widget_code);
                    const firewall = widget ? widget.querySelector(".firewall") : null;
                    if (!firewall) return;

                    if (this.mobilePcSectionLocked) {
                        firewall.classList.remove("widget_setting_btn");
                        return;
                    }

                    if (!this.row.idx) {
                        firewall.classList.add("widget_setting_btn");
                    }
                },
                getPcHeight() {
                    return Math.max(0, parseInt(this.row.pc_height, 10) || 0);
                },
                getMobileHeight() {
                    return Math.max(0, parseInt(this.row.mobile_height, 10) || this.getPcHeight());
                },
                getResizeField() {
                    return this.deviceMode === "mobile" ? "mobile_height" : "pc_height";
                },
                getResizeHeight() {
                    return this.getResizeField() === "mobile_height" ? this.getMobileHeight() : this.getPcHeight();
                },
                prepareResizeTarget() {
                    if (this.deviceMode !== "mobile") return;

                    const currentHeight = this.row.mobile_height_type === "custom" ? this.getMobileHeight() : this.getPcHeight();

                    if (this.row.mobile_height_type !== "custom") {
                        this.row.mobile_height_type = "custom";
                    }

                    this.row.mobile_height = this.normalizeResizeHeight(currentHeight);
                },
                getResizePoint(event) {
                    const source = event.touches && event.touches.length ? event.touches[0] : event;
                    return {
                        y: source.clientY,
                    };
                },
                normalizeResizeHeight(value) {
                    return Math.max(1, parseInt(value, 10) || 1);
                },
                startResize(event) {
                    this.syncDeviceMode();
                    this.syncMobilePcSectionLock();
                    if (this.mobilePcSectionLocked) {
                        this.alertMobilePcSectionBlocked();
                        return;
                    }
                    if (!this.resizeEnabled) return;

                    this.prepareResizeTarget();

                    const point = this.getResizePoint(event);
                    this._resizeField = this.getResizeField();
                    this._resizeStartY = point.y;
                    this._resizeStartHeight = this.normalizeResizeHeight(this.getResizeHeight());
                    this.resizing = true;

                    document.body.classList.add("design-widget-blank-resizing");
                    this.disableWidgetDrag();
                    this.bindResizeEvents();
                },
                bindResizeEvents() {
                    document.addEventListener("mousemove", this.onResizeMove);
                    document.addEventListener("mouseup", this.stopResize);
                    document.addEventListener("touchmove", this.onResizeMove, {passive: false});
                    document.addEventListener("touchend", this.stopResize);
                    document.addEventListener("touchcancel", this.stopResize);
                },
                unbindResizeEvents() {
                    document.removeEventListener("mousemove", this.onResizeMove);
                    document.removeEventListener("mouseup", this.stopResize);
                    document.removeEventListener("touchmove", this.onResizeMove);
                    document.removeEventListener("touchend", this.stopResize);
                    document.removeEventListener("touchcancel", this.stopResize);
                    document.body.classList.remove("design-widget-blank-resizing");
                    this.enableWidgetDrag();
                },
                onResizeMove(event) {
                    if (!this.resizing) return;

                    if (event.cancelable) event.preventDefault();

                    const point = this.getResizePoint(event);
                    const nextHeight = this.normalizeResizeHeight(this._resizeStartHeight + point.y - this._resizeStartY);
                    this.row[this._resizeField] = nextHeight;
                    this.emitHeightChange();
                },
                async stopResize() {
                    if (!this.resizing) return;

                    this.resizing = false;
                    this.unbindResizeEvents();
                    await this.saveResizeHeight();
                },
                async saveResizeHeight() {
                    if (this.savingResize) return;

                    this.savingResize = true;
                    const resizeField = this._resizeField || this.getResizeField();

                    if (resizeField === "mobile_height") {
                        this.row.mobile_height_type = "custom";
                    }

                    this.row[resizeField] = this.normalizeResizeHeight(this.row[resizeField]);

                    try {
                        await this.table.post(this.row, {return: true});
                        await this.getRow();
                        this.emitHeightChange();
                        this.resetGrid();
                    } finally {
                        this.savingResize = false;
                    }
                },
                emitHeightChange() {
                    if (typeof window === "undefined" || typeof window.CustomEvent !== "function") return;

                    window.dispatchEvent(new CustomEvent("design-widget-blank-height-change", {
                        detail: {
                            widget_code: this.widget_code,
                            pc_height: this.getPcHeight(),
                            mobile_height: this.getMobileHeight(),
                            mobile_height_type: this.row.mobile_height_type,
                            resize_field: this._resizeField || this.getResizeField(),
                            row: Object.assign({}, this.row),
                        },
                    }));
                },
                disableWidgetDrag() {
                    const widget = document.getElementById(this.widget_code);
                    if (!widget || !window.jQuery) return;

                    const $widget = window.jQuery(widget);
                    if (!$widget.data("ui-draggable")) return;

                    this._resizeDraggable = $widget;
                    $widget.draggable("disable");
                },
                enableWidgetDrag() {
                    if (!this._resizeDraggable || !this._resizeDraggable.data("ui-draggable")) return;

                    this._resizeDraggable.draggable("enable");
                    this._resizeDraggable = null;
                },
                resetGrid() {
                    if (window.design && window.design.menu && window.design.menu.grid) {
                        window.design.menu.grid.reset();
                    }
                },
            },
            computed: {
                blankStyle() {
                    const pcHeight = Math.max(0, parseInt(this.row.pc_height, 10) || 0);
                    const mobileHeight = Math.max(0, parseInt(this.row.mobile_height, 10) || pcHeight);

                    return {
                        "--design-widget-blank-pc-height"    : `${pcHeight}px`,
                        "--design-widget-blank-mobile-height": `${mobileHeight}px`,
                    };
                },
                mobileClass() {
                    const type = this.row.mobile_height_type || "auto";
                    return `design-widget-blank--mobile-${type}`;
                },
                isDesignMode() {
                    return typeof window !== "undefined" && !!(window.design && window.design.menu);
                },
                resizeEnabled() {
                    return this.isDesignMode && !this.mobilePcSectionLocked;
                },
                appState() {
                    return window.JAYDREAM_VUE_GLOBAL[this.$app_name];
                },
            },
            watch: {
                appState: {
                    async handler(newVal) {
                        if (newVal.reload) {
                            await this.getRow();
                            this.appState.reload = false;
                        }
                    },
                    deep: true,
                },
            },
        }});
</script>

<style>
    .design-widget-blank {
        width: 100%;
        position: relative;
    }

    .design-widget-blank-spacer {
        position: relative;
        width: 100%;
        height: var(--design-widget-blank-pc-height, 75px);
        box-sizing: border-box;
    }

    .design-widget-blank--editable .design-widget-blank-spacer {
        border: 1px dotted #cfcfcf;
    }

    .design-widget-blank-resize-handle {
        position: absolute;
        left: 0;
        right: 0;
        bottom: -6px;
        z-index: 95;
        display: block;
        width: 100%;
        height: 12px;
        padding: 0;
        border: 0;
        background: transparent;
        cursor: ns-resize;
        touch-action: none;
    }

    .design-widget-blank-resize-handle::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: 5px;
        border-bottom: 2px solid transparent;
    }

    .design-widget-blank-resize-handle:hover::after,
    .design-widget-blank--dragging .design-widget-blank-resize-handle::after {
        border-bottom-color: #ff5f37;
    }

    body.design-widget-blank-resizing {
        cursor: ns-resize !important;
        user-select: none;
    }

    @media (max-width: 767px) {
        .design-widget-blank--mobile-custom .design-widget-blank-spacer {
            height: var(--design-widget-blank-mobile-height, 75px);
        }

        .design-widget-blank--mobile-hide {
            display: none !important;
        }
    }

    body._mbv .design-widget-blank--mobile-custom .design-widget-blank-spacer {
        height: var(--design-widget-blank-mobile-height, 75px);
    }

    body._mbv .design-widget-blank--mobile-hide {
        display: none !important;
    }
</style>

<script>
    let site_code = "st68f59208a5890";
    let jaydream_widget_table_map = {
        'design-widget-image': 'jd_widget_image',
        'design-widget-map': 'jd_widget_map',
        'design-widget-multi': 'jd_widget_multi',
        'design-widget-slide': 'jd_widget_slide',
        'design-widget-imagemap': 'jd_widget_imagemap',
        'design-widget-text': 'jd_widget_text',
        'design-widget-button': 'jd_widget_button',
        'design-widget-blank': 'jd_widget_blank'
    };
    let jaydream_widget_selector = Object.keys(jaydream_widget_table_map).join(',');

    document.addEventListener('DOMContentLoaded', async () => {
        let loaded_widgets = {};
        let preload_items = [];
        let widget_codes = [];

        document.querySelectorAll('.widget[id]').forEach((widget) => {
            let code = widget.getAttribute('id');
            let component = widget.querySelector(jaydream_widget_selector);
            let tag = component ? component.tagName.toLowerCase() : "";
            let table = tag ? jaydream_widget_table_map[tag] : "";

            if (!code || loaded_widgets[code]) return;
            if (!table) return;

            loaded_widgets[code] = true;
            preload_items.push({
                code: code,
                table: table
            });
            widget_codes.push(code);
        });

        if (preload_items.length && window.JayDream && JayDream.lib && JayDream.api) {
            try {
                let response = await JayDream.lib.ajax(
                    "widget_batch_get",
                    {items: preload_items},
                    "/JayDream/api.php",
                    {return: true, component_name: "widget_batch_get"}
                );

                if (response && response.success && JayDream.api.setWidgetDetailCache) {
                    JayDream.api.setWidgetDetailCache(response.data || {});
                }
            } catch (e) {
                if (window.console) console.warn(e);
            }
        }

        widget_codes.forEach((code) => {
            window.vueLoad(code);
        });
    });

</script>
