Rails Modal Manager

Rails 애플리케이션을 위한 고급 모달 매니저입니다. @reshacs/react-modal-manager에서 포팅되었습니다.

Version: 1.0.33


목차

  1. 설치
  2. 기본 사용법
  3. 모달 옵션
  4. 컴포넌트
  5. 부모-자식 모달
  6. JavaScript API
  7. CSS 커스터마이징
  8. 예제

설치

1. Gemfile에 추가

gem 'rails_modal_manager', path: 'path/to/rails-modal-manager'

2. Bundle 설치

bundle install

3. JavaScript 설정

app/javascript/application.js에 추가:

import { Application } from "@hotwired/stimulus"
import { registerRMMControllers, initHistoryStack, initGlobalClickHandler } from "rails_modal_manager"

const application = Application.start()

// RMM 컨트롤러 등록
registerRMMControllers(application)

// 히스토리 스택 초기화 (브라우저 뒤로가기 지원)
initHistoryStack()

// 글로벌 클릭 핸들러 초기화 (data-rmm-modal-id 버튼 지원)
initGlobalClickHandler()

4. CSS 설정

app/assets/stylesheets/application.css에 추가:

@import "rails_modal_manager";

5. Importmap 설정 (importmap-rails 사용 시)

config/importmap.rb에 추가:

pin "rails_modal_manager", to: "rails_modal_manager/index.js"
pin "rails_modal_manager/modal_store", to: "rails_modal_manager/modal_store.js"
pin "rails_modal_manager/history_stack_manager", to: "rails_modal_manager/history_stack_manager.js"
pin "rails_modal_manager/controllers/rmm_modal_controller", to: "rails_modal_manager/controllers/rmm_modal_controller.js"
pin "rails_modal_manager/controllers/rmm_overlay_controller", to: "rails_modal_manager/controllers/rmm_overlay_controller.js"
pin "rails_modal_manager/controllers/rmm_header_controller", to: "rails_modal_manager/controllers/rmm_header_controller.js"
pin "rails_modal_manager/controllers/rmm_sidebar_controller", to: "rails_modal_manager/controllers/rmm_sidebar_controller.js"
pin "rails_modal_manager/controllers/rmm_submenu_controller", to: "rails_modal_manager/controllers/rmm_submenu_controller.js"
pin "rails_modal_manager/controllers/rmm_taskbar_controller", to: "rails_modal_manager/controllers/rmm_taskbar_controller.js"
pin "rails_modal_manager/controllers/rmm_resize_handle_controller", to: "rails_modal_manager/controllers/rmm_resize_handle_controller.js"

기본 사용법

정적 모달

HTML에 미리 정의된 모달:

<!-- Overlay -->
<div class="rmm-overlay" id="my-modal-overlay"
     data-controller="rmm-overlay"
     data-rmm-overlay-modal-id-value="my-modal"
     data-rmm-overlay-close-on-click-value="true"
     data-action="click->rmm-overlay#handleClick">
</div>

