"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var bupan; (function (bupan) { // ... (既存のインターフェースとAreaUtil, ImageBox, ImageEditBoxの定義) ... class ImageSelector { constructor($root) { this.template_id = ""; this.imageBoxes = []; // 初期化 this.selectedImageBox = null; this.$addBox = null; this.sortableInstance = null; // SortableJS インスタンスを保持 this.$root = $root; this.init(); // this.template_id = `temp-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`; } init() { // SortableJSを初期化 this.sortableInstance = new Sortable(this.$root, { delay: 150, delayOnTouchOnly: true, handle: ".drag-handle-overlay", animation: 150, filter: '.add-box', onEnd: () => { // ドラッグ&ドロップが終了したときに呼び出される // DOMの順序はSortableJSが自動的に更新してくれるので、 // 内部のimageBoxes配列をDOMの順序に合わせる this.syncImageBox(); // 選択中のサムネイルが移動した場合に、選択状態を再適用 this.refreshSelected(); this.onSortChanged(); }, // Add this option to delay touch start for better scrolling on mobile touchStartThreshold: 10, // px }); // Add the add_box back to the DOM before adding new items this.$addBox = document.getElementById('add_box'); if (this.$addBox) { this.$root.appendChild(this.$addBox); } } /** * 複数のテンプレート画像データをImageSelectorに追加します。 * @param t_template_images - テンプレート画像の配列。 */ loadImageAll(t_template_images) { return __awaiter(this, void 0, void 0, function* () { for (const t_template_image of t_template_images) { const rootElement = document.createElement("div"); rootElement.classList.add("thumb-box"); // rootElement.draggable = true; // SortableJSがdraggable属性を管理するので不要 this.$root.appendChild(rootElement); const newImageBox = new bupan.ImageBox(rootElement); newImageBox.image_idx = t_template_image.image_idx; this.imageBoxes.push(newImageBox); // Base64データからBlobを作成してloadImageに渡す // またはBase64文字列を直接loadImageに渡す (ImageBox.loadImageが対応済み) // alert(); yield newImageBox.loadImage(t_template_image.base64, { x: t_template_image.x, y: t_template_image.y, width: t_template_image.width, height: t_template_image.height }); rootElement.addEventListener('click', () => { this.selectImageBox(newImageBox, true); }); } if (this.imageBoxes.length > 0) { this.selectImageBox(this.imageBoxes[0]); // 最初の画像を自動選択 } if (this.$addBox) { this.$root.appendChild(this.$addBox); } //syncImageBox呼び出し不可 データを再アップロードしてしまう。 }); } /** * 新しい画像をImageSelectorに追加します。 * @param base64 - 画像のBase64データ。 */ addImage(base64) { return __awaiter(this, void 0, void 0, function* () { const rootElement = document.createElement("div"); rootElement.classList.add("thumb-box"); // rootElement.draggable = true; // SortableJSがdraggable属性を管理するので不要 this.$root.appendChild(rootElement); const newImageBox = new bupan.ImageBox(rootElement); this.imageBoxes.push(newImageBox); yield newImageBox.loadImage(base64); // 画像をロード // クリックイベントリスナーを設定 rootElement.addEventListener('click', () => { this.selectImageBox(newImageBox, true); }); // 新規追加された画像を自動的に選択状態にする this.selectImageBox(newImageBox); this.syncImageBox(); }); } refreshSelected() { this.imageBoxes.forEach(box => { box.$root.classList.remove("active-thumb-box"); }); if (this.selectedImageBox != null) { this.selectedImageBox.$root.classList.add("active-thumb-box"); } } /** * 現在選択されている画像を削除します。 */ removeSelectedImage() { if (!this.selectedImageBox) { console.warn("削除する画像が選択されていません。"); return; } // DOMから要素を削除 (SortableJSは自動でDOMを更新しますが、明示的に削除) if (this.selectedImageBox.$root && this.selectedImageBox.$root.parentNode) { this.selectedImageBox.$root.parentNode.removeChild(this.selectedImageBox.$root); } // 配列から削除 this.imageBoxes = this.imageBoxes.filter(box => box !== this.selectedImageBox); // 選択状態を解除し、可能であれば次の画像を選択 if (this.imageBoxes.length > 0) { this.selectImageBox(this.imageBoxes[0], false); // 最初の画像を選択 } else { this.selectImageBox(null, false); // 最初の画像を選択 } // DOMとimageBoxesの同期が必要な場合は、updateImageBoxOrderをここで呼ぶ // (ただしSortableJSのonEndイベントは削除操作では発火しないため、必要に応じて) this.syncImageBox(); } /** * 画像ボックスを選択状態にします。 * @param imageBox - 選択するImageBoxインスタンス。 */ selectImageBox(imageBox, isUserSelected = false) { // if (this.selectedImageBox === imageBox) { // return; // すでに選択されている場合は何もしない // } this.selectedImageBox = imageBox; this.refreshSelected(); this.onSelectChanged({ isUserSelected: isUserSelected }); // 選択変更を通知 } /** * 選択が変更された際に呼び出されるコールバック。 * 必要に応じてオーバーライドされます。 */ onSelectChanged(p0) { // このメソッドは、外部(例えばImageEditBox)からオーバーライドされることを想定しています。 // 例えば、選択された画像に合わせてImageEditBoxの表示を更新するロジックをここに書くことができます。 } onSortChanged() { // このメソッドは、外部(例えばImageEditBox)からオーバーライドされることを想定しています。 // 例えば、選択された画像に合わせてImageEditBoxの表示を更新するロジックをここに書くことができます。 } toData() { let sort_idx = 0; return this.imageBoxes.map(imageBox => { // 画像データはbase64で保存することを想定 // ImageBoxがBase64文字列を保持していない場合、ここで生成する必要があります // 今回のImageBoxには imageUrl があるのでそれを利用 let base64Data; if (typeof imageBox.imageUrl === 'string' && imageBox.imageUrl.startsWith('data:')) { base64Data = imageBox.imageUrl; } else if (imageBox.$image && imageBox.$image.src.startsWith('data:')) { base64Data = imageBox.$image.src; // Canvasから生成したBase64を使う } else { // Blob URLなどの場合は、ここにBase64への変換ロジックが必要になります。 // 簡単のため、ここでは警告を出すか、スキップします。 console.warn("Base64形式でない画像URLはバックアップできません:", imageBox.imageUrl); base64Data = ""; // または適切なデフォルト値 } sort_idx++; return { template_id: this.template_id, image_idx: imageBox.image_idx, sort_idx: sort_idx, // image_name: this.template_id + "-" + idx + ".png", // 仮のファイル名 base64: base64Data, x: imageBox.imageViewArea.x, y: imageBox.imageViewArea.y, width: imageBox.imageViewArea.width, height: imageBox.imageViewArea.height }; }); } // /** // * 現在のImageSelectorの状態をバックアップします。 // * LocalStorageにt_template_image形式で保存します。 // */ // public upload(): void { // try { // localStorage.setItem('imageSelectorBackup', JSON.stringify(this.toData() )); // console.log("ImageSelectorのデータをバックアップしました。"); // } catch (e) { // console.error("Local Storageへの保存に失敗しました。", e); // } // } // /** // * LocalStorageからImageSelectorの状態を復元します。 // * @returns 復元が成功したかどうか。 // */ // public async recovery(): Promise { // try { // const savedData = localStorage.getItem('imageSelectorBackup'); // if (!savedData) { // console.log("バックアップデータが見つかりませんでした。"); // return false; // } // const t_template_images: t_template_image[] = JSON.parse(savedData); // // 既存の画像をすべてクリア // this.imageBoxes.forEach(box => { // if (box.$root && box.$root.parentNode) { // box.$root.parentNode.removeChild(box.$root); // } // }); // this.imageBoxes = []; // this.selectedImageBox = null; // // SortableJSインスタンスを破棄し、再初期化する準備 // if (this.sortableInstance) { // this.sortableInstance.destroy(); // this.sortableInstance = null; // } // await this.loadImageAll(t_template_images); // 復元データをロード // console.log("ImageSelectorのデータを復元しました。"); // // SortableJSを再初期化 // this.init(); // init() メソッド内で SortableJS が再初期化されることを想定 // return true; // } catch (e) { // console.error("Local Storageからの復元に失敗しました。", e); // return false; // } // } // --- Drag and Drop Methods (SortableJSが処理するため、これらは不要になります) --- // private handleDragStart(e: DragEvent): void { ... } // private handleDragOver(e: DragEvent): void { ... } // private handleDrop(e: DragEvent): void { ... } // private handleDragEnter(e: DragEvent): void { ... } // private handleDragLeave(e: DragEvent): void { ... } // private handleDragEnd(e: DragEvent): void { ... } syncImageBox() { const orderedElements = Array.from(this.$root.children); // childrenで直接の子要素を取得 this.imageBoxes = orderedElements .filter(el => el.classList.contains('thumb-box') && el.id !== 'add_box') // 'thumb-box'クラスを持ち、'add_box'でない要素のみを対象 .map(el => { // DOM要素に対応する ImageBox インスタンスを見つける return this.imageBoxes.find(box => box.$root === el); }) .filter((box) => box !== undefined); // undefinedを除外 if (this.$addBox) { this.$root.appendChild(this.$addBox); } this.onDataUpdated(this.toData()); } onDataUpdated(data) { // try { // localStorage.setItem('imageSelectorBackup', JSON.stringify(this.toData() )); // console.log("ImageSelectorのデータをバックアップしました。"); // } catch (e) { // console.error("Local Storageへの保存に失敗しました。", e); // } } } bupan.ImageSelector = ImageSelector; })(bupan || (bupan = {}));