@gemboss/gem-framework 0.18.15 → 0.19.0
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/README.md +106 -1
- package/dist/SOURCE.json +3 -3
- package/dist/gem-framework.css +23 -1
- package/dist/gem-runtime.js +473 -55
- package/dist/index.js +2062 -44
- package/dist/motion/core.d.ts +208 -0
- package/dist/motion/core.js +1 -0
- package/dist/motion/orbit.d.ts +4 -0
- package/dist/motion/orbit.js +1 -0
- package/dist/motion/quick-view.d.ts +4 -0
- package/dist/motion/quick-view.js +1 -0
- package/dist/motion/rail.d.ts +5 -0
- package/dist/motion/rail.js +1 -0
- package/dist/orbit-base.css +39 -0
- package/dist/plugins/before_after.js +1 -1
- package/dist/plugins/bundle.d.ts +9 -0
- package/dist/plugins/bundle.js +394 -0
- package/dist/plugins/carousel.js +1 -1
- package/dist/plugins/cart-drawer.js +1 -1
- package/dist/plugins/context-adapter.js +1 -1
- package/dist/plugins/countdown.js +1 -1
- package/dist/plugins/disclosure.js +1 -1
- package/dist/plugins/hero-slideshow.js +1 -1
- package/dist/plugins/img-in.js +1 -1
- package/dist/plugins/localization.js +1 -1
- package/dist/plugins/media-gallery.js +1 -1
- package/dist/plugins/mobile-menu.js +1 -1
- package/dist/plugins/offer-engine.js +1 -1
- package/dist/plugins/orbit.d.ts +9 -0
- package/dist/plugins/orbit.js +1 -0
- package/dist/plugins/predictive-search.js +1 -1
- package/dist/plugins/promo-popup.js +1 -1
- package/dist/plugins/quantity-input.js +1 -1
- package/dist/plugins/quick-view.js +1 -69
- package/dist/plugins/rail.d.ts +9 -0
- package/dist/plugins/rail.js +1 -0
- package/dist/plugins/recently-viewed.js +1 -1
- package/dist/plugins/reveal-on-scroll.js +1 -1
- package/dist/plugins/reviews.js +2 -2
- package/dist/plugins/runtime-slot-loader.js +1 -1
- package/dist/plugins/seasonal-effects.js +1 -1
- package/dist/plugins/size-guide.d.ts +9 -0
- package/dist/plugins/size-guide.js +38 -0
- package/dist/plugins/sticky-atc.js +1 -1
- package/dist/plugins/tabs.js +1 -1
- package/dist/plugins/variant-selects.js +1 -1
- package/dist/popup-shell.css +15 -0
- package/dist/primitives.css +4 -1
- package/dist/rail-base.css +48 -0
- package/dist/runtime-core.d.ts +2 -0
- package/dist/runtime-core.js +26 -15
- package/dist/section-gate.js +2754 -37
- package/dist/tokens.css +4 -0
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ gem-framework của GemTheme đóng thành package, cho các repo **ngoài** mon
|
|
|
9
9
|
| `…/sections` | Các hàm `headless.tsx` được import: `elementOverrides`, `imageSrcSet`, `subscriptionPlans`, `defaultPlan`; từ 0.15.0 thêm bộ đọc nhấn trong câu `inlineMarkRuns`, `phraseMarkRuns`, `markClassName`, `inlineMarkText`, `safeMarkHref`, `MARK_KINDS`; từ 0.16.0 thêm giá thẻ sản phẩm `cardPrice` (`card-price.ts`: nhiều giá thì "From" + giá thấp nhất, Sale chỉ khi biến thể mua được đang giảm, hết hàng, biến thể mà giá nói tới). `product-card-grid` quick-view-grid và featured từ section-library 1.39.6 import chúng: đồng bộ hai section đó khi đã lên 0.16.0 | `packages/gem-sections/src` |
|
|
10
10
|
| `…/section-gate` | `validateSectionArtifacts`: ĐÚNG bộ kiểm section gemtheme dùng khi duyệt. Từ 0.3.0 thêm ba cổng của thư viện cho MỘT section: `layoutKnobCheck` (+ `LAYOUT_ATTRS`), `slotContractDrift` (+ `slotsReadBy`, `slotSourceFromElementTree`), `cssPairingOrphans` (+ `classesStyledBy`, truyền CSS từ `createGemThemeCss`). Từ 0.3.2 thêm hai cổng pháp lý: `fabricatedProofInSource` / `fabricatedProofInSchema` (điểm, số lượt, người, lời hứa bịa) và `inventedTestimonyIn` (hằng DEMO_*_QUOTE/REVIEW… làm giá trị khi thiếu dữ liệu). Từ 0.3.3 hai cổng này đọc được tiếng Việt ("4,8/5", "đã bán", "miễn phí vận chuyển", "199k", cả chữ NFD), tra giá trị của `DEMO.<key>` ở mọi chỗ đọc nó (`??`, `\|\|`, toán tử ba ngôi, dòng bị Prettier ngắt), và bỏ qua số lượng hiển thị (`reviews_to_show`…). Nâng từ 0.3.2 lên có thể làm đỏ section trước đó xanh: đó là lời khẳng định nằm trong DEMO. Từ 0.3.5 bắt thêm chữ ký người Việt (tên kèm nơi chốn như "Chị Lan, Hà Nội", hoặc họ + Thị/Văn như "Nguyễn Thị Hoa"; tiêu đề viết hoa từng chữ thì không), điểm sao ("4,9 ★"), `rating: 4.9` / `reviewCount: "1.284"` trong mảng sản phẩm mẫu, và hằng demo ở nhánh else của toán tử ba ngôi (`bound ? [] : DEMO_REVIEWS`). Mỗi thứ bịa chỉ báo MỘT lần: tại dòng DEMO nếu dòng đó tự lộ, không thì tại chỗ đọc. "đồng hồ" không còn bị đọc là giá. Từ 0.3.6 bắt lời hứa có thời hạn đứng sau từ khoá ("Dùng thử 30 ngày", "Bảo hành 12 tháng", "Nhận trong 2–4 ngày", "Giao 2 giờ") và "hoàn tiền" không kèm số; "Chính sách hoàn tiền" vẫn là nhãn. Từ 0.3.7 câu hỏi (kết thúc bằng "?") không bao giờ là lời hứa, "không hợp lệ"/số bước/nhịp giao không bị báo, và bắt thêm "trong vòng N ngày", "Giao hàng 2–4 ngày", "Bảo hành 1 đổi 1…", "Try it free for 30 days", "Ships in 24h". Từ 0.3.8 xuất thêm `claimIn` (câu hỏi "một chuỗi này có phải lời khẳng định, người bịa, hay lời hứa?"). Từ 0.3.9 bắt "cancel anytime", "guaranteed delivery", "huỷ bất cứ lúc nào" (chính sách huỷ gói và giao hàng). Từ 0.3.10 lời từ chối ("cannot be canceled", "Không thể hủy") không bị báo, "giao dịch/giao diện" không phải giao hàng, "Delivery not guaranteed" là lời miễn trừ, một chuỗi chỉ gồm con số viết tắt ("199k", "12k+") mang nhãn trung tính "hardcoded figure" (không phân biệt được giá với số đếm), và `stringLiteralsIn(line)` thay cho hằng `STRING_LITERAL` (đã gỡ: một regex `/g` dùng chung rò `lastIndex`; 0 chỗ dùng ở gemboss/gemfactory/gemsite). Từ 0.3.11 "Huỷ bỏ lọc…" và "Hủy đơn, gọi chúng tôi bất cứ lúc nào" không bị đọc là chính sách huỷ; "huỷ đăng ký" là bỏ nhận tin (như "Unsubscribe anytime"); `SOURCE.json` ghi `gemtheme_dirty`, và `npm pack` từ chối khi ĐẦU VÀO của gói (`packages/`, gói này trừ `package.json`, `scripts/lib/`) có thay đổi chưa commit hoặc git không trả lời được. Từ 0.3.12 bắt lời hứa hoàn tiền không có chữ "hoàn tiền" liền nhau ("nhận lại toàn bộ tiền", "Hoàn lại 100% tiền", "Trả lại tiền nếu không hài lòng"); lời từ chối ("Không được hoàn lại tiền") và tiền thừa/thối khi thanh toán không bị bắt Từ 0.4.0: `demoOutsidePreview(tsx)` (chữ mẫu DEMO chỉ hiện trong bản xem trước, tức khi `section.demo === true`; plan xuất bản không bao giờ mang cờ đó) và schema `SectionSpec` khai trường `demo` | `packages/gem-sections/src/section-artifacts.ts`, `section-contract.ts` |
|
|
11
11
|
| `…/settings-schema` | `validateSettingsSchema` | `scripts/lib/settings-schema-contract.mjs` |
|
|
12
|
-
| `…/gem-framework.css`, `…/gem-runtime.js` | CSS token mặc định + runtime mọi plugin, dựng sẵn. `gem-runtime.js` tự chạy khi tải: đăng ký và lên lịch đủ
|
|
12
|
+
| `…/gem-framework.css`, `…/gem-runtime.js` | CSS token mặc định + runtime mọi plugin, dựng sẵn. `gem-runtime.js` tự chạy khi tải: đăng ký và lên lịch đủ 26 plugin | build |
|
|
13
13
|
| `…/tokens.css`, `…/primitives.css`, `…/sections.css`, `…/popup-shell.css` | Từ 0.8.0: `gem-framework.css` cắt ba (từ 0.8.3 cắt bốn), nối lại theo đúng thứ tự đó thì bằng `gem-framework.css` từng byte (build dừng nếu không bằng). `tokens.css`: biến `:root` + núm style. `primitives.css`: reset, phần tử nền, tiện ích, mọi primitive `.gem-*`. `sections.css`: khung header/footer, lớp section chung, profile theme. `popup-shell.css` (0.8.3): khung popup `.gem-popup-shell`, chỉ trang có popup mới cần tải, đặt sau `primitives.css` và trước `sections.css` (thứ tự nối: tokens, primitives, popup-shell, sections). CSS riêng của từng section (`section-library/**/gem.css`) KHÔNG nằm trong gói | `createGemThemeCssParts` |
|
|
14
14
|
| `…/plugins/<key>` (`…/plugins/<key>.js`), `…/runtime-core` | Từ 0.8.0: mỗi plugin của runtime là một ES module, `export function mount(root = document, options?)` trả về `unmount()`, import không làm gì. Cùng dòng mã với `gem-runtime.js` (một bảng trong `runtime.ts`), chỉ khác: mọi `x.addEventListener(` thành `__gemListen(x, ` để unmount gỡ được. `runtime-core.js` là phần dùng chung: `mountPlugin`, `createGemApi`, `gemApi` | `runtime.ts`, `runtime-modules.ts`, `runtime-core.js` |
|
|
15
15
|
|
|
@@ -93,6 +93,111 @@ Từ 0.8.2 mỗi module có `.d.ts` cạnh nó (`mount(root?: ParentNode | null,
|
|
|
93
93
|
|
|
94
94
|
## Nhật ký thay đổi
|
|
95
95
|
|
|
96
|
+
- **0.19.0** (30/10/2026): **motion engine**, nền tảng chuyển động cho section (Chris chọn 30/10: "Motion engine trong gem-framework",
|
|
97
|
+
như engine bundle #41 và reviews #44), port từ gemmotion. Ba effect đầu tiên: `orbit`, `rail`, `quick-view`.
|
|
98
|
+
- **Nền tảng** (`dist/motion/core.js`, `@gemboss/gem-framework/motion/core`): `defineEffect({ key, hook, settings, renderer, tier,
|
|
99
|
+
fallback, budget, mount })` + `checkEffect` cho cổng; schema settings (kiểu, khoảng, mặc định, nhãn) đọc từ
|
|
100
|
+
`data-gem-<key>-<tên>`, setting `css` soi sang `--gem-<key>-<tên>`; MỘT vòng requestAnimationFrame cho mọi effect của trang
|
|
101
|
+
(ngủ khi không gì chuyển động, ngoài màn hình, tab ẩn; giảm chuyển động thì vẽ ngay, không xin khung nào), chặn khung chậm
|
|
102
|
+
(> 20 ms, 30 khung liền) hạ mọi effect xuống `light`; renderer `dom` có sẵn, `canvas2d` / `video` / `webgl` là interface +
|
|
103
|
+
`defineRenderer(key, () => import(...))` (chunk lười, không nạp được thì giữ fallback tĩnh); bậc `static` / `light` / `full`
|
|
104
|
+
(giảm chuyển động, con trỏ thô, save-data, ≤ 2 GB); dịch vụ dùng chung: `ringPose`, `follow`, `spring`, `throwTarget`,
|
|
105
|
+
`scrollProgress`, kéo / ném, bánh xe, phím mũi tên, FLIP, quan sát hiển thị và kích thước, tải ảnh, `priceText`.
|
|
106
|
+
- **`orbit`** (`[data-gem-orbit]`): vòng nghiêng kéo / ném, nút ‹ ›, ← →, bánh xe ngang; bấm món bên thì quay tới, bấm món trước
|
|
107
|
+
thì mở quick view. **`rail`** (`[data-gem-rail]`): giá treo, món gần con trỏ xoay nghiêng → chính diện (pair / sprite / flip),
|
|
108
|
+
móc đung đưa, dưới 700 px là hàng vuốt. **`quick-view`**: lõi, mọi phần tử mang `data-gem-product` +
|
|
109
|
+
`data-gem-quick-view-trigger` mở nó; ảnh bay ra (FLIP) và về, ‹ › theo host, màu / size, `gem:cart:add
|
|
110
|
+
{ variantId: String(id), quantity: 1 }`; dialog dựng trong gốc host (khung popup-shell). Hợp đồng surface cũ
|
|
111
|
+
(`[data-gem-quick-view]` + `[data-gem-quick-view-surface]`) giữ nguyên trong chunk.
|
|
112
|
+
- Effect NHẬN markup server vẽ, không dựng lại chữ hay ảnh: giá theo `price_text` (cardPrice) nguyên văn, dự phòng `price` (cent)
|
|
113
|
+
+ `data-gem-money-format`.
|
|
114
|
+
- **Lười:** `gem-runtime.js` chỉ mang một dòng stub mỗi plugin + bộ nạp; chunk `plugins/<key>.js` (đã minify) chỉ tải khi trang có
|
|
115
|
+
hook (orbit / rail khi tới gần màn hình, quick-view khi chạm trigger lần đầu hoặc lúc rảnh); hook đến muộn (canvas, editor,
|
|
116
|
+
điều hướng phía client) bắt bằng MutationObserver + `shopify:section:load`. Trang không có hook: 0 byte JS motion.
|
|
117
|
+
- **CSS chia hai**, không dòng nào vào `gem-framework.css`: (a) CSS nền `orbit-base.css` / `rail-base.css` (`GEM_ORBIT_BASE_CSS`,
|
|
118
|
+
`GEM_RAIL_BASE_CSS`) đi cùng section, vẽ tư thế đầu bằng `sin()` / `cos()` nên gắn plugin không xê dịch gì; (b) CSS lúc chạy
|
|
119
|
+
nằm trong chunk, adopt khi mount. Lớp `gem-orbit*`, `gem-rail*`, `gem-turn*`, `gem-quick-view__*` đăng ký nhóm `motion`.
|
|
120
|
+
- `runtime-core.js`: `ResizeObserver` vào sổ, `onUnmount(fn)` cho plugin. Plugin mới `orbit`, `rail` (28 plugin);
|
|
121
|
+
`quick-view` load `idle` (chỉ là stub).
|
|
122
|
+
|
|
123
|
+
- **0.18.22** (29/10/2026): `hasExternalDependency`, luật duy nhất cho `external_dependency`, đọc ở `validateGemFrameworkArtifact`,
|
|
124
|
+
test library-files của gem-sections và `validatePerformanceBudget` của validators (trước đó mỗi nơi chép regex riêng). Miễn:
|
|
125
|
+
URL `www.w3.org` như cũ nhưng chỉ cắt ký tự của chính URL (không còn nuốt phần phía sau), và endpoint share-intent của Facebook,
|
|
126
|
+
X, Pinterest, WhatsApp, Threads tới dấu `?` ở đúng hai dạng của một link chia sẻ: `<a href>`, hoặc giá trị object literal có
|
|
127
|
+
key là tên mạng đó. Mọi chỗ khác (kể cả `.href =`, vì `<link>`/`<use>` tải href) vẫn đỏ; phần sau `?` vẫn bị đọc. Lint bắt
|
|
128
|
+
phụ thuộc lỡ tay, không phải sandbox: URL đi qua biến không được lần theo.
|
|
129
|
+
- **0.18.21** (26/10/2026): bậc chữ `text-xs`, P3 của thang GemPages v7 (Chris duyệt 26/10: H1–H6, P1–P3 + Custom; P1 là `body`,
|
|
130
|
+
P2 là `text-sm`). Số 0.18.20 để lại cho #893 đang mở.
|
|
131
|
+
- `--gem-text-xs: calc(var(--gem-type-base) / (var(--gem-type-scale) * var(--gem-type-scale)))`: base ÷ scale², một bậc dưới
|
|
132
|
+
`--gem-text-sm`, 10,75px ở mặc định (16px, 1.22). Theo `--gem-type-base` / `--gem-type-scale` như cả thang, nên style pack và
|
|
133
|
+
base điện thoại kéo nó theo.
|
|
134
|
+
- Thêm, không đổi tên, không bỏ: không giá trị nào đang vẽ thay đổi. Danh sách `type-step` (framework-names.json) và `steps` của
|
|
135
|
+
`gem_font_size` / `font_size` ở seam 1.0.110 có thêm `text-xs` đứng đầu (nhỏ → lớn).
|
|
136
|
+
- **0.18.19** (22/10/2026): độ dày nét và gạch chân (GemFactory gap #14). `2px` viết tay ở 96 section; đo cả kho 22/10:
|
|
137
|
+
phần lớn là NÉT (border / border-width ~250 chỗ, vòng `box-shadow: inset 0 0 0 2px` của thẻ đã chọn 53, vạch 2px bằng
|
|
138
|
+
height/width 44) và gạch chân (`text-decoration-thickness` 38).
|
|
139
|
+
- `--gem-stroke` (1px) và `--gem-stroke-thick` (2px), nhân `--gem-style-border` như `--gem-border-subtle/strong`: style pack
|
|
140
|
+
làm mảnh / bỏ viền thì nét của section đi theo. Tên riêng (stroke): nhóm token `border` phát `--gem-border-<khoá>`.
|
|
141
|
+
- `--gem-underline-thickness` (2px, KHÔNG theo knob viền: style không viền vẫn giữ gạch chân); `.gem-mark-link:hover` của
|
|
142
|
+
framework đọc biến này (mặc định 2px như cũ).
|
|
143
|
+
- Không đổi: vòng focus vẫn `--gem-focus-ring` / `--gem-focus-offset` (knob viền 0 không được làm mất focus); khoảng 2px
|
|
144
|
+
(padding / gap / margin) là `--gem-space-2xs` có từ #6. Không giá trị nào đang vẽ thay đổi.
|
|
145
|
+
- **0.18.18** (22/10/2026): hình nút của MỘT section (GemFactory gap #69). Chấm khớp nguồn (nút vuông của boticario,
|
|
146
|
+
bellavitaluxe; viên thuốc của threebirdnest…) đỏ chỉ vì nút theo bo của preset, còn section ghi `border-radius` lên
|
|
147
|
+
`.gem-button` thì nút thôi theo preset luôn (cảnh báo `button_token_shadowed` của GemFactory).
|
|
148
|
+
- `data-gem-button-shape="square|soft|pill"` trên gốc section (hoặc trên một nút) đặt `--gem-radius-button` mà nút vốn đọc:
|
|
149
|
+
square 0, soft `--gem-radius-sm`, pill 999px. Không có thuộc tính (hay `preset`) thì preset quyết như trước.
|
|
150
|
+
- Square và pill là HÌNH nên không co giãn; soft là CỠ nên theo thang bo của preset (`--gem-style-radius`). Đo trong
|
|
151
|
+
Chromium: preset nút viên thuốc → section square ra 0, soft 4px; preset nút vuông → section pill ra 999px (trước đó không
|
|
152
|
+
làm được); thang bo 0 → soft 0, pill vẫn 999px.
|
|
153
|
+
- Bộ kiểm framework: biến đặt dưới một selector CHỈ là thuộc tính `[data-gem-…]` (một knob đặt lại token cho phần tử mang nó)
|
|
154
|
+
không còn bị tính là biến nội bộ, nên section vẫn đọc `--gem-radius-button` không bị báo biến lạ. Cùng luật đó `--gem-ambient-amp`
|
|
155
|
+
(đặt dưới `[data-gem-ambient]`) nay là biến công khai. Thuộc tính trên một lớp (`.gem-row[data-…]`) vẫn là nội bộ như cũ.
|
|
156
|
+
|
|
157
|
+
- **0.18.17** (21/10/2026): nút "Bảng size" của buybox mở bảng size ở band KHÁC (GemFactory gap #49). 12 section bảng size
|
|
158
|
+
(product-details/*) đã nghe `window` `gem:size-guide:open`, nhưng behavior.js không được chạm section khác và runtime chưa có
|
|
159
|
+
gì phát sự kiện đó.
|
|
160
|
+
- Plugin `size-guide` (load `idle`: nút phải có trước khi ai bấm): nút mang `data-gem-size-guide-trigger` (rỗng, hoặc id của
|
|
161
|
+
bảng nó mở), gốc bảng size mang `data-gem-size-guide` (rỗng, hoặc id đó). Bấm nút: `gem:size-guide:open`
|
|
162
|
+
`{ sectionId, productId }` lên `window` (chỗ các bảng size nghe) và `api.emit` như mọi plugin; `productId` đọc từ
|
|
163
|
+
`data-product-id` gần nhất.
|
|
164
|
+
- Nút vẽ sẵn `hidden`, plugin chỉ hiện nó khi trang có bảng nó mở; không có runtime hay không có bảng thì nút vẫn ẩn, không
|
|
165
|
+
thành nút bấm không làm gì.
|
|
166
|
+
- `gem-popup-shell` thêm `data-gem-popup-desktop="drawer-end"`: ngăn cao hết màn bám mép cuối dòng trên desktop (phải;
|
|
167
|
+
trái dưới RTL), rộng theo `--gem-popup-drawer-width` (mặc định 640px), trượt vào bằng `--gem-motion-base` /
|
|
168
|
+
`--gem-ease-enter`, tắt trượt khi giảm chuyển động; mobile vẫn takeover / sheet. Trong canvas editor ngăn đứng yên, cao 720px.
|
|
169
|
+
- Bộ kiểm framework biết các biến của popup-shell.css (trước đó section đặt `--gem-popup-drawer-width` sẽ bị báo
|
|
170
|
+
`unknown_gem_css_variable`).
|
|
171
|
+
- Bảng size tìm trên CẢ TRANG (`document.documentElement`): gắn như module lên chính section buybox (cách gemsite chạy behavior,
|
|
172
|
+
vd. `import { mount } from "@gemboss/gem-framework/plugins/size-guide"` trong behavior.js của buybox, như sticky-atc), `document`
|
|
173
|
+
của plugin chỉ thấy bên trong section đó, còn bảng size luôn ở band khác. Nút thì vẫn chỉ trong phần được gắn.
|
|
174
|
+
|
|
175
|
+
- **0.18.16** (20/10/2026): plugin `bundle`, mô hình bộ tự chọn BYOB (GemFactory gap #41). 9 section bundle-builder của GemFactory
|
|
176
|
+
(slot-sidebar, box-size-first, routine-kit, tier-bar, system-list, reward-tiles, silhouette-tray, gift-box, grid-milestone-panel)
|
|
177
|
+
mỗi cái chép một khối "LÕI BYOB" trong behavior.js. Phần chúng VẼ khác nhau nhiều (ô + tab, cỡ hộp + độ đa dạng, thang bậc + bộ
|
|
178
|
+
đếm, kit + bước, hộp quà có nắp), nên engine là mô hình và bộ điều khiển, không vẽ.
|
|
179
|
+
- Section xin engine bằng `data-bb-root`.
|
|
180
|
+
- Engine giữ luật (`data-bb-rules`: min, max, per, repeat, tiers, price_mode, sizes, kits + steps), món đã chọn, cỡ hộp, kit và
|
|
181
|
+
bước, giá. Có ba cách tính: `totals` (theo bậc bộ sẽ đạt), `totalsNow` (theo bậc đang đạt), `unitNow` (giá một món trên thẻ).
|
|
182
|
+
- Engine xử lý mọi thao tác đổi bộ: `[data-bb-add]` / `[data-bb-inc]`, `[data-bb-dec]` / `[data-bb-minus]`, `[data-bb-uncheck]`,
|
|
183
|
+
`[data-bb-remove]`, `[data-bb-clear]`, ô số `input[data-bb-count]`, cách mua `input[data-bb-plan]` (radio hoặc checkbox),
|
|
184
|
+
`input[data-bb-size]` (cỡ hộp, hoặc biến thể của một món khi có `data-bb-variant`), `[data-bb-kit-tab]`, `[data-bb-step-nav]`,
|
|
185
|
+
`[data-bb-cta]` / `[data-bb-skip]` (nút mang `aria-disabled="true"` thì không gửi).
|
|
186
|
+
- Bật theo thuộc tính, không đổi gì của section không khai: cỡ hộp `data-bb-sizes="trim|queue"` (hạ cỡ thì bỏ món cuối, hoặc
|
|
187
|
+
giữ chúng chờ vào lại), bấm lại để bỏ `data-bb-add-toggle`, tiền "1.234đ" `data-bb-money="vi"`, chữ ở `data-bb-labels`; theo
|
|
188
|
+
dữ liệu của món: biến thể theo thẻ (`data-bb-variants` + `data-bb-current`, khoá `<thẻ>~<chỉ số>`), món quà `data-bb-gift`
|
|
189
|
+
(vào giỏ cùng bộ, không tính số món).
|
|
190
|
+
- Giỏ: MỘT `gem:cart:add-many`, mỗi dòng mang `_bundle_id`, `_bundle`, `_bundle_rule`, cộng `sellingPlanId` khi định kỳ. Có cầu giỏ
|
|
191
|
+
gemsite thì chờ `gem:cart:added` / `gem:cart:add-failed`. Phần riêng của section nối vào qua `setBase(giá)` (giá vỏ hộp vào tổng)
|
|
192
|
+
và `setCartHead(fn)` (dòng vỏ hộp đứng đầu, mang thuộc tính nắp / quà).
|
|
193
|
+
- Sau mỗi lần đổi phát `gem:bundle:change` (kèm `action`) trên gốc. API `root.gemBundle` (`state()`, `money`, `fill`, `say`, toán
|
|
194
|
+
bậc / giá, các thao tác) cho `paint()` của section.
|
|
195
|
+
- Đầu ra chung tuỳ chọn `[data-bb-out]` cho trang không tự vẽ.
|
|
196
|
+
|
|
197
|
+
Đối chiếu trong Chromium, 9 section, 1280 và 390: behavior.js gốc so với engine + behavior chỉ còn phần vẽ, cùng chuỗi thao tác
|
|
198
|
+
(thêm, +, −, ✓, ×, xoá hết, ô số, đổi cách mua, tần suất, cỡ hộp, biến thể, chip, kit, bước, nắp, quà, sheet, nút chính + nội
|
|
199
|
+
dung `gem:cart:add-many`). Trạng thái mọi phần tử mang hook, focus và giỏ trùng từng bước (0 khác biệt); engine cố ý làm sai một
|
|
200
|
+
bậc thì cả 9 lệch. Kèm vá của `reviews`: dải ảnh nhỏ trong lightbox đặt `loading` trước `src`.
|
|
96
201
|
- **0.18.15** (20/10/2026): plugin `reviews`, engine danh sách review (GemFactory gap #44). 8 section product-reviews của
|
|
97
202
|
GemFactory mỗi cái mang một bản riêng của cùng một lõi trong behavior.js (14–27 KB mỗi cái); nay lõi đó là MỘT plugin:
|
|
98
203
|
`gem-runtime.js` (load `idle`) hoặc `import { mount } from "@gemboss/gem-framework/plugins/reviews"`. Section xin engine bằng
|
package/dist/SOURCE.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "@gemboss/gem-framework",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"gemtheme_sha": "
|
|
3
|
+
"version": "0.19.0",
|
|
4
|
+
"gemtheme_sha": "4e993003a8085fa5548a91d742930cb783d8168d",
|
|
5
5
|
"gemtheme_dirty": false,
|
|
6
|
-
"framework_version": "0.
|
|
6
|
+
"framework_version": "0.19.0"
|
|
7
7
|
}
|
package/dist/gem-framework.css
CHANGED
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
--gem-morphology-policy-tone: standard;
|
|
63
63
|
--gem-type-base: 1rem;
|
|
64
64
|
--gem-type-scale: 1.22;
|
|
65
|
+
--gem-text-xs: calc(var(--gem-type-base) / (var(--gem-type-scale) * var(--gem-type-scale)));
|
|
65
66
|
--gem-text-sm: calc(var(--gem-type-base) / var(--gem-type-scale));
|
|
66
67
|
--gem-body: var(--gem-type-base);
|
|
67
68
|
--gem-h6: calc(var(--gem-type-base) * var(--gem-type-scale));
|
|
@@ -73,6 +74,9 @@
|
|
|
73
74
|
--gem-focus-ring: 2px solid var(--gem-color-accent);
|
|
74
75
|
--gem-tap-min: 44px;
|
|
75
76
|
--gem-focus-offset: 3px;
|
|
77
|
+
--gem-stroke: calc(1px * var(--gem-style-border, 1));
|
|
78
|
+
--gem-stroke-thick: calc(2px * var(--gem-style-border, 1));
|
|
79
|
+
--gem-underline-thickness: 2px;
|
|
76
80
|
}
|
|
77
81
|
:where(:root [data-gem-section-background='dark'], :root [data-gem-section-background='accent']) {
|
|
78
82
|
--gem-border-subtle: calc(1px * var(--gem-style-border, 1)) solid var(--gem-color-border);
|
|
@@ -195,6 +199,9 @@ a {
|
|
|
195
199
|
cursor: not-allowed;
|
|
196
200
|
opacity: 0.55;
|
|
197
201
|
}
|
|
202
|
+
[data-gem-button-shape='square'] { --gem-radius-button: 0px; }
|
|
203
|
+
[data-gem-button-shape='soft'] { --gem-radius-button: var(--gem-radius-sm); }
|
|
204
|
+
[data-gem-button-shape='pill'] { --gem-radius-button: 999px; }
|
|
198
205
|
.gem-input:disabled, .gem-select:disabled, .gem-textarea:disabled, .gem-quantity-input__button:disabled {
|
|
199
206
|
cursor: not-allowed;
|
|
200
207
|
opacity: 0.6;
|
|
@@ -1160,7 +1167,7 @@ a {
|
|
|
1160
1167
|
:where(.gem-mark--alert) { color: var(--gem-color-danger, #b42318); }
|
|
1161
1168
|
:where(.gem-mark--highlight.gem-mark--accent), :where(.gem-mark--highlight.gem-mark--alert) { color: var(--gem-mark-highlight-ink, #1f1d18); }
|
|
1162
1169
|
.gem-mark-link { color: var(--gem-color-accent); text-decoration-line: underline; text-decoration-thickness: from-font; text-underline-offset: 0.18em; }
|
|
1163
|
-
.gem-mark-link:hover { text-decoration-thickness: 2px; }
|
|
1170
|
+
.gem-mark-link:hover { text-decoration-thickness: var(--gem-underline-thickness, 2px); }
|
|
1164
1171
|
.gem-mark-link.gem-mark--highlight, .gem-mark--highlight .gem-mark-link { color: var(--gem-mark-highlight-ink, #1f1d18); }
|
|
1165
1172
|
@keyframes gem-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(calc(-1 * var(--gem-ambient-amp, 7px))); } }
|
|
1166
1173
|
@keyframes gem-sheen { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
|
|
@@ -1626,6 +1633,19 @@ a {
|
|
|
1626
1633
|
.gem-popup-shell[data-gem-popup-media='background']:is([data-gem-popup-desktop='split-left'], [data-gem-popup-desktop='split-right'], [data-gem-popup-desktop='card']) .gem-popup-shell__close { color: var(--gem-color-text); }
|
|
1627
1634
|
.gem-popup-shell[data-gem-popup-media='background']:is([data-gem-popup-desktop='split-left'], [data-gem-popup-desktop='split-right'], [data-gem-popup-desktop='card']) .gem-popup-shell__media::after { content: none; }
|
|
1628
1635
|
}
|
|
1636
|
+
@keyframes gem-popup-drawer-in { from { translate: calc(100% * var(--gem-popup-drawer-dir, 1)) 0; } }
|
|
1637
|
+
.gem-popup-shell__dialog:dir(rtl) { --gem-popup-drawer-dir: -1; }
|
|
1638
|
+
@media (min-width: 750px) {
|
|
1639
|
+
.gem-popup-shell[data-gem-popup-desktop='drawer-end'] .gem-popup-shell__dialog {
|
|
1640
|
+
width: min(var(--gem-popup-drawer-width, 640px), 100vw); max-width: 100vw; height: 100dvh; max-height: 100dvh;
|
|
1641
|
+
margin-block: 0; margin-inline: auto 0; border-radius: 0;
|
|
1642
|
+
animation: gem-popup-drawer-in var(--gem-motion-base, 180ms) var(--gem-ease-enter, ease-out);
|
|
1643
|
+
}
|
|
1644
|
+
.gem-popup-shell[data-gem-popup-desktop='drawer-end'] .gem-popup-shell__frame { grid-template-columns: 1fr; align-content: start; min-height: 100%; }
|
|
1645
|
+
.gem-popup-shell[data-gem-popup-desktop='drawer-end'] .gem-popup-shell__media { max-height: 240px; }
|
|
1646
|
+
.gem-popup-shell[data-gem-popup-desktop='drawer-end'] .gem-popup-shell__panel { justify-content: flex-start; align-items: stretch; text-align: start; }
|
|
1647
|
+
}
|
|
1648
|
+
@media (prefers-reduced-motion: reduce) { .gem-popup-shell[data-gem-popup-desktop='drawer-end'] .gem-popup-shell__dialog { animation: none; } }
|
|
1629
1649
|
@media (max-width: 749px) {
|
|
1630
1650
|
.gem-popup-shell__dialog { width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh; margin: 0; border-radius: 0; }
|
|
1631
1651
|
.gem-popup-shell[data-gem-popup-mobile='sheet'] .gem-popup-shell__dialog { height: auto; max-height: 88dvh; margin: auto 0 0; border-radius: var(--gem-radius-lg) var(--gem-radius-lg) 0 0; }
|
|
@@ -1649,6 +1669,8 @@ a {
|
|
|
1649
1669
|
@media (min-width: 750px) {
|
|
1650
1670
|
:root:has([data-gem-canvas]) .gem-popup-shell[data-gem-popup-preview='true'][data-gem-popup-desktop='fullscreen'] { padding: 0; }
|
|
1651
1671
|
:root:has([data-gem-canvas]) .gem-popup-shell[data-gem-popup-preview='true'][data-gem-popup-desktop='fullscreen'] .gem-popup-shell__dialog { height: 720px; max-height: none; }
|
|
1672
|
+
:root:has([data-gem-canvas]) .gem-popup-shell[data-gem-popup-preview='true'][data-gem-popup-desktop='drawer-end'] { padding-inline: 0; }
|
|
1673
|
+
:root:has([data-gem-canvas]) .gem-popup-shell[data-gem-popup-preview='true'][data-gem-popup-desktop='drawer-end'] .gem-popup-shell__dialog { height: 720px; max-height: none; animation: none; }
|
|
1652
1674
|
}
|
|
1653
1675
|
@media (max-width: 749px) {
|
|
1654
1676
|
:root:has([data-gem-canvas]) .gem-popup-shell[data-gem-popup-preview='true'] { padding: 0; }
|