<!-- Modal -->
<div id="my-modal"
     class="rmm-modal rmm-size-md rmm-position-center"
     data-controller="rmm-modal"
     data-rmm-modal-modal-id-value="my-modal"
     data-rmm-modal-title-value="모달 제목"
     data-rmm-modal-size-value="md"
     data-rmm-modal-position-value="center"
     data-rmm-modal-draggable-value="true"
     data-rmm-modal-closable-value="true"
     data-rmm-modal-close-on-esc-value="true"
     data-action="click->rmm-modal#handleClick"
     role="dialog"
     aria-modal="true">

  <!-- Header -->
  <div class="rmm-header rmm-draggable"
       data-controller="rmm-header"
       data-rmm-header-modal-id-value="my-modal"
       data-rmm-header-draggable-value="true"
       data-action="mousedown->rmm-header#handleMouseDown">
    <div class="rmm-header-left">
      <span class="rmm-drag-handle" title="드래그하여 이동">
        <svg viewBox="0 0 24 24" fill="currentColor">
          <circle cx="9" cy="5" r="1.5"></circle>
          <circle cx="15" cy="5" r="1.5"></circle>
          <circle cx="9" cy="10" r="1.5"></circle>
          <circle cx="15" cy="10" r="1.5"></circle>
          <circle cx="9" cy="15" r="1.5"></circle>
          <circle cx="15" cy="15" r="1.5"></circle>
        </svg>
      </span>
      <h2 class="rmm-header-title">모달 제목</h2>
    </div>
    <div class="rmm-header-controls">
      <button type="button" class="rmm-header-btn rmm-close-btn"
              data-action="click->rmm-header#close">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <line x1="18" y1="6" x2="6" y2="18"></line>
          <line x1="6" y1="6" x2="18" y2="18"></line>
        </svg>
      </button>
    </div>
  </div>

  <!-- Body -->
  <div class="rmm-body">
    <div class="rmm-content-wrapper">
      <div class="rmm-content">
        <p>모달 내용</p>
      </div>
    </div>
  </div>
</div>

모달 열기/닫기

// 모달 열기
const modalElement = document.getElementById('my-modal')
const controller = application.getControllerForElementAndIdentifier(modalElement, 'rmm-modal')
controller.open()

// 또는 JavaScript API 사용
import { openModal, closeModal } from "rails_modal_manager"
openModal('my-modal')
closeModal('my-modal')

모달 옵션

모달 컨트롤러 (rmm-modal)

속성 타입 기본값 설명
modal-id String - 모달 고유 ID (필수)
title String "Modal" 모달 제목
size String "md" 크기 (fit, xss, xs, sm, md, lg, xl, full)
position String "center" 위치 (center, top, bottom, top-left, top-right, bottom-left, bottom-right)
height String "auto" 높이 (auto, 300px, 50vh 등)
draggable Boolean false 드래그 가능 여부
resizable Boolean false 리사이즈 가능 여부
minimizable Boolean false 최소화 가능 여부
closable Boolean true 닫기 가능 여부
close-on-esc Boolean true ESC 키로 닫기
close-on-overlay Boolean true 오버레이 클릭으로 닫기
hide-overlay Boolean false 오버레이 숨김
enable-history-stack Boolean true 브라우저 히스토리 연동
confirm-close Boolean false 닫기 시 확인 다이얼로그
confirm-close-message String "정말 닫으시겠습니까?" 확인 메시지
mobile-default-maximized Boolean false 모바일에서 기본 최대화
parent-modal-id String - 부모 모달 ID (종속 관계)
persistent-id String - 크기 상태 공유용 ID (같은 타입 모달 간 크기 공유)
min-width Number 200 최소 너비 (px)
min-height Number 150 최소 높이 (px)
max-width Number - 최대 너비 (px)
max-height Number - 최대 높이 (px)

크기 (Size)

너비 최소 너비 설명
fit auto 150px 콘텐츠에 맞춰 자동 조절
xss 20% 200px 매우 작은 크기
xs 30% 280px 작은 크기
sm 40% 360px 중소 크기
md 50% 480px 중간 크기 (기본값)
lg 60% 600px 큰 크기
xl 70% 720px 매우 큰 크기
full 100% 100% 전체 화면

컴포넌트

모달 상단 영역으로 제목, 드래그 핸들, 컨트롤 버튼을 포함합니다.

