Uh oh!
There was an error while loading. Please reload this page.
Add :per_translation_urls attr to core LanguageSwitcher - #525
Merged
Conversation
… view Containers that set `data-sortable-handle="<selector>"` on the SortableGrid hook now restrict drag initiation to descendants matching the selector (SortableJS `handle:` option). Unset → unchanged whole-item drag. `<.table_default>`'s mobile card view opts in by default when `:on_reorder` is set: emits `data-sortable-handle=".pk-drag-handle"`, adds the `pk-drag-handle` class to the existing footer drag-handle div, and drops `cursor-grab` from the card body. Clicks, text selection, and button presses on the card body now pass through normally.
SortableGrid hook snapshots each <td>'s offsetWidth into an inline style on `onChoose` and restores on `onUnchoose`. forceFallback + fallbackOnBody clones the dragged <tr> to <body>, where it loses its <table> ancestor and every cell collapses to its content width — the floating row visually flattened to the left. Pinning widths inline keeps the column layout intact while the drag is in flight. table_default's card-view footer reorganised so the drag handle stays leftmost while action buttons cluster on the right via a wrapper with ml-auto. Pre-fix used `card-actions justify-between` with an isolated handle div — when the action buttons wrapped on narrow cards, the handle ended up alone on row 1 above an empty space and the buttons on row 2, visually disconnected. Now both share a flex-wrap row; when buttons must wrap, the handle and buttons stay clustered.
The hook listens for a `sortable:flash` push_event the LV emits after
each reorder attempt — `{uuid, status: "ok" | "error"}` — and applies
a transient highlight class to every DOM element with the matching
data-id (so both the table row and the card view counterpart light up
together).
Implementation uses a `::after` pseudo-element overlay rather than
animating background-color directly. Direct bg animation overrode
existing card backgrounds (e.g. bg-base-200) and bled the page bg
through during the keyframe interpolation; the overlay sits on top of
whatever bg the row/card already has, so cards keep their grey while
the green/red flash fades in and out.
`moved_id` is now included on every reorder payload (was previously
only on cross-container drops) so the LV can flash any reorder, not
just cross-container moves.Lets a feature module (currently `phoenix_kit_publishing`) supply
explicit per-translation URLs that override the locale-rewrite
default in the dropdown / buttons / inline variants of the core
switcher. Useful when the consumer has computed canonical URLs for
each available translation that the simple locale-rewrite default
can't reproduce — for example when a post has per-language URL
slugs.
The new attr accepts a list of `%{code: <display_code>, url:
<full_url>}` maps (atom or string keys). The internal `resolve_url/3`
helper picks each language's URL by matching the entry's `code`
(normalized to its base via `DialectMapper.extract_base/1`) against
the language's `base_code`, falling back to the existing
`generate_base_code_url/2` default when no entry matches.
Host root layouts pass
`per_translation_urls={assigns[:phoenix_kit_publishing_translations]}`
— when publishing's controller assigned the conn, the switcher
shows publishing's URLs; when not (non-publishing pages), the assign
is `nil` and the switcher falls back to default behavior.
Pairs with `phoenix_kit_publishing` feature branch
`feat/language-switcher-host-integration` which exposes the conn
assign + adds the `publishing_show_language_switcher` setting that
suppresses publishing's own in-page switcher when the host already
provides one.
Tests: 7 new tests in
test/phoenix_kit_web/components/core/language_switcher_test.exs
covering the URL override (atom + string keyed), full-dialect to
base-code normalization, fallback per-language when an entry isn't
in the list, nil/empty/missing-attr pass-through to the historical
default behavior.ddon added a commit
that referenced
this pull request
May 8, 2026
Eight post-merge reviews covering V111 PDF tables, the DB-module extraction, MediaBrowser modal + LV login return_to, external-plugin admin permissions, dashboard sidebar gettext, live Hex.pm known-packages fetch, the publishing routing-strategy shim, and the LanguageSwitcher per_translation_urls attr (plus the bundled DnD work). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ddon added a commit
that referenced
this pull request
May 8, 2026
…525 Code/doc fixes addressing one finding per PR (or several where trivial). Each closes a NITPICK or IMPROVEMENT-LOW from the matching CLAUDE_REVIEW.md; design-level / breaking / risky items deferred per the FOLLOW_UP.md "Skipped" sections. - #516: Drop dead `String.to_atom` fallback in OAuth interpolate_url - #518: Delete stray 0-byte pages_html.ex - #519: Fix stale `viewer={true}` template comment + login_path trailing-slash self-loop guard - #521: Resolution-order doc on permission_key_for_admin_view/1 - #522: Hot-reload safety pitfall in per-module-i18n.md - #523: KnownPackages — max-pages cap, ensure_table race comment, Logger-levels operational signals in moduledoc - #524: __mix_recompile__?/0 note next to apply/3 explanation - #525: LanguageSwitcher attr doc atom/string keys + DRY resolve_url per-language + JS sortable:flash defensive status check Plus FOLLOW_UP.md per PR enumerating closed vs deferred items. PR #525's FOLLOW_UP also captures the bundled DnD audit trail (table_default drag-handle scoping, sortable:flash, TR cell-width preservation) absent from the original PR body. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ddon added a commit
that referenced
this pull request
May 8, 2026
Three-day window of accumulated work since 1.7.105 (2026-05-05): PRs #516, #518, #519, #521, #522, #523, #524, #525, plus the review-doc suite and post-merge triage closing nitpicks across all eight. Headline changes — V111 PDF library tables, DB module extracted to phoenix_kit_db, MediaBrowser modal viewer, sidebar gettext API, live Hex.pm catalog, publishing routing-strategy shim closing the /:locale/<literal>/... host-route shadowing bug, LanguageSwitcher :per_translation_urls, and bundled DnD improvements (drag-handle scoping, sortable:flash, TR cell-width preservation). All changes are strictly additive / non-breaking; one transitional extraction (DB → phoenix_kit_db) requires the paired Hex package once it ships. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lets a feature module supply explicit per-translation URLs that
override the locale-rewrite default in
PhoenixKitWeb.Components.Core.LanguageSwitcher. Pairs withBeamLabEU/phoenix_kit_publishing#15.Why
The core switcher's locale-rewrite default works when languages
share a URL structure that differs only by the
:localesegment.It breaks when a feature module computes per-translation canonical
URLs that aren't simple substitutions — most notably publishing's
per-language URL slugs, where
/en/blog/my-postand/fr/blog/mon-articlearen't related by segment swap.Publishing already builds the right per-translation URLs for its
in-page switcher and now exposes them on the conn under
:phoenix_kit_publishing_translations(in the matching publishingPR). The host's root layout passes that list to the core switcher
via the new
:per_translation_urlsattr, and each language renderswith its publishing URL instead of the locale-rewrite default.
When the assign is
nil(non-publishing pages), the switcher fallsback to its existing default — fully backward compatible.
How
A new private
resolve_url/3helper replaces the eightgenerate_base_code_url/2call sites in the dropdown / buttons /inline templates. It looks up the language's
base_codeagainst thesupplied
per_translation_urlslist (atom or string keyed —publishing's atom-keyed shape is the typical caller), normalizing
full dialect codes (
"en-US") to their base ("en") viaDialectMapper.extract_base/1. On a miss it falls through togenerate_base_code_url/2per-language, so URLs without apublishing entry continue to use the default.
Backward compatibility
The new attr defaults to
nil, so any existing caller that doesn'tpass it gets identical pre-feature behavior. No host integration is
required to keep current installs working.
Verification
Verified pre-existing nature by stash-and-rerun against the diff.
The 7 new tests in
test/phoenix_kit_web/components/core/language_switcher_test.exsall pass; previously there was no test file for
Components.Core.LanguageSwitcher.Test plan
mix precommitcleanfull-dialect-to-base-code normalization, fallback per-language
when an entry isn't in the list, nil/empty/missing-attr
pass-through to the historical default
per_translation_urlsisn't passedCoordination
Doesn't strictly require the matching publishing PR to ship —
hosts that don't use publishing get backward-compatible behavior
(attr defaults to
nil, no override). The publishing PR is whatpopulates the conn assign that hosts can then thread through.
Files
lib/phoenix_kit_web/components/core/language_switcher.ex(3 new attrs across the dropdown/buttons/inline variants, 1 new privateresolve_url/3helper, 8 call-site replacements, 2 helpersentry_base_code/1andentry_url/1)test/phoenix_kit_web/components/core/language_switcher_test.exs(NEW, 7 tests)