@ablogcms/modal 3.2.28-beta.0 → 3.2.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  import { useCallback, useEffect, useRef as useRef2 } from "react";
12
12
  import { emit } from "@ablogcms/events";
13
13
  import useUpdateEffect from "@ablogcms/react-hooks/use-update-effect";
14
- import { datasetToProps } from "@ablogcms/utils/react";
14
+ import { datasetToProps } from "@ablogcms/react-utils";
15
15
  import Modal, { ModalBody, ModalFooter, ModalHeader } from "@ablogcms/components/modal";
16
16
 
17
17
  // src/store/use-modal-manager.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/modal-controller.tsx","../src/store/use-modal-manager.ts","../src/store/modal-manager.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { emit } from '@ablogcms/events';\nimport useUpdateEffect from '@ablogcms/react-hooks/use-update-effect';\nimport { datasetToProps } from '@ablogcms/utils/react';\nimport Modal, { ModalBody, ModalFooter, ModalHeader } from '@ablogcms/components/modal';\nimport useModalManager, { UseModalManagerOptions } from './store/use-modal-manager';\n\ntype ModalProps = React.ComponentProps<typeof Modal>;\ntype ModalHeaderProps = React.ComponentProps<typeof ModalHeader>;\ntype ModalBodyProps = React.ComponentProps<typeof ModalBody>;\ntype ModalFooterProps = React.ComponentProps<typeof ModalFooter>;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ModalControllerProps extends UseModalManagerOptions {}\n\n/**\n * data-modal-*属性を動的に取得する関数\n */\nfunction parseProps<T extends 'modal' | 'modal-header' | 'modal-body' | 'modal-footer'>(\n element: HTMLElement,\n type: T\n): Partial<\n T extends 'modal'\n ? ModalProps\n : T extends 'modal-header'\n ? ModalHeaderProps\n : T extends 'modal-body'\n ? ModalBodyProps\n : ModalFooterProps\n> {\n const props = datasetToProps(element.dataset, type);\n return props as Partial<\n T extends 'modal'\n ? ModalProps\n : T extends 'modal-header'\n ? ModalHeaderProps\n : T extends 'modal-body'\n ? ModalBodyProps\n : ModalFooterProps\n >;\n}\n\nfunction ModalController({ ...props }: ModalControllerProps) {\n const { manager, state } = useModalManager(props);\n const openStateRef = useRef<boolean>(state.modalProps.isOpen);\n\n const hydrateModalContent = useCallback(() => {\n if (manager.container) {\n ACMS.Dispatch(manager.container);\n void emit('dialog.open.after', { element: manager.container });\n ACMS.dispatchEvent('acmsDialogOpened', manager.container, {\n item: manager.container,\n });\n }\n }, [manager.container]);\n\n const handleAfterOpen = useCallback(() => {\n openStateRef.current = true;\n hydrateModalContent();\n }, [hydrateModalContent]);\n\n const handleAfterClose = useCallback(() => {\n openStateRef.current = false;\n // 閉じるトランジション完了後に state をクリアする\n manager.reset();\n }, [manager]);\n\n useEffect(() => {\n const handleOpen = (event: Event) => {\n if (!(event.target instanceof HTMLElement)) {\n return;\n }\n\n const element = event.target.closest<HTMLElement>(ModalController.openTrigger);\n\n if (!element) {\n return;\n }\n\n const { target: selector, url } = element.dataset;\n\n // Modal用 props とセレクタ情報を data-* から取得\n const modalProps = parseProps(element, 'modal');\n const modalHeaderProps = parseProps(element, 'modal-header');\n const modalBodyProps = parseProps(element, 'modal-body');\n const modalFooterProps = parseProps(element, 'modal-footer');\n\n // ModalManagerでモーダルを開く\n manager.open({\n selector,\n url,\n modalProps,\n modalHeaderProps,\n modalBodyProps,\n modalFooterProps,\n });\n };\n\n const handleClose = (event: Event) => {\n if (!(event.target instanceof HTMLElement)) {\n return;\n }\n\n const element = event.target.closest<HTMLElement>(ModalController.closeTrigger);\n\n if (!element) {\n return;\n }\n\n manager.close();\n };\n document.addEventListener('click', handleOpen);\n document.addEventListener('click', handleClose);\n\n return () => {\n document.removeEventListener('click', handleOpen);\n document.removeEventListener('click', handleClose);\n };\n }, [manager]);\n\n useUpdateEffect(() => {\n if (!openStateRef.current) {\n // モーダルが開いた初回はonAfterOpenでhydrateするためスキップ\n return;\n }\n\n hydrateModalContent();\n // コンテンツが変更されたら組み込みJSを実行\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [state.content.raw]);\n\n if (!manager.container) {\n return null;\n }\n\n return (\n <Modal\n {...state.modalProps}\n onClose={manager.close}\n container={manager.container}\n onAfterOpen={handleAfterOpen}\n onAfterClose={handleAfterClose}\n >\n {state.content.header && (\n <ModalHeader {...state.modalHeaderProps}>\n {}\n <span dangerouslySetInnerHTML={{ __html: state.content.header }} />\n </ModalHeader>\n )}\n <ModalBody {...state.modalBodyProps}>\n {}\n <div dangerouslySetInnerHTML={{ __html: state.content.body || state.content.raw }} />\n </ModalBody>\n {state.content.footer && (\n <ModalFooter {...state.modalFooterProps}>\n {}\n <div dangerouslySetInnerHTML={{ __html: state.content.footer }} />\n </ModalFooter>\n )}\n </Modal>\n );\n}\n\nModalController.openTrigger = '.js-acms-modal-open';\nModalController.closeTrigger = '.js-acms-modal-close';\n\nexport default ModalController;\n","import { useRef, useState, useSyncExternalStore } from 'react';\nimport ModalManager, { ModalManagerOptions } from './modal-manager';\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface UseModalManagerOptions extends Partial<ModalManagerOptions> {}\n\nexport default function useModalManager(options: UseModalManagerOptions = {}) {\n const mostRecentOptions = useRef(options);\n\n mostRecentOptions.current = options;\n const [manager] = useState(() => new ModalManager(mostRecentOptions));\n\n const state = useSyncExternalStore(\n manager.subscribe,\n () => manager.getSnapshot(),\n () => manager.getSnapshot()\n );\n\n ACMS.Library.modal = manager;\n\n return {\n state,\n manager,\n };\n}\n","import { pending } from '@ablogcms/pending';\nimport type Modal from '@ablogcms/components/modal';\nimport type { ModalBody, ModalFooter, ModalHeader } from '@ablogcms/components/modal';\nimport { fetchContentFromUrl, getContentFromSelector, patchFormActions } from './content-fetcher';\nimport SecurityManager, { SecurityConfig } from './security-manager';\n\ntype ModalProps = Omit<React.ComponentProps<typeof Modal>, 'onClose'>;\ntype ModalHeaderProps = Omit<React.ComponentProps<typeof ModalHeader>, 'children'>;\ntype ModalBodyProps = Omit<React.ComponentProps<typeof ModalBody>, 'children'>;\ntype ModalFooterProps = Omit<React.ComponentProps<typeof ModalFooter>, 'children'>;\n\nexport interface ModalManagerState {\n content: Content;\n modalProps: ModalProps;\n modalHeaderProps: ModalHeaderProps;\n modalBodyProps: ModalBodyProps;\n modalFooterProps: ModalFooterProps;\n}\n\nexport interface ModalManagerOptions {\n header: string;\n body: string;\n footer: string;\n security: Partial<SecurityConfig>;\n}\n\ninterface Content {\n header?: string;\n body?: string;\n footer?: string;\n raw: string;\n}\n\ninterface ModalManagerOpenOptions {\n selector?: string;\n url?: string;\n modalProps?: Partial<ModalProps>;\n modalHeaderProps?: Partial<ModalHeaderProps>;\n modalBodyProps?: Partial<ModalBodyProps>;\n modalFooterProps?: Partial<ModalFooterProps>;\n}\n\nconst defaultOptions: ModalManagerOptions = {\n header: '[data-modal-section=\"header\"]',\n body: '[data-modal-section=\"body\"]',\n footer: '[data-modal-section=\"footer\"]',\n security: {},\n};\n\nclass ModalManager {\n private listeners = new Set<() => void>();\n\n private state: ModalManagerState = {\n content: {\n raw: '',\n },\n modalProps: {\n isOpen: false,\n },\n modalHeaderProps: {},\n modalBodyProps: {},\n modalFooterProps: {},\n };\n\n #container: HTMLElement | null = null;\n\n private config: ModalManagerOptions = defaultOptions;\n\n private securityManager: SecurityManager;\n\n constructor(options: React.MutableRefObject<Partial<ModalManagerOptions>>) {\n this.config = { ...defaultOptions, ...options.current };\n this.securityManager = new SecurityManager(this.config.security);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.open = this.open.bind(this);\n this.close = this.close.bind(this);\n this.reset = this.reset.bind(this);\n this.init();\n }\n\n subscribe = (callback: () => void) => {\n this.listeners.add(callback);\n return () => this.listeners.delete(callback);\n };\n\n getSnapshot = () => this.state;\n\n public setState(state: ModalManagerState | ((state: ModalManagerState) => ModalManagerState)) {\n if (typeof state === 'function') {\n this.state = state(this.state);\n } else {\n this.state = state;\n }\n this.emit();\n }\n\n public get content() {\n return this.state.content;\n }\n\n public get modalProps() {\n return this.state.modalProps;\n }\n\n public get modalHeaderProps() {\n return this.state.modalHeaderProps;\n }\n\n public get modalBodyProps() {\n return this.state.modalBodyProps;\n }\n\n public get modalFooterProps() {\n return this.state.modalFooterProps;\n }\n\n public get container() {\n return this.#container;\n }\n\n private init() {\n // モーダル用のコンテナ要素を作成\n const container = document.getElementById('acms-modal-manager-container');\n if (container !== null) {\n this.#container = container;\n } else {\n this.#container = document.createElement('div');\n this.#container.id = 'acms-modal-manager-container';\n document.body.appendChild(this.#container);\n }\n this.registerEventListeners();\n }\n\n async open(options: ModalManagerOpenOptions = {}) {\n const removeSplash = pending.splash(ACMS.i18n('splash.default'));\n try {\n // コンテンツ取得ユーティリティを使用\n const element = await this.fetchContent(options);\n\n // コンテンツをパース\n const content = this.parseContent(element);\n\n // 状態を更新\n this.setState({\n content,\n modalProps: {\n ...options.modalProps,\n isOpen: true,\n },\n modalHeaderProps: {\n ...options.modalHeaderProps,\n },\n modalBodyProps: {\n ...options.modalBodyProps,\n },\n modalFooterProps: {\n ...options.modalFooterProps,\n },\n });\n } catch (error) {\n console.error('Failed to open modal:', error);\n // エラーを再スローして呼び出し元でハンドリングできるようにする\n throw error;\n } finally {\n removeSplash();\n }\n }\n\n close() {\n // 閉じるトランジション中は size などの modalProps を保持する必要があるため、\n // isOpen のみを切り替える。完全な初期化は reset() を onAfterClose 経由で呼ぶ\n this.setState((prev) => ({\n ...prev,\n modalProps: {\n ...prev.modalProps,\n isOpen: false,\n },\n }));\n }\n\n reset() {\n // 閉じるトランジション完了後に呼ばれることを想定し、コンテンツと props をクリアする\n this.setState({\n content: {\n raw: '',\n },\n modalProps: {\n isOpen: false,\n },\n modalHeaderProps: {},\n modalBodyProps: {},\n modalFooterProps: {},\n });\n }\n\n private emit = () => {\n this.listeners.forEach((cb) => cb());\n };\n\n // クリーンアップ用メソッド\n destroy() {\n this.unregisterEventListeners();\n if (this.#container) {\n this.#container.remove();\n this.#container = null;\n }\n }\n\n private async handleSubmit(event: SubmitEvent) {\n if (event.defaultPrevented) {\n // バリデーターがフォームの送信をキャンセルしている場合は、何もしない\n return;\n }\n event.preventDefault();\n const form = event.target as HTMLFormElement;\n const { submitter } = event;\n const formData = new FormData(form, submitter);\n\n let isSuccess = false;\n\n const removeSplash = pending.splash(ACMS.i18n('splash.save'));\n try {\n const response = await fetch(form.action, {\n method: form.method,\n body: formData,\n headers: {\n 'X-Requested-With': 'XMLHttpRequest',\n 'X-Csrf-Token': window.csrfToken || '',\n },\n });\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const html = await response.text();\n isSuccess = this.checkSuccessFromContent(html);\n const content = this.parseContent(patchFormActions(html, form.action));\n this.setState((prev) => ({\n ...prev,\n content,\n }));\n } catch (error) {\n console.error('Failed to submit form:', error);\n } finally {\n removeSplash();\n if (isSuccess) {\n setTimeout(() => {\n // 成功メッセージの表示が完了してから確認ダイアログを表示\n if (confirm(ACMS.i18n('modal.reload'))) {\n window.location.reload();\n }\n }, 500);\n }\n }\n }\n\n private checkSuccessFromContent(html: string): boolean {\n const parser = new DOMParser();\n const doc = parser.parseFromString(html, 'text/html');\n\n // 成功メッセージのセレクタを確認\n const successSelectors = [\n '.acms-admin-alert-info', // 成功アラート\n '.acms-admin-alert-success', // 成功アラート\n '[data-status=\"success\"]', // 成功ステータス\n '.success-message', // 成功メッセージ\n ];\n\n // エラーメッセージのセレクタを確認\n const errorSelectors = [\n '.acms-admin-alert-danger', // エラーアラート\n '.acms-admin-alert-error', // エラーアラート\n '[data-status=\"error\"]', // エラーステータス\n '.error-message', // エラーメッセージ\n ];\n\n // エラーメッセージが存在する場合は失敗\n for (const selector of errorSelectors) {\n if (doc.querySelector(selector)) {\n return false;\n }\n }\n\n // 成功メッセージが存在する場合は成功\n for (const selector of successSelectors) {\n if (doc.querySelector(selector)) {\n return true;\n }\n }\n\n // デフォルトは成功とみなす(エラーがない場合)\n return true;\n }\n\n private registerEventListeners() {\n this.#container?.addEventListener('submit', this.handleSubmit);\n }\n\n private unregisterEventListeners() {\n this.#container?.removeEventListener('submit', this.handleSubmit);\n }\n\n private parseContent(htmlContent: string) {\n const { header, body, footer } = this.config;\n const result: Content = {\n raw: htmlContent,\n };\n\n if (!header && !body && !footer) {\n // セレクタが指定されていない場合は、全体をボディとして扱う\n result.body = htmlContent;\n return result;\n }\n\n // 一時的なDOM要素を作成してHTMLを解析\n const div = document.createElement('div');\n div.innerHTML = htmlContent;\n\n // ヘッダーセクションの抽出\n if (header) {\n const headerElement = div.querySelector(header);\n if (headerElement) {\n result.header = headerElement.innerHTML;\n }\n }\n\n // ボディセクションの抽出\n if (body) {\n const bodyElement = div.querySelector(body);\n if (bodyElement) {\n result.body = bodyElement.innerHTML;\n }\n }\n\n // フッターセクションの抽出\n if (footer) {\n const footerElement = div.querySelector(footer);\n if (footerElement) {\n result.footer = footerElement.innerHTML;\n }\n }\n\n // セレクタが指定されていない場合は、対応するセクションに全体のコンテンツを設定\n if (!result.header && !result.body && !result.footer) {\n result.body = htmlContent;\n }\n\n return result;\n }\n\n private fetchContent(options: ModalManagerOpenOptions) {\n const { selector, url } = options;\n\n if (selector) {\n return getContentFromSelector(selector);\n }\n\n if (url) {\n if (!this.securityManager.validateUrl(url)) {\n throw new Error(`URL is not allowed: ${url}`);\n }\n return fetchContentFromUrl(url);\n }\n\n throw new Error('No selector or URL provided');\n }\n}\n\nexport default ModalManager;\n"],"mappings":"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","names":["useRef","useRef"]}