<div class="rmm-header rmm-draggable"
     data-controller="rmm-header"
     data-rmm-header-modal-id-value="my-modal"
     data-rmm-header-draggable-value="true"
     data-rmm-header-default-size-value="md"
     data-action="mousedown->rmm-header#handleMouseDown touchstart->rmm-header#handleTouchStart">

  <div class="rmm-header-left">
    <!-- 사이드바 토글 버튼 (선택) -->
    <button type="button" class="rmm-header-btn rmm-sidebar-toggle"
            onclick="...">
      <svg>...</svg>
    </button>

    <!-- 드래그 핸들 -->
    <span class="rmm-drag-handle" title="드래그하여 이동">
      <svg>...</svg>
    </span>

    <!-- 제목 -->
    <h2 class="rmm-header-title">모달 제목</h2>
  </div>

  <div class="rmm-header-controls">
    <!-- 최소화 버튼 -->
    <button type="button" class="rmm-header-btn"
            data-action="click->rmm-header#minimize" title="최소화">
      <svg>...</svg>
    </button>

    <!-- 기본 크기 버튼 -->
    <button type="button" class="rmm-header-btn"
            data-action="click->rmm-header#restoreDefaultSize" title="기본 크기">
      <svg>...</svg>
    </button>

    <!-- 최대화/복원 버튼 -->
    <button type="button" class="rmm-header-btn rmm-maximize-btn"
            data-action="click->rmm-header#maximize" title="최대화">
      <svg class="rmm-icon-maximize">...</svg>
      <svg class="rmm-icon-restore" style="display: none;">...</svg>
    </button>

    <!-- 닫기 버튼 -->
    <button type="button" class="rmm-header-btn rmm-close-btn"
            data-action="click->rmm-header#close">
      <svg>...</svg>
    </button>
  </div>
</div>

버튼 순서 권장: [최소화] [크기조절] [커스텀 버튼들] [닫기]

헤더에 커스텀 버튼을 추가할 수 있습니다:

header_buttons: [
  { id: "delete-btn", icon: "trash", title: "삭제", variant: "danger", onclick: "deleteItem()" },
  { id: "edit-btn", icon: "edit", title: "수정", onclick: "editItem()" }
]

Header Button Options:

  • id - 버튼 ID (선택)
  • icon - 미리 정의된 아이콘: trash, edit, settings, info, warning, download, refresh, plus
  • icon_svg - 커스텀 SVG 아이콘 (icon 옵션보다 우선)
  • title - 툴팁 텍스트
  • variant - 스타일: default, danger, warning, success, info
  • onclick - JavaScript 클릭 핸들러

모달 좌측 네비게이션 영역입니다.

<div class="rmm-body">
  <!-- Sidebar -->
  <div class="rmm-sidebar"
       data-controller="rmm-sidebar"
       data-rmm-sidebar-modal-id-value="my-modal"
       data-rmm-sidebar-collapsed-value="false">
    <nav class="rmm-sidebar-nav" data-rmm-sidebar-target="nav">
      <button type="button" class="rmm-sidebar-item rmm-active"
              data-item-id="home"
              data-item-label="홈"
              title="홈"
              data-action="click->rmm-sidebar#selectItem">
        <span class="rmm-sidebar-item-icon"><svg>...</svg></span>
        <span class="rmm-sidebar-item-label">홈</span>
      </button>
      <button type="button" class="rmm-sidebar-item"
              data-item-id="settings"
              data-item-label="설정"
              title="설정"
              data-action="click->rmm-sidebar#selectItem">
        <span class="rmm-sidebar-item-icon"><svg>...</svg></span>
        <span class="rmm-sidebar-item-label">설정</span>
        <span class="rmm-sidebar-item-badge">3</span>
      </button>
    </nav>
  </div>

  <!-- Mobile Overlay -->
  <div class="rmm-sidebar-overlay" data-action="click->rmm-sidebar#closeOverlay"></div>

  <!-- Content -->
  <div class="rmm-content-wrapper">
    ...
  </div>
</div>

이벤트:

  • rmm-sidebar:itemSelect - 아이템 선택 시 발생

컨텐츠 상단 탭 네비게이션입니다.

