MishkaCmsCoreWeb.UIAdapters.Admin (Mishka CMS Core v0.0.1)

Copy Markdown View Source

UI adapter for the admin card pages, extending the Tailwind adapter.

Only the pieces the design redraws differ — the row-action button, the nav_link, and its glyph — textarea/1, which is a code editor or a rich-text editor when the field asks for one, and text_input/1, which is a picture from the media library when the field asks for one. Everything else falls back, including the view switcher (template_switcher/1), which now lives in the Tailwind adapter as the single switcher every admin page shares.

Declared via presentation do ui_adapter … end by the resources across the extensions whose pages draw these cards, and paired with MishkaCmsCoreWeb.Templates.CardChrome.

Summary

Functions

A row-action button, drawn with this design's glyphs at this design's size.

A row-action's glyph, from its ui do icon … end name.

A :link row action, drawn the same way button/1 is — the card's row mixes the two.

A text field, and a picture from the site's media library when the field asks for one.

A textarea, and a real code editor or a rich-text editor when the field asks for one.

Functions

alert(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.alert/1.

archive_toggle(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.archive_toggle/1.

array_fields(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.array_fields/1.

badge(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.badge/1.

bulk_action_bar(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.bulk_action_bar/1.

bulk_action_button(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.bulk_action_button/1.

button(assigns)

A row-action button, drawn with this design's glyphs at this design's size.

The label always reaches the DOM — as title, and as a .lbl span an action's ui do class end hides with [&_.lbl]:hidden — but so does the Tailwind adapter's, so that is not why this exists. It exists for glyph/1: the fallback renders any name that is not a heroicon as an empty span, and MishkaBlog.Comment asks for icon "reply". It also pins hero icons to the 15px the cards' action squares are drawn around, rather than the fallback's 16px.

Retire this the day gervaz can render a non-heroicon name and the 1px is settled — and retire it together with nav_link/1, never one alone, or a card's link actions and its button actions would wear different icon sizes side by side in the same row.

Attributes

  • label (:string) - Defaults to nil.
  • class (:string) - Defaults to nil.
  • type (:string) - Defaults to "button".
  • icon (:string) - Defaults to nil.
  • variant (:atom) - Defaults to :default.
  • Global attributes are accepted. Supports all globals plus: ["phx-click", "phx-target", "phx-value-id", "phx-value-event", "phx-value-values", "data-confirm", "disabled"].

cell_array(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_array/1.

cell_avatars(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_avatars/1.

cell_bars(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_bars/1.

cell_code(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_code/1.

cell_date(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_date/1.

cell_datetime(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_datetime/1.

cell_empty(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_empty/1.

cell_number(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_number/1.

cell_stacked(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_stacked/1.

cell_stats(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_stats/1.

cell_tags(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_tags/1.

cell_text(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.cell_text/1.

checkbox(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.checkbox/1.

combobox(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.combobox/1.

copy_button(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.copy_button/1.

date_input(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.date_input/1.

date_range_container(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.date_range_container/1.

datetime_input(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.datetime_input/1.

empty_state(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.empty_state/1.

error_state(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.error_state/1.

field_error(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.field_error/1.

field_group(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.field_group/1.

field_wrapper(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.field_wrapper/1.

filter_reset_button(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.filter_reset_button/1.

form_container(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.form_container/1.

form_footer(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.form_footer/1.

form_header(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.form_header/1.

glyph(assigns)

A row-action's glyph, from its ui do icon … end name.

A hero-* name renders the heroicon; any other name is a design glyph the icon set has no match for, drawn inline here — the row-action contract passes a string, so an SVG cannot travel through the resource DSL, and this is where it lands instead. Each inline glyph is sized to 13px to match what the card's action class shrinks the heroicons to. Add a clause per glyph the design needs.

Attributes

  • name (:string) (required)

icon(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.icon/1.

json_editor(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.json_editor/1.

load_more_select(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.load_more_select/1.

loading_state(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.loading_state/1.

multi_select(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.multi_select/1.

nested_fields(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.nested_fields/1.

number_input(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.number_input/1.

pagination_container(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.pagination_container/1.

pagination_nav_button(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.pagination_nav_button/1.

pagination_page_button(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.pagination_page_button/1.

password_input(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.password_input/1.

range_input(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.range_input/1.

search_select(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.search_select/1.

select(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.select/1.

spinner(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.spinner/1.

step_indicator(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.step_indicator/1.

step_navigation(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.step_navigation/1.

string_list_input(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.string_list_input/1.

table(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.table/1.

table_header(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.table_header/1.

td(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.td/1.

template_switcher(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.template_switcher/1.

template_switcher_button(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.template_switcher_button/1.

text_input(assigns)

A text field, and a picture from the site's media library when the field asks for one.

ui do extra %{media: :images} end on a :text field holding an address draws MishkaCmsCoreWeb.Live.Admin.Components.MediaField: the address, a preview of it, and the library under it to upload a picture to or choose one from — folder: "name" files the uploads in that folder. The library is the site's the form's Site field names, as the content editor's is.

ui do extra %{share_card: &MyExtension.ShareCards.target/1} end on a virtual field draws the record's share card and its Regenerate button (MishkaCmsCoreWeb.Live.Admin.Components.ShareCardField): the function is handed the record the form edits and returns the MishkaCmsCore.Runtime.ShareCard.target/0 its card is drawn for, or nil. A create form has no record, and draws the note that there is no share image yet.

Every other text field is the fallback's.

textarea(assigns)

A textarea, and a real code editor or a rich-text editor when the field asks for one.

ui do extra %{code: "js"} end on a :textarea field turns it into CodeMirror with that language's grammar, line numbers and bracket matching — the same editor the page builder's drawer uses, loaded from the same on-demand bundle. Anything else is the Tailwind adapter's plain box, handed exactly the assigns the field passed — its class, rows and phx-debounce defaults are the Tailwind adapter's own — so this costs nothing to every other form in the admin.

The code editor is rows tall (four when unset) and sends its changes at the field's debounce when that is a number of milliseconds, else every 300ms.

The textarea is still the form field. CodeMirror mounts beside it and writes into it on every change, so validation, phx-change and save never learn that an editor exists — and a page where the bundle fails to load has a working textarea rather than an empty div.

phx-update="ignore" is what keeps LiveView from reconciling away the DOM CodeMirror builds, and the id is the FIELD's, not the content's: a form echoes a sanitized value back on every keystroke, and keying on the text would remount the editor — and drop the cursor to the end — mid-word.

The page must set :code_editor for the bundle's address to be published; MishkaCmsCore.Layouts.Nav does it for every admin page.

The box's height is rows × 1.75rem with a floor of six, and it is a CLASS rather than a style attribute so the policy in MishkaCmsCore.Runtime.Plugs.SecurityHeaders can close style-src-attr, which no nonce can ever reopen for an attribute. code_height_class/1 writes each height out whole, one clause per row count, because Tailwind reads source text — an interpolated class name gets no rule at all, the box collapses to nothing and the editor inside it disappears. Its clauses run to twenty, comfortably past the largest rows any resource declares; a field asking for more takes twenty's height until a clause is added for it.

The content editor

ui do extra %{content_editor: true} end on a :textarea field holding HTML turns it into a rich-text editor, with components placed in the content as blocks. It is Mishka Chelekom's headless editor engine, drawn with that component's markup contract — the root, a toolbar of data-editor-command buttons, the hidden value textarea carrying the field's name, and the surface the ContentEditor hook mounts the engine on — written here, so the admin needs no site to have installed the kit. The engine writes the HTML into the hidden textarea and dispatches input there: the textarea is the field, as it is for the code editor.

A placed component is stored as its marker, in a paragraph of its own — see MishkaCmsCore.Runtime.ContentComponents. The palette, a component's settings and the answers the hook asks for are MishkaCmsCoreWeb.Live.Admin.Components.ContentComponentPicker, mounted inside the editor and named on the surface as data-components; a master's record places the components of the site its Site field names. Every id is the field's control id, which Gervaz writes from the form's, so two forms on one page draw two editors.

The document scrolls inside the editor past a height of its own, so the toolbar stays in view on a long post — a sticky toolbar would not stick inside the form's modal, whose box clips what overflows it. The editable element itself is the textbox: the engine names it with the field's label, from the surface's data-label-id. An empty field shows its placeholder, or a line saying what to do.

The page must set :content_editor for the bundle's address to be published; MishkaCmsCore.Layouts.Nav does it for every admin page.

Attributes

  • name (:string) (required)
  • id (:string)
  • value (:any)
  • placeholder (:string)
  • rows (:integer)
  • class (:string)
  • disabled (:boolean)
  • readonly (:boolean)
  • extra (:map)

th(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.th/1.

toggle_input(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.toggle_input/1.

tr(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.tr/1.

upload_dropzone(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.upload_dropzone/1.

upload_existing_file(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.upload_existing_file/1.

upload_file_input(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.upload_file_input/1.

upload_preview(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.upload_preview/1.

upload_progress(assigns)

Callback implementation for MishkaGervaz.Behaviours.UIAdapter.upload_progress/1.