1
+ {"version":3,"sources":["../src/modal-controller.tsx","../src/store/use-modal-manager.ts","../src/store/modal-manager.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { emit } from '@ablogcms/events';\nimport useUpdateEffect from '@ablogcms/react-hooks/use-update-effect';\nimport { datasetToProps } from '@ablogcms/react-utils';\nimport Modal, { ModalBody, ModalFooter, ModalHeader } from '@ablogcms/components/modal';\nimport useModalManager, { UseModalManagerOptions } from './store/use-modal-manager';\n\ntype ModalProps = React.ComponentProps<typeof Modal>;\ntype ModalHeaderProps = React.ComponentProps<typeof ModalHeader>;\ntype ModalBodyProps = React.ComponentProps<typeof ModalBody>;\ntype ModalFooterProps = React.ComponentProps<typeof ModalFooter>;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ModalControllerProps extends UseModalManagerOptions {}\n\n/**\n * data-modal-*属性を動的に取得する関数\n */\nfunction parseProps<T extends 'modal' | 'modal-header' | 'modal-body' | 'modal-footer'>(\n element: HTMLElement,\n type: T\n): Partial<\n T extends 'modal'\n ? ModalProps\n : T extends 'modal-header'\n ? ModalHeaderProps\n : T extends 'modal-body'\n ? ModalBodyProps\n : ModalFooterProps\n> {\n const props = datasetToProps(element.dataset, type);\n return props as Partial<\n T extends 'modal'\n ? ModalProps\n : T extends 'modal-header'\n ? ModalHeaderProps\n : T extends 'modal-body'\n ? ModalBodyProps\n : ModalFooterProps\n >;\n}\n\nfunction ModalController({ ...props }: ModalControllerProps) {\n const { manager, state } = useModalManager(props);\n const openStateRef = useRef<boolean>(state.modalProps.isOpen);\n\n const hydrateModalContent = useCallback(() => {\n if (manager.container) {\n ACMS.Dispatch(manager.container);\n void emit('dialog.open.after', { element: manager.container });\n ACMS.dispatchEvent('acmsDialogOpened', manager.container, {\n item: manager.container,\n });\n }\n }, [manager.container]);\n\n const handleAfterOpen = useCallback(() => {\n openStateRef.current = true;\n hydrateModalContent();\n }, [hydrateModalContent]);\n\n const handleAfterClose = useCallback(() => {\n openStateRef.current = false;\n // 閉じるトランジション完了後に state をクリアする\n manager.reset();\n }, [manager]);\n\n useEffect(() => {\n const handleOpen = (event: Event) => {\n if (!(event.target instanceof HTMLElement)) {\n return;\n }\n\n const element = event.target.closest<HTMLElement>(ModalController.openTrigger);\n\n if (!element) {\n return;\n }\n\n const { target: selector, url } = element.dataset;\n\n // Modal用 props とセレクタ情報を data-* から取得\n const modalProps = parseProps(element, 'modal');\n const modalHeaderProps = parseProps(element, 'modal-header');\n const modalBodyProps = parseProps(element, 'modal-body');\n const modalFooterProps = parseProps(element, 'modal-footer');\n\n // ModalManagerでモーダルを開く\n manager.open({\n selector,\n url,\n modalProps,\n modalHeaderProps,\n modalBodyProps,\n modalFooterProps,\n });\n };\n\n const handleClose = (event: Event) => {\n if (!(event.target instanceof HTMLElement)) {\n return;\n }\n\n const element = event.target.closest<HTMLElement>(ModalController.closeTrigger);\n\n if (!element) {\n return;\n }\n\n manager.close();\n };\n document.addEventListener('click', handleOpen);\n document.addEventListener('click', handleClose);\n\n return () => {\n document.removeEventListener('click', handleOpen);\n document.removeEventListener('click', handleClose);\n };\n }, [manager]);\n\n useUpdateEffect(() => {\n if (!openStateRef.current) {\n // モーダルが開いた初回はonAfterOpenでhydrateするためスキップ\n return;\n }\n\n hydrateModalContent();\n // コンテンツが変更されたら組み込みJSを実行\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [state.content.raw]);\n\n if (!manager.container) {\n return null;\n }\n\n return (\n <Modal\n {...state.modalProps}\n onClose={manager.close}\n container={manager.container}\n onAfterOpen={handleAfterOpen}\n onAfterClose={handleAfterClose}\n >\n {state.content.header && (\n <ModalHeader {...state.modalHeaderProps}>\n {}\n <span dangerouslySetInnerHTML={{ __html: state.content.header }} />\n </ModalHeader>\n )}\n <ModalBody {...state.modalBodyProps}>\n {}\n <div dangerouslySetInnerHTML={{ __html: state.content.body || state.content.raw }} />\n </ModalBody>\n {state.content.footer && (\n <ModalFooter {...state.modalFooterProps}>\n {}\n <div dangerouslySetInnerHTML={{ __html: state.content.footer }} />\n </ModalFooter>\n )}\n </Modal>\n );\n}\n\nModalController.openTrigger = '.js-acms-modal-open';\nModalController.closeTrigger = '.js-acms-modal-close';\n\nexport default ModalController;\n","import { useRef, useState, useSyncExternalStore } from 'react';\nimport ModalManager, { ModalManagerOptions } from './modal-manager';\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface UseModalManagerOptions extends Partial<ModalManagerOptions> {}\n\nexport default function useModalManager(options: UseModalManagerOptions = {}) {\n const mostRecentOptions = useRef(options);\n\n mostRecentOptions.current = options;\n const [manager] = useState(() => new ModalManager(mostRecentOptions));\n\n const state = useSyncExternalStore(\n manager.subscribe,\n () => manager.getSnapshot(),\n () => manager.getSnapshot()\n );\n\n ACMS.Library.modal = manager;\n\n return {\n state,\n manager,\n };\n}\n","import { pending } from '@ablogcms/pending';\nimport type Modal from '@ablogcms/components/modal';\nimport type { ModalBody, ModalFooter, ModalHeader } from '@ablogcms/components/modal';\nimport { fetchContentFromUrl, getContentFromSelector, patchFormActions } from './content-fetcher';\nimport SecurityManager, { SecurityConfig } from './security-manager';\n\ntype ModalProps = Omit<React.ComponentProps<typeof Modal>, 'onClose'>;\ntype ModalHeaderProps = Omit<React.ComponentProps<typeof ModalHeader>, 'children'>;\ntype ModalBodyProps = Omit<React.ComponentProps<typeof ModalBody>, 'children'>;\ntype ModalFooterProps = Omit<React.ComponentProps<typeof ModalFooter>, 'children'>;\n\nexport interface ModalManagerState {\n content: Content;\n modalProps: ModalProps;\n modalHeaderProps: ModalHeaderProps;\n modalBodyProps: ModalBodyProps;\n modalFooterProps: ModalFooterProps;\n}\n\nexport interface ModalManagerOptions {\n header: string;\n body: string;\n footer: string;\n security: Partial<SecurityConfig>;\n}\n\ninterface Content {\n header?: string;\n body?: string;\n footer?: string;\n raw: string;\n}\n\ninterface ModalManagerOpenOptions {\n selector?: string;\n url?: string;\n modalProps?: Partial<ModalProps>;\n modalHeaderProps?: Partial<ModalHeaderProps>;\n modalBodyProps?: Partial<ModalBodyProps>;\n modalFooterProps?: Partial<ModalFooterProps>;\n}\n\nconst defaultOptions: ModalManagerOptions = {\n header: '[data-modal-section=\"header\"]',\n body: '[data-modal-section=\"body\"]',\n footer: '[data-modal-section=\"footer\"]',\n security: {},\n};\n\nclass ModalManager {\n private listeners = new Set<() => void>();\n\n private state: ModalManagerState = {\n content: {\n raw: '',\n },\n modalProps: {\n isOpen: false,\n },\n modalHeaderProps: {},\n modalBodyProps: {},\n modalFooterProps: {},\n };\n\n #container: HTMLElement | null = null;\n\n private config: ModalManagerOptions = defaultOptions;\n\n private securityManager: SecurityManager;\n\n constructor(options: React.MutableRefObject<Partial<ModalManagerOptions>>) {\n this.config = { ...defaultOptions, ...options.current };\n this.securityManager = new SecurityManager(this.config.security);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.open = this.open.bind(this);\n this.close = this.close.bind(this);\n this.reset = this.reset.bind(this);\n this.init();\n }\n\n subscribe = (callback: () => void) => {\n this.listeners.add(callback);\n return () => this.listeners.delete(callback);\n };\n\n getSnapshot = () => this.state;\n\n public setState(state: ModalManagerState | ((state: ModalManagerState) => ModalManagerState)) {\n if (typeof state === 'function') {\n this.state = state(this.state);\n } else {\n this.state = state;\n }\n this.emit();\n }\n\n public get content() {\n return this.state.content;\n }\n\n public get modalProps() {\n return this.state.modalProps;\n }\n\n public get modalHeaderProps() {\n return this.state.modalHeaderProps;\n }\n\n public get modalBodyProps() {\n return this.state.modalBodyProps;\n }\n\n public get modalFooterProps() {\n return this.state.modalFooterProps;\n }\n\n public get container() {\n return this.#container;\n }\n\n private init() {\n // モーダル用のコンテナ要素を作成\n const container = document.getElementById('acms-modal-manager-container');\n if (container !== null) {\n this.#container = container;\n } else {\n this.#container = document.createElement('div');\n this.#container.id = 'acms-modal-manager-container';\n document.body.appendChild(this.#container);\n }\n this.registerEventListeners();\n }\n\n async open(options: ModalManagerOpenOptions = {}) {\n const removeSplash = pending.splash(ACMS.i18n('splash.default'));\n try {\n // コンテンツ取得ユーティリティを使用\n const element = await this.fetchContent(options);\n\n // コンテンツをパース\n const content = this.parseContent(element);\n\n // 状態を更新\n this.setState({\n content,\n modalProps: {\n ...options.modalProps,\n isOpen: true,\n },\n modalHeaderProps: {\n ...options.modalHeaderProps,\n },\n modalBodyProps: {\n ...options.modalBodyProps,\n },\n modalFooterProps: {\n ...options.modalFooterProps,\n },\n });\n } catch (error) {\n console.error('Failed to open modal:', error);\n // エラーを再スローして呼び出し元でハンドリングできるようにする\n throw error;\n } finally {\n removeSplash();\n }\n }\n\n close() {\n // 閉じるトランジション中は size などの modalProps を保持する必要があるため、\n // isOpen のみを切り替える。完全な初期化は reset() を onAfterClose 経由で呼ぶ\n this.setState((prev) => ({\n ...prev,\n modalProps: {\n ...prev.modalProps,\n isOpen: false,\n },\n }));\n }\n\n reset() {\n // 閉じるトランジション完了後に呼ばれることを想定し、コンテンツと props をクリアする\n this.setState({\n content: {\n raw: '',\n },\n modalProps: {\n isOpen: false,\n },\n modalHeaderProps: {},\n modalBodyProps: {},\n modalFooterProps: {},\n });\n }\n\n private emit = () => {\n this.listeners.forEach((cb) => cb());\n };\n\n // クリーンアップ用メソッド\n destroy() {\n this.unregisterEventListeners();\n if (this.#container) {\n this.#container.remove();\n this.#container = null;\n }\n }\n\n private async handleSubmit(event: SubmitEvent) {\n if (event.defaultPrevented) {\n // バリデーターがフォームの送信をキャンセルしている場合は、何もしない\n return;\n }\n event.preventDefault();\n const form = event.target as HTMLFormElement;\n const { submitter } = event;\n const formData = new FormData(form, submitter);\n\n let isSuccess = false;\n\n const removeSplash = pending.splash(ACMS.i18n('splash.save'));\n try {\n const response = await fetch(form.action, {\n method: form.method,\n body: formData,\n headers: {\n 'X-Requested-With': 'XMLHttpRequest',\n 'X-Csrf-Token': window.csrfToken || '',\n },\n });\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const html = await response.text();\n isSuccess = this.checkSuccessFromContent(html);\n const content = this.parseContent(patchFormActions(html, form.action));\n this.setState((prev) => ({\n ...prev,\n content,\n }));\n } catch (error) {\n console.error('Failed to submit form:', error);\n } finally {\n removeSplash();\n if (isSuccess) {\n setTimeout(() => {\n // 成功メッセージの表示が完了してから確認ダイアログを表示\n if (confirm(ACMS.i18n('modal.reload'))) {\n window.location.reload();\n }\n }, 500);\n }\n }\n }\n\n private checkSuccessFromContent(html: string): boolean {\n const parser = new DOMParser();\n const doc = parser.parseFromString(html, 'text/html');\n\n // 成功メッセージのセレクタを確認\n const successSelectors = [\n '.acms-admin-alert-info', // 成功アラート\n '.acms-admin-alert-success', // 成功アラート\n '[data-status=\"success\"]', // 成功ステータス\n '.success-message', // 成功メッセージ\n ];\n\n // エラーメッセージのセレクタを確認\n const errorSelectors = [\n '.acms-admin-alert-danger', // エラーアラート\n '.acms-admin-alert-error', // エラーアラート\n '[data-status=\"error\"]', // エラーステータス\n '.error-message', // エラーメッセージ\n ];\n\n // エラーメッセージが存在する場合は失敗\n for (const selector of errorSelectors) {\n if (doc.querySelector(selector)) {\n return false;\n }\n }\n\n // 成功メッセージが存在する場合は成功\n for (const selector of successSelectors) {\n if (doc.querySelector(selector)) {\n return true;\n }\n }\n\n // デフォルトは成功とみなす(エラーがない場合)\n return true;\n }\n\n private registerEventListeners() {\n this.#container?.addEventListener('submit', this.handleSubmit);\n }\n\n private unregisterEventListeners() {\n this.#container?.removeEventListener('submit', this.handleSubmit);\n }\n\n private parseContent(htmlContent: string) {\n const { header, body, footer } = this.config;\n const result: Content = {\n raw: htmlContent,\n };\n\n if (!header && !body && !footer) {\n // セレクタが指定されていない場合は、全体をボディとして扱う\n result.body = htmlContent;\n return result;\n }\n\n // 一時的なDOM要素を作成してHTMLを解析\n const div = document.createElement('div');\n div.innerHTML = htmlContent;\n\n // ヘッダーセクションの抽出\n if (header) {\n const headerElement = div.querySelector(header);\n if (headerElement) {\n result.header = headerElement.innerHTML;\n }\n }\n\n // ボディセクションの抽出\n if (body) {\n const bodyElement = div.querySelector(body);\n if (bodyElement) {\n result.body = bodyElement.innerHTML;\n }\n }\n\n // フッターセクションの抽出\n if (footer) {\n const footerElement = div.querySelector(footer);\n if (footerElement) {\n result.footer = footerElement.innerHTML;\n }\n }\n\n // セレクタが指定されていない場合は、対応するセクションに全体のコンテンツを設定\n if (!result.header && !result.body && !result.footer) {\n result.body = htmlContent;\n }\n\n return result;\n }\n\n private fetchContent(options: ModalManagerOpenOptions) {\n const { selector, url } = options;\n\n if (selector) {\n return getContentFromSelector(selector);\n }\n\n if (url) {\n if (!this.securityManager.validateUrl(url)) {\n throw new Error(`URL is not allowed: ${url}`);\n }\n return fetchContentFromUrl(url);\n }\n\n throw new Error('No selector or URL provided');\n }\n}\n\nexport default ModalManager;\n"],"mappings":"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","names":["useRef","useRef"]}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@ablogcms/modal",
3
- "version": "3.2.28-beta.0",
3
+ "version": "3.2.30",
4
4
  "type": "module",