<div class="rmm-content-wrapper">
  <!-- Submenu -->
  <div class="rmm-submenu"
       data-controller="rmm-submenu"
       data-rmm-submenu-modal-id-value="my-modal">
    <button type="button" class="rmm-submenu-item rmm-active"
            data-item-id="tab1"
            data-item-label="탭 1"
            data-action="click->rmm-submenu#selectItem">
      <span>탭 1</span>
    </button>
    <button type="button" class="rmm-submenu-item"
            data-item-id="tab2"
            data-item-label="탭 2"
            data-action="click->rmm-submenu#selectItem">
      <span>탭 2</span>
    </button>
  </div>

  <!-- Content -->
  <div class="rmm-content">
    ...
  </div>
</div>

모달 하단 버튼 영역입니다.

<div class="rmm-footer">
  <div class="rmm-footer-left">
    <span class="rmm-footer-message">메시지 영역</span>
  </div>
  <div class="rmm-footer-right">
    <button type="button" class="rmm-btn rmm-btn-secondary"
            data-action="click->rmm-header#close">취소</button>
    <button type="button" class="rmm-btn rmm-btn-primary">확인</button>
  </div>
</div>

버튼 클래스:

  • rmm-btn-primary - 기본 (파란색)
  • rmm-btn-secondary - 보조 (회색)
  • rmm-btn-success - 성공 (초록색)
  • rmm-btn-danger - 위험 (빨간색)
  • rmm-btn-warning - 경고 (주황색)

Overlay

모달 배경 오버레이입니다.

<div class="rmm-overlay" id="my-modal-overlay"
     data-controller="rmm-overlay"
     data-rmm-overlay-modal-id-value="my-modal"
     data-rmm-overlay-close-on-click-value="true"
     data-rmm-overlay-close-on-dblclick-value="false"
     data-action="click->rmm-overlay#handleClick">
</div>
속성 타입 기본값 설명
modal-id String - 연결된 모달 ID
close-on-click Boolean true 클릭으로 닫기
close-on-dblclick Boolean false 더블클릭으로 닫기

오버레이 이벤트 통과:

<div class="rmm-overlay rmm-overlay-passthrough" ...>
</div>

Taskbar

최소화된 모달을 표시하는 하단 바입니다.

<div class="rmm-taskbar"
     data-controller="rmm-taskbar"
     data-rmm-taskbar-target="container">
  <div class="rmm-taskbar-items" data-rmm-taskbar-target="items"></div>
</div>

Taskbar는 자동으로 최소화된 모달들을 렌더링합니다.

Taskbar 기능 (v1.0.33+):

  • 클릭하여 복구: 태스크바 아이템 클릭 시 모달 복구 (X버튼 제외)
  • 드래그 앤 드롭: 아이템을 드래그하여 순서 변경
  • 키보드 단축키: Alt+1~9로 빠른 복구/최소화 토글
  • 모두 닫기: 2개 이상 최소화 시 "모두 닫기" 버튼 표시
  • 확인 다이얼로그: X버튼 클릭 시 닫기 확인

Resize Handle

모달 리사이즈를 위한 핸들입니다.

<div class="rmm-resize-handles">
  <div class="rmm-resize-handle rmm-resize-handle-n"
       data-controller="rmm-resize-handle"
       data-rmm-resize-handle-direction-value="n"
       data-rmm-resize-handle-modal-id-value="my-modal"
       data-action="mousedown->rmm-resize-handle#handleMouseDown"></div>
  <div class="rmm-resize-handle rmm-resize-handle-s" ...></div>
  <div class="rmm-resize-handle rmm-resize-handle-e" ...></div>
  <div class="rmm-resize-handle rmm-resize-handle-w" ...></div>
  <div class="rmm-resize-handle rmm-resize-handle-ne" ...></div>
  <div class="rmm-resize-handle rmm-resize-handle-nw" ...></div>
  <div class="rmm-resize-handle rmm-resize-handle-se" ...></div>
  <div class="rmm-resize-handle rmm-resize-handle-sw" ...></div>
</div>

방향: n, s, e, w, ne, nw, se, sw


부모-자식 모달

모달 간 종속 관계를 설정할 수 있습니다.

설정

자식 모달에 parent-modal-id 속성을 추가합니다:

<!-- 부모 모달 -->
<div id="parent-modal" data-controller="rmm-modal" ...>
  ...
</div>

<!-- 자식 모달 -->
<div id="child-modal"
     data-controller="rmm-modal"
     data-rmm-modal-parent-modal-id-value="parent-modal"
     ...>
  ...
</div>

<!-- 손자 모달 -->
<div id="grandchild-modal"
     data-controller="rmm-modal"
     data-rmm-modal-parent-modal-id-value="child-modal"
     ...>
  ...
</div>

동작

  • 부모 최소화: 모든 자손 모달도 함께 최소화
  • 부모 닫기: 모든 자손 모달도 함께 닫힘
  • 자식 닫기: 해당 자식의 모든 자손만 닫힘
  • Taskbar: 그룹으로 표시되며, 그룹 크기 배지 표시

JavaScript API

모듈 임포트

import {
  VERSION,
  modalStore,
  historyStackManager,
  registerRMMControllers,
  initHistoryStack,
  openModal,
  closeModal,
  closeTopModal,
  closeAllModals,
  arrangeModals,
  getOpenModalCount,
  getModalStore,
} from "rails_modal_manager"

함수

registerRMMControllers(application)

Stimulus 애플리케이션에 모든 RMM 컨트롤러를 등록합니다.

import { Application } from "@hotwired/stimulus"
import { registerRMMControllers } from "rails_modal_manager"

const application = Application.start()
registerRMMControllers(application)

initHistoryStack()

히스토리 스택 매니저를 초기화합니다. 브라우저 뒤로가기로 모달을 닫을 수 있습니다.

initHistoryStack()

openModal(modalId)

모달을 엽니다.

openModal('my-modal')

closeModal(modalId, source)

모달을 닫습니다.

closeModal('my-modal', 'programmatic')

closeTopModal()

가장 위에 있는 모달을 닫습니다.

const closedId = closeTopModal()

closeAllModals()

모든 모달을 닫습니다.

closeAllModals()

arrangeModals(arrangement, baseSize)

열린 모달들을 정렬합니다.

arrangeModals('cascade', 'sm')  // 계단식
arrangeModals('tile', 'sm')     // 타일식
arrangeModals('corners', 'sm')  // 모서리
arrangeModals('horizontal')     // 수평
arrangeModals('vertical')       // 수직

getOpenModalCount()

열린 모달 개수를 반환합니다.

const count = getOpenModalCount()

직접 모달 스토어에 접근할 수 있습니다:

import { modalStore } from "rails_modal_manager"

// 모달 설정 가져오기
const config = modalStore.getModalConfig('my-modal')

// 모달 업데이트
modalStore.updateModal('my-modal', { size: 'lg' })

// 맨 앞으로 가져오기
modalStore.bringToFront('my-modal')

// 구독
const unsubscribe = modalStore.subscribe((state) => {
  console.log('State changed:', state)
})

// 부모-자식 관계
const rootId = modalStore.getRootModal('child-modal')
const children = modalStore.getChildModals('parent-modal')
const descendants = modalStore.getAllDescendants('parent-modal')

CSS 커스터마이징

CSS 변수

