porting to the use of penguinui5
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -13,20 +13,20 @@
|
||||
{{ ui::button(label=t(key="new-category", lang=lang | default(value='sk')), href="/admin/catalog/categories/new") }}
|
||||
</div>
|
||||
|
||||
<div class="mt-6 overflow-hidden rounded-radius border border-outline dark:border-outline-dark">
|
||||
<div class="mt-6 {{ ui::table_wrap_cls() }}">
|
||||
{% if categories | length > 0 %}
|
||||
<table class="w-full text-left text-sm">
|
||||
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70">
|
||||
<table class="{{ ui::table_cls() }}">
|
||||
<thead class="{{ ui::thead_cls() }}">
|
||||
<tr>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="name", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="admin-products", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="status", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 text-right font-semibold">{{ t(key="actions", lang=lang | default(value='sk')) }}</th>
|
||||
{{ ui::th(label=t(key="name", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="admin-products", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="status", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="actions", lang=lang | default(value='sk')), align="text-right") }}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline dark:divide-outline-dark">
|
||||
<tbody class="{{ ui::tbody_cls() }}">
|
||||
{% for row in categories %}
|
||||
<tr class="hover:bg-surface-alt dark:hover:bg-surface-dark-alt">
|
||||
<tr class="{{ ui::row_cls() }}">
|
||||
<td class="px-4 py-3 font-medium text-on-surface-strong dark:text-on-surface-dark-strong">
|
||||
<span style="padding-left: {{ row.depth * 20 }}px" class="inline-flex items-center gap-1.5">
|
||||
{% if row.depth > 0 %}<span class="text-on-surface/40 dark:text-on-surface-dark/40">↳</span>{% endif %}
|
||||
|
||||
@@ -13,21 +13,21 @@
|
||||
{{ ui::button(label=t(key="new-product", lang=lang | default(value='sk')), href="/admin/catalog/products/new") }}
|
||||
</div>
|
||||
|
||||
<div class="mt-6 overflow-hidden rounded-radius border border-outline dark:border-outline-dark">
|
||||
<div class="mt-6 {{ ui::table_wrap_cls() }}">
|
||||
{% if products | length > 0 %}
|
||||
<table class="w-full text-left text-sm">
|
||||
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70">
|
||||
<table class="{{ ui::table_cls() }}">
|
||||
<thead class="{{ ui::thead_cls() }}">
|
||||
<tr>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="product", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="price", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="stock", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="status", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 text-right font-semibold">{{ t(key="actions", lang=lang | default(value='sk')) }}</th>
|
||||
{{ ui::th(label=t(key="product", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="price", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="stock", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="status", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="actions", lang=lang | default(value='sk')), align="text-right") }}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline dark:divide-outline-dark">
|
||||
<tbody class="{{ ui::tbody_cls() }}">
|
||||
{% for product in products %}
|
||||
<tr class="hover:bg-surface-alt dark:hover:bg-surface-dark-alt">
|
||||
<tr class="{{ ui::row_cls() }}">
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex items-center gap-3">
|
||||
{% if product.image %}
|
||||
|
||||
@@ -7,21 +7,21 @@
|
||||
{% block content %}
|
||||
<h1 class="text-2xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="admin-orders", lang=lang | default(value='sk')) }}</h1>
|
||||
|
||||
<div class="mt-6 overflow-hidden rounded-radius border border-outline dark:border-outline-dark">
|
||||
<div class="mt-6 {{ ui::table_wrap_cls() }}">
|
||||
{% if orders | length > 0 %}
|
||||
<table class="w-full text-left text-sm">
|
||||
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70">
|
||||
<table class="{{ ui::table_cls() }}">
|
||||
<thead class="{{ ui::thead_cls() }}">
|
||||
<tr>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="order-number", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="order-customer", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="order-status", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 text-right font-semibold">{{ t(key="order-total", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3"></th>
|
||||
{{ ui::th(label=t(key="order-number", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="order-customer", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="order-status", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="order-total", lang=lang | default(value='sk')), align="text-right") }}
|
||||
{{ ui::th(label="") }}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline dark:divide-outline-dark">
|
||||
<tbody class="{{ ui::tbody_cls() }}">
|
||||
{% for order in orders %}
|
||||
<tr class="hover:bg-surface-alt dark:hover:bg-surface-dark-alt">
|
||||
<tr class="{{ ui::row_cls() }}">
|
||||
<td class="px-4 py-3 font-mono font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ order.order_number }}</td>
|
||||
<td class="px-4 py-3">{{ order.email }}</td>
|
||||
<td class="px-4 py-3">
|
||||
|
||||
@@ -16,16 +16,16 @@
|
||||
|
||||
<div class="mt-6 grid gap-6 lg:grid-cols-3">
|
||||
<div class="space-y-6 lg:col-span-2">
|
||||
<div class="overflow-hidden rounded-radius border border-outline dark:border-outline-dark">
|
||||
<table class="w-full text-left text-sm">
|
||||
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70">
|
||||
<div class="{{ ui::table_wrap_cls() }}">
|
||||
<table class="{{ ui::table_cls() }}">
|
||||
<thead class="{{ ui::thead_cls() }}">
|
||||
<tr>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="product", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="quantity", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 text-right font-semibold">{{ t(key="order-total", lang=lang | default(value='sk')) }}</th>
|
||||
{{ ui::th(label=t(key="product", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="quantity", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="order-total", lang=lang | default(value='sk')), align="text-right") }}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline dark:divide-outline-dark">
|
||||
<tbody class="{{ ui::tbody_cls() }}">
|
||||
{% for item in items %}
|
||||
<tr>
|
||||
<td class="px-4 py-3">{{ item.product_name }}</td>
|
||||
@@ -34,7 +34,7 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot class="border-t border-outline dark:border-outline-dark">
|
||||
<tfoot class="{{ ui::tfoot_cls() }}">
|
||||
<tr>
|
||||
<td colspan="2" class="px-4 py-3 text-right font-semibold">{{ t(key="order-total", lang=lang | default(value='sk')) }}</td>
|
||||
<td class="px-4 py-3 text-right font-bold tabular-nums text-primary dark:text-primary-dark">{{ order.total }} {{ order.currency }}</td>
|
||||
|
||||
@@ -129,3 +129,50 @@
|
||||
{% macro radio(name, value="", id="", checked=false, attrs="", extra="") -%}
|
||||
<input {% if id %}id="{{ id }}" {% endif %}name="{{ name }}" type="radio" value="{{ value }}"{% if checked %} checked{% endif %} class="before:content[''] relative h-4 w-4 appearance-none rounded-full border border-outline bg-surface before:invisible before:absolute before:left-1/2 before:top-1/2 before:h-1.5 before:w-1.5 before:-translate-x-1/2 before:-translate-y-1/2 before:rounded-full before:bg-on-primary checked:border-primary checked:bg-primary checked:before:visible focus:outline-2 focus:outline-offset-2 focus:outline-outline-strong checked:focus:outline-primary disabled:cursor-not-allowed dark:border-outline-dark dark:bg-surface-dark dark:before:bg-on-primary-dark dark:checked:border-primary-dark dark:checked:bg-primary-dark dark:focus:outline-outline-dark-strong dark:checked:focus:outline-primary-dark {{ extra }}" {{ attrs | safe }}/>
|
||||
{%- endmacro radio %}
|
||||
|
||||
{# ---- Table chrome. The same wrapper/thead/tbody/row/tfoot class strings were
|
||||
copy-pasted across 5 admin/shop tables; centralized here so the styling has a
|
||||
single source of truth. Tera has no slot/{% raw %}{% call %}{% endraw %} mechanism, so cells stay
|
||||
inline (their content varies too much to macro-ize: images, htmx forms, Alpine
|
||||
inputs, badges) and these macros expose just the shared CLASS STRINGS used as
|
||||
`class="{{ ui::thead_cls() }}"`. Adopts Penguin default-table.html's
|
||||
`w-full overflow-x-auto` wrapper so wide tables scroll on mobile.
|
||||
|
||||
Skeleton:
|
||||
<div class="{{ ui::table_wrap_cls() }}">
|
||||
<table class="{{ ui::table_cls() }}">
|
||||
<thead class="{{ ui::thead_cls() }}"><tr>{{ ui::th(label="Name") }}{{ ui::th(label="Total", align="text-right") }}</tr></thead>
|
||||
<tbody class="{{ ui::tbody_cls() }}">
|
||||
<tr class="{{ ui::row_cls() }}"><td class="px-4 py-3">…</td></tr> {# row_cls = hover; omit for non-interactive rows #}
|
||||
</tbody>
|
||||
</table>
|
||||
</div> #}
|
||||
{% macro table_wrap_cls() -%}
|
||||
overflow-hidden w-full overflow-x-auto rounded-radius border border-outline dark:border-outline-dark
|
||||
{%- endmacro table_wrap_cls %}
|
||||
|
||||
{% macro table_cls() -%}
|
||||
w-full text-left text-sm
|
||||
{%- endmacro table_cls %}
|
||||
|
||||
{% macro thead_cls() -%}
|
||||
border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70
|
||||
{%- endmacro thead_cls %}
|
||||
|
||||
{% macro tbody_cls() -%}
|
||||
divide-y divide-outline dark:divide-outline-dark
|
||||
{%- endmacro tbody_cls %}
|
||||
|
||||
{% macro row_cls() -%}
|
||||
hover:bg-surface-alt dark:hover:bg-surface-dark-alt
|
||||
{%- endmacro row_cls %}
|
||||
|
||||
{% macro tfoot_cls() -%}
|
||||
border-t border-outline dark:border-outline-dark
|
||||
{%- endmacro tfoot_cls %}
|
||||
|
||||
{# Header cell. align ∈ "" (left, default) | "text-right". Pass label="" for the
|
||||
empty actions column. #}
|
||||
{% macro th(label, align="") -%}
|
||||
<th class="px-4 py-3 font-semibold{% if align %} {{ align }}{% endif %}">{{ label }}</th>
|
||||
{%- endmacro th %}
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
and returned on its own by /cart/update and /cart/remove. #}
|
||||
{% import "macros/ui.html" as ui %}
|
||||
{% if items | length > 0 %}
|
||||
<div class="overflow-hidden rounded-radius border border-outline dark:border-outline-dark">
|
||||
<table class="w-full text-left text-sm">
|
||||
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70">
|
||||
<div class="{{ ui::table_wrap_cls() }}">
|
||||
<table class="{{ ui::table_cls() }}">
|
||||
<thead class="{{ ui::thead_cls() }}">
|
||||
<tr>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="product", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="price", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 font-semibold">{{ t(key="quantity", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3 text-right font-semibold">{{ t(key="cart-total", lang=lang | default(value='sk')) }}</th>
|
||||
<th class="px-4 py-3"></th>
|
||||
{{ ui::th(label=t(key="product", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="price", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="quantity", lang=lang | default(value='sk'))) }}
|
||||
{{ ui::th(label=t(key="cart-total", lang=lang | default(value='sk')), align="text-right") }}
|
||||
{{ ui::th(label="") }}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline dark:divide-outline-dark">
|
||||
<tbody class="{{ ui::tbody_cls() }}">
|
||||
{% for item in items %}
|
||||
<tr>
|
||||
<td class="px-4 py-3">
|
||||
@@ -50,7 +50,7 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot class="border-t border-outline dark:border-outline-dark">
|
||||
<tfoot class="{{ ui::tfoot_cls() }}">
|
||||
<tr>
|
||||
<td colspan="3" class="px-4 py-3 text-right font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="cart-total", lang=lang | default(value='sk')) }}</td>
|
||||
<td class="px-4 py-3 text-right text-lg font-bold tabular-nums text-primary dark:text-primary-dark">{{ total }} {{ currency }}</td>
|
||||
|
||||
@@ -247,30 +247,34 @@ from the build. If the build ever balloons, check that exclusion is intact.
|
||||
|
||||
---
|
||||
|
||||
## 14. Table — PENDING
|
||||
**Penguin UI: `table/` (5 variants)**
|
||||
## 14. Table — ✅ DONE
|
||||
**Penguin UI: `table/default-table.html`**
|
||||
|
||||
> **Priority: HIGH** | ~196 lines across5 instances.
|
||||
> The same class structure is copy-pasted5 times. Penguin match:
|
||||
> `table/default-table.html` for basic tables, `table-with-action.html` for
|
||||
> row actions. Consider a `ui::table_header()` + `ui::table_row()` macro.
|
||||
- Exact upstream mirror at `penguinui-components/table/default-table.html` (reference only)
|
||||
- The same wrapper/thead/tbody/row/tfoot class structure was copy-pasted across all
|
||||
5 tables (orders index, order detail, products, categories, cart body). Centralized
|
||||
into **class-string macros** in `macros/ui.html`: `ui::table_wrap_cls()`,
|
||||
`ui::table_cls()`, `ui::thead_cls()`, `ui::tbody_cls()`, `ui::row_cls()` (hover),
|
||||
`ui::tfoot_cls()`, plus an element macro `ui::th(label, align="")` for header cells.
|
||||
- **Why class-string macros, not full row macros:** Tera has no slot/`{% raw %}{% call %}{% endraw %}`
|
||||
mechanism, and the cells are heterogeneous (product image+name, htmx quantity input
|
||||
with inline Alpine `@change`, badges, action-button forms), so rows stay inline. The
|
||||
macros centralize only the drift-prone chrome styling — `class="{{ ui::thead_cls() }}"`.
|
||||
- **Penguin improvement adopted:** the wrapper now carries `w-full overflow-x-auto`
|
||||
(from `default-table.html`) so wide tables scroll horizontally on mobile instead of
|
||||
overflowing. Our `text-xs uppercase` thead + `px-4 py-3` cells were kept (deliberate,
|
||||
richer than Penguin's `text-sm`/`p-4`).
|
||||
- Interactive lists (orders/products/categories) use `ui::row_cls()` for the hover
|
||||
highlight; non-interactive rows (order items, cart) omit it. `tfoot` (order detail +
|
||||
cart totals) uses `ui::tfoot_cls()`.
|
||||
|
||||
| # | Location | What it is | Size |
|
||||
|---|----------|------------|------|
|
||||
| 34 | `assets/views/admin/orders/index.html:11-36` | Orders table: number, customer, status pill, total, "View" link | ~26 lines |
|
||||
| 35 | `assets/views/admin/orders/show.html:20-44` | Order items table: product, quantity, line total + tfoot summary | ~25 lines |
|
||||
| 36 | `assets/views/admin/catalog/products.html:20-70` | Products table: image+name+category, price, stock, status pill, edit/view/delete actions | ~51 lines |
|
||||
| 37 | `assets/views/admin/catalog/categories.html:20-59` | Categories table: tree-indented name, product count, status pill, edit/delete | ~40 lines |
|
||||
| 38 | `assets/views/shop/_cart_body.html:6-59` | Cart table: product link, price, quantity input, line total, remove button + tfoot total | ~54 lines |
|
||||
|
||||
**Pattern:** Every table uses the same class structure:
|
||||
```
|
||||
<table class="w-full text-left text-sm">
|
||||
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70">
|
||||
<tbody class="divide-y divide-outline">
|
||||
<tr class="hover:bg-surface-alt">
|
||||
```
|
||||
This is copy-pasted 5 times.
|
||||
| # | Location | What it is |
|
||||
|---|----------|------------|
|
||||
| 34 | `assets/views/admin/orders/index.html` | Orders table |
|
||||
| 35 | `assets/views/admin/orders/show.html` | Order items table + tfoot summary |
|
||||
| 36 | `assets/views/admin/catalog/products.html` | Products table (image+name, status pill, actions) |
|
||||
| 37 | `assets/views/admin/catalog/categories.html` | Categories table (tree-indented, actions) |
|
||||
| 38 | `assets/views/shop/_cart_body.html` | Cart table (htmx qty input, remove) + tfoot total |
|
||||
|
||||
---
|
||||
|
||||
@@ -536,7 +540,7 @@ Both are wrapped in a single card-style `<form>`.
|
||||
| ~~5~~ | ~~Country/Phone Combobox~~ | ⛔ WON'T PORT — needs Alpine Focus plugin; our lightweight version kept | — | — |
|
||||
| ~~7~~ | ~~Image Gallery~~ | ✅ DONE — `carousel/default-carousel.html` | Small | ~19 |
|
||||
| ~~8~~ | ~~Radio Groups~~ | ✅ DONE — `radio/radio-with-container.html` | Small | ~47 |
|
||||
| 14 | Table | `table/default-table.html` | Medium | ~196 |
|
||||
| ~~14~~ | ~~Table~~ | ✅ DONE — `table/default-table.html` (class-string macros) | Medium | ~196 |
|
||||
|
||||
### Phase 2 — MEDIUM (good match, more integration risk)
|
||||
|
||||
@@ -559,7 +563,7 @@ Both are wrapped in a single card-style `<form>`.
|
||||
| 26 | Shipping Settings Row | Already fully uses Penguin macros |
|
||||
| 27 | Form Wrappers | Already fully uses Penguin macros |
|
||||
|
||||
### Already DONE (15 of 27)
|
||||
### Already DONE (16 of 27)
|
||||
|
||||
| # | Component |
|
||||
|---|-----------|
|
||||
@@ -575,14 +579,14 @@ Both are wrapped in a single card-style `<form>`.
|
||||
| 11 | Textarea |
|
||||
| 12 | Select/Dropdown |
|
||||
| 13 | File Input |
|
||||
| 14 | Table |
|
||||
| 15 | Alert / Error Banner |
|
||||
| 16 | Badge / Status Pill |
|
||||
| 17 | Buttons |
|
||||
|
||||
**Remaining real ports: just #14 Table (~196 lines, Medium) and #1 Navbar
|
||||
(~143 lines, Large).** #5 Combobox is a conscious WON'T-PORT (Alpine Focus
|
||||
plugin dependency). The Phase-3 items are already internally Penguin-adapted
|
||||
or have no applicable component.
|
||||
**Remaining real port: just #1 Navbar (~143 lines, Large).** #5 Combobox is a
|
||||
conscious WON'T-PORT (Alpine Focus plugin dependency). The Phase-3 items are
|
||||
already internally Penguin-adapted or have no applicable component.
|
||||
|
||||
---
|
||||
|
||||
@@ -604,7 +608,7 @@ or have no applicable component.
|
||||
| 11 | Textarea | `text-area/` | ✅ DONE | ~10 |
|
||||
| 12 | Select | `select/` | ✅ DONE | ~23 |
|
||||
| 13 | File input | `file-input/` | ✅ DONE | ~12 |
|
||||
| 14 | Table | `table/` | **HIGH** | ~196 |
|
||||
| 14 | Table | `table/` | ✅ DONE | ~196 |
|
||||
| 15 | Alert/Error | `alert/` | ✅ DONE | ~9 |
|
||||
| 16 | Badge/Pill | `badge/` | ✅ DONE | ~17 |
|
||||
| 17 | Button | `buttons/` | ✅ DONE | ~200+ |
|
||||
@@ -619,7 +623,7 @@ or have no applicable component.
|
||||
| 26 | Shipping settings row | `card/` | LOW | ~21 |
|
||||
| 27 | Form wrappers | `card/` | LOW | ~150 |
|
||||
|
||||
**Status: 15 of 27 components fully ported to Penguin UI. Only 2 real ports
|
||||
remain — #14 Table (HIGH) and #1 Navbar (MED). #5 Combobox is a conscious
|
||||
WON'T-PORT (Alpine Focus plugin dependency). The remaining Phase-3 items are
|
||||
already internally Penguin-adapted or have no applicable match.**
|
||||
**Status: 16 of 27 components fully ported to Penguin UI. Only 1 real port
|
||||
remains — #1 Navbar (MED). #5 Combobox is a conscious WON'T-PORT (Alpine Focus
|
||||
plugin dependency). The remaining Phase-3 items are already internally
|
||||
Penguin-adapted or have no applicable match.**
|
||||
|
||||
Reference in New Issue
Block a user