# Picture Box

Picture Box 是獨立的 daisyUI 擴充元件，用來呈現圖片的目標尺寸、比例、預覽與生命週期狀態。它可以單獨搭配原生 `input[type="file"]`，也可以透過選配 Adapter 與 FilePond 整合。

## 設計邊界

- Picture Box Core 負責尺規、圖片 viewport、預覽、替換、移除與狀態呈現。
- FilePond Adapter 負責將 FilePond 的檔案選擇、驗證、預覽、進度與伺服器事件映射到 Picture Box。
- 上傳 API、權限、檔案儲存與資料庫更新由使用元件的專案負責。
- Picture Box 不修改 FilePond 的內部 JavaScript，也不把 FilePond 打包進 Core。

## 載入檔案

原生模式只需要 UI Components：

```html
<link rel="stylesheet" href="/assets/vendor/ui-components/standalone.css">
<script src="/assets/vendor/ui-components/picture-box.js" defer></script>
```

## 原生模式

```html
<div
   data-picture-box
   data-width="1200"
   data-height="800"
   data-prompt="選擇產品圖片"
   data-description="點擊、按 Enter，或將圖片拖放到這裡"
>
   <input type="file" name="product_picture" accept="image/*">
</div>
```

頁面完成載入後，`[data-picture-box]` 會自動初始化。也可以手動建立：

```javascript
const oPictureBox = UicPictureBox.create(document.querySelector('#product-picture'));
```

## 顯示既有圖片

```html
<div
   data-picture-box
   data-width="1600"
   data-height="900"
   data-src="/uploads/current-image.jpg"
   data-alt="目前的首頁橫幅"
   data-prompt="替換目前圖片"
>
   <input type="file" name="banner_picture" accept="image/*">
</div>
```

`data-src` 只負責顯示現有圖片；若使用者沒有選擇新檔案，表單中的 file input 仍然是空值。

## 尺寸驗證模式

使用 `data-dimension-mode` 控制 Core 的前端尺寸檢查：

| 值 | 行為 |
| --- | --- |
| `guide` | 預設值，只顯示目標尺寸，不阻擋圖片 |
| `minimum` | 圖片寬、高都不得小於目標尺寸 |
| `exact` | 圖片寬、高必須完全等於目標尺寸 |
| `ratio` | 圖片比例必須接近目標比例 |

比例模式預設容許 2% 誤差，可用 `data-aspect-tolerance="0.01"` 調整。

前端驗證只能改善使用體驗；伺服器仍必須重新驗證檔案類型、大小、尺寸與內容。

## Core API

```javascript
const oPictureBox = UicPictureBox.create(oRoot);

oPictureBox.setState('loading', { text: '正在讀取圖片…' });
oPictureBox.setState('uploading', { text: '正在上傳圖片…' });
oPictureBox.setProgress(0.68);
oPictureBox.setState('success', { text: '圖片上傳完成。' });
oPictureBox.setError('圖片尺寸不符。');
oPictureBox.clear();
```

支援狀態：

- `empty`
- `loading`
- `preview`
- `uploading`
- `success`
- `error`

其他方法與屬性：

| API | 說明 |
| --- | --- |
| `element` | Picture Box 根元素 |
| `input` | 原生 file input |
| `target` | `{ width, height }` 目標尺寸 |
| `setImage(src, options)` | 以 URL 顯示圖片，回傳 Promise |
| `setActualSize(width, height, bytes)` | 更新實際尺寸與檔案大小 |
| `useExternalControl()` | 將輸入控制權交給外部 Adapter |
| `releaseExternalControl()` | 還原原生模式 |
| `destroy()` | 移除元件行為並還原 file input |

## 自訂事件

事件會從 Picture Box 根元素向上冒泡：

```javascript
oRoot.addEventListener('uic-picture-box:change', (oEvent) => {
   const oFile = oEvent.detail.file;
   const iWidth = oEvent.detail.width;
   const iHeight = oEvent.detail.height;
});
```

| 事件 | 說明 |
| --- | --- |
| `uic-picture-box:change` | 原生模式選取並成功讀取圖片 |
| `uic-picture-box:remove` | 使用者移除圖片 |
| `uic-picture-box:error` | 圖片讀取或驗證失敗 |
| `uic-picture-box:statechange` | 元件狀態改變 |
| `uic-picture-box:filepond-ready` | FilePond Adapter 建立完成 |

## FilePond Adapter

Adapter 針對 FilePond 4 穩定版設計。先安裝選配相依套件：

```shell
npm install filepond filepond-plugin-image-preview \
   filepond-plugin-file-validate-type \
   filepond-plugin-file-validate-size \
   filepond-plugin-image-validate-size \
   filepond-plugin-image-exif-orientation
```

載入 FilePond 與 Adapter：

```html
<link rel="stylesheet" href="/node_modules/filepond/dist/filepond.min.css">
<link rel="stylesheet" href="/node_modules/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css">
<link rel="stylesheet" href="/assets/vendor/ui-components/standalone.css">

<script src="/node_modules/filepond/dist/filepond.min.js"></script>
<script src="/assets/vendor/ui-components/picture-box.js"></script>
<script src="/assets/vendor/ui-components/picture-box-filepond.js"></script>
```

註冊需要的 FilePond 插件後建立 Adapter：

```javascript
FilePond.registerPlugin(
   FilePondPluginImagePreview,
   FilePondPluginFileValidateType,
   FilePondPluginFileValidateSize,
   FilePondPluginImageValidateSize,
   FilePondPluginImageExifOrientation
);

const oAdapter = UicPictureBoxFilePond.create(oRoot, {
   acceptedFileTypes: ['image/jpeg', 'image/png', 'image/webp'],
   maxFileSize: '5MB',
   server: {
      process: '/api/uploads/process',
      revert: '/api/uploads/revert',
      restore: '/api/uploads/restore?id=',
      load: '/api/uploads/load?id='
   }
});
```

Adapter 使用 FilePond 的公開事件同步下列狀態：

- `addfilestart` → `loading`
- `addfile` → `preview`
- `processfilestart` → `uploading`
- `processfileprogress` → 上傳進度
- `processfile` → `success` 或 `error`
- `removefile` → `empty`
- `processfilerevert` → `preview`

應由各專案提供 `server` 設定；UI Components 不假設任何後端網址或認證方式。

## 主題與客製化

Picture Box 使用 daisyUI 主題變數：

- `--color-base-100`、`--color-base-300`：viewport、棋盤與尺規表面
- `--color-base-content`：文字、刻度與框線
- `--color-primary`：主要選擇動作與進度
- `--color-success`、`--color-error`：完成與錯誤狀態
- `--radius-box`、`--radius-field`：外框與操作按鈕圓角

可在個別元件上覆寫：

```html
<div
   data-picture-box
   data-width="800"
   data-height="600"
   style="--uic-picture-box-fit: contain; --uic-picture-box-grid-size: 1rem"
>
   <input type="file" accept="image/*">
</div>
```

## 無障礙與操作

- 原生模式使用真正的 button 與 file input。
- 支援鍵盤選取、滑鼠點擊與拖放。
- 狀態文字透過 `aria-live` 更新。
- 上傳期間設定 `aria-busy="true"`。
- 焦點樣式在亮色與深色主題都保持可見。
- `prefers-reduced-motion` 會縮短非必要動畫。
- `readonly` 與 `disabled` 模式會停止選取與移除操作。