:root {
  /* Colors */
  --rmm-bg: #ffffff;
  --rmm-border: #e2e8f0;
  --rmm-overlay-bg: rgba(0, 0, 0, 0.5);

  /* Header */
  --rmm-header-height: 48px;
  --rmm-header-bg: #f8fafc;
  --rmm-header-text: #1e293b;
  --rmm-header-border: #e2e8f0;

  /* Content */
  --rmm-content-bg: #ffffff;
  --rmm-content-text: #334155;

  /* Footer */
  --rmm-footer-height: 56px;
  --rmm-footer-bg: #f8fafc;
  --rmm-footer-border: #e2e8f0;

  /* Sidebar */
  --rmm-sidebar-width: 220px;
  --rmm-sidebar-collapsed-width: 56px;
  --rmm-sidebar-bg: #f1f5f9;
  --rmm-sidebar-text: #475569;
  --rmm-sidebar-hover: #e2e8f0;
  --rmm-sidebar-active: #3b82f6;
  --rmm-sidebar-active-bg: #dbeafe;

  /* Buttons */
  --rmm-btn-primary-bg: #3b82f6;
  --rmm-btn-primary-text: #ffffff;
  --rmm-btn-secondary-bg: #e2e8f0;
  --rmm-btn-secondary-text: #334155;
  --rmm-btn-danger-bg: #ef4444;
  --rmm-btn-danger-text: #ffffff;

  /* Taskbar */
  --rmm-taskbar-bg: #1e293b;
  --rmm-taskbar-text: #f1f5f9;
  --rmm-taskbar-height: 40px;

  /* Shadows */
  --rmm-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);

  /* Animation */
  --rmm-transition-duration: 200ms;
}

다크 모드

.dark {
  --rmm-bg: #1e293b;
  --rmm-border: #334155;
  --rmm-header-bg: #0f172a;
  --rmm-header-text: #f1f5f9;
  --rmm-content-bg: #1e293b;
  --rmm-content-text: #e2e8f0;
  /* ... */
}

예제

기본 확인 모달

<%= render 'modals/confirm',
    id: 'delete-confirm',
    title: '삭제 확인',
    message: '정말 삭제하시겠습니까?',
    confirm_text: '삭제',
    cancel_text: '취소' %>

동적 모달 생성

function createModal(id, title, content) {
  const html = `
    <div class="rmm-overlay" id="${id}-overlay"
         data-controller="rmm-overlay"
         data-rmm-overlay-modal-id-value="${id}"
         data-rmm-overlay-close-on-click-value="true"
         data-action="click->rmm-overlay#handleClick"></div>

    <div id="${id}"
         class="rmm-modal rmm-size-md rmm-position-center"
         data-controller="rmm-modal"
         data-rmm-modal-modal-id-value="${id}"
         data-rmm-modal-title-value="${title}"
         data-rmm-modal-draggable-value="true"
         data-rmm-modal-closable-value="true"
         data-action="click->rmm-modal#handleClick">

      <div class="rmm-header rmm-draggable"
           data-controller="rmm-header"
           data-rmm-header-modal-id-value="${id}"
           data-rmm-header-draggable-value="true"
           data-action="mousedown->rmm-header#handleMouseDown">
        <div class="rmm-header-left">
          <h2 class="rmm-header-title">${title}</h2>
        </div>
        <div class="rmm-header-controls">
          <button type="button" class="rmm-header-btn rmm-close-btn"
                  data-action="click->rmm-header#close">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <line x1="18" y1="6" x2="6" y2="18"></line>
              <line x1="6" y1="6" x2="18" y2="18"></line>
            </svg>
          </button>
        </div>
      </div>

      <div class="rmm-body">
        <div class="rmm-content-wrapper">
          <div class="rmm-content">
            ${content}
          </div>
        </div>
      </div>
    </div>
  `

  document.body.insertAdjacentHTML('beforeend', html)

  requestAnimationFrame(() => {
    const element = document.getElementById(id)
    const controller = application.getControllerForElementAndIdentifier(element, 'rmm-modal')
    controller.open()
  })
}

Turbo Frame과 함께 사용

<%= turbo_frame_tag "modal-content" do %>
  <div class="rmm-content">
    <%= render partial: "my_partial" %>
  </div>
<% end %>

라이선스

MIT License


기여

버그 리포트와 기능 제안은 GitHub Issues를 이용해주세요.


변경 이력