5
- "description": "data-modal-* 属性トリガー監視とモーダル制御",
5
+ "description": "Attribute-driven modal controller for a-blog cms admin UI",
6
6
  "license": "MIT",
7
7
  "homepage": "https://www.a-blogcms.jp",
8
8
  "repository": {
@@ -12,20 +12,21 @@
12
12
  },
13
13
  "sideEffects": false,
14
14
  "dependencies": {
15
- "@ablogcms/components": "3.2.28-beta.0",
16
- "@ablogcms/react-hooks": "3.2.28-beta.0",
17
- "@ablogcms/utils": "3.2.28-beta.0",
18
- "@ablogcms/pending": "3.2.28-beta.0",
19
- "@ablogcms/types": "3.2.28-beta.0",
20
- "@ablogcms/events": "3.2.28-beta.0"
15
+ "@ablogcms/events": "3.2.30",
16
+ "@ablogcms/pending": "3.2.30",
17
+ "@ablogcms/react-hooks": "3.2.30",
18
+ "@ablogcms/components": "3.2.30",
19
+ "@ablogcms/react-utils": "3.2.30",
20
+ "@ablogcms/types": "3.2.30",
21
+ "@ablogcms/utils": "3.2.30"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@testing-library/react": "^16.3.2",
24
- "vitest": "^4.1.9"
25
+ "vitest": "^4.1.10"
25
26
  },
26
27
  "peerDependencies": {
27
28
  "react": "^19",
28
- "@types/react": "^19.2.17"
29
+ "@types/react": "^19.2.18"
29
30
  },
30
31
  "peerDependenciesMeta": {
31
32
  "@types/react": {