v1.0.33

  • Taskbar 기능 대폭 개선:
    • 클릭하여 복구: 태스크바 아이템 클릭 시 모달 복구 (X버튼 제외)
    • 드래그 앤 드롭: 최소화된 모달 순서 변경 지원
    • 키보드 단축키: Alt+1~9로 빠른 복구/최소화 토글
    • 모두 닫기 버튼: 2개 이상 최소화 시 표시 (확인 다이얼로그 포함)
    • 개별 닫기 확인: X버튼 클릭 시 확인 다이얼로그 표시
  • 복구된 모달 상태 표시: 최소화에서 복구된 모달은 태스크바에서 비활성화 상태로 표시
  • 같은 ID 모달 재오픈 버그 수정: 최소화된 상태에서 같은 ID로 모달 열기 시 정상적으로 복구

v1.0.32

  • persistent_id 옵션 추가: 같은 타입의 모달들이 크기 상태를 공유할 수 있도록 지원
  • 업체 모달, 사용자 프로필 모달 등에서 활용 가능

v1.0.31

  • 모달 크기 기억 기능 추가: 모달이 마지막 크기(최대화/기본)를 localStorage에 저장하여 다음에 열 때 동일한 크기로 열림
  • 모바일 사이드바 동작 개선: 최대화된 모달에서 아이콘 상태는 inline(본문 공간 차지), 확장 상태는 오버레이 방식으로 변경
  • 사이드바 3상태 토글: expanded(확장), icons(아이콘), hidden(숨김) 상태 지원

v1.0.24

  • submenu_groups 옵션 추가: 사이드바의 각 메뉴에 서로 다른 서브메뉴 그룹을 연결 가능
  • forceReloadActive 이벤트 발생: 사이드바 전환 시 rmm-submenu:itemSelect 이벤트 발생 (source: 'forceReload')
  • 커스텀 콘텐츠 로딩 가이드 추가: 커스텀 컨트롤러에서 콘텐츠 로딩 시 권장 패턴 문서화

v1.0.23

  • title 속성 추가: 사이드바 아이템과 서브메뉴 아이템에 마우스 오버 툴팁 지원

v1.0.19 ~ v1.0.22

  • 내부 개선 및 버그 수정

v1.0.18

  • 모달 리셋 시 콘텐츠 동기화: resetToFirst() 호출 시 itemSelect 이벤트 발생하도록 개선
  • 사이드바/서브메뉴 리셋 시 커스텀 컨트롤러에 알림 전달
  • 이벤트 detail에 source: 'reset' 플래그 추가로 일반 선택과 리셋 구분 가능

v1.0.17

  • 모바일 최대화 모달 사이드바 개선: 최대화된 모달에서 사이드바가 접혔을 때 아이콘만 표시 (데스크톱과 동일한 UX)
  • 모바일 기본크기 버튼 숨김: 모바일에서 크기 컨트롤의 기본크기 버튼 숨김 처리
  • 모바일 사이드바 리셋 개선: 모달 닫힘 시 모바일에서 사이드바가 항상 접힌 상태로 리셋
  • mobile_default_maximized 옵션 수정: ERB 템플릿에서 누락된 data 속성 추가

v1.0.16

  • 모달 상태 리셋 개선: 모달 닫힘 시 사이드바와 서브메뉴가 첫 번째 항목으로 자동 리셋

v1.0.13 ~ v1.0.15

  • 내부 개선 및 버그 수정

v1.0.12

  • 최소화된 모달 자동 복원: openModal() 호출 시 해당 모달이 이미 열려있고 최소화된 상태라면 자동으로 복원되도록 개선

v1.0.11

  • 사이드바-서브메뉴 AJAX 연동 개선: 사이드바 메뉴 전환 시 활성화된 서브메뉴의 콘텐츠가 자동으로 로드되도록 개선
  • 서브메뉴 컨트롤러에 forceReloadActive() 메서드 추가: 외부에서 현재 활성 탭의 콘텐츠를 강제로 다시 로드할 수 있는 메서드 추가

v1.0.10

  • 초기 안정화 버전