Skip to content

Toolbar, Selection, Saved Views

Everything the package ships besides useDataTable and DataTable. All from @aaix/laravel-islands-datagrid/vue.

None of it carries wording: every label is a prop, because the application owns its language. Hand the package a translator once with provideDatagrid({ t, locale }) and the built-in strings follow it too.

Pickers

Combobox

One value from a list, with search. variant decides the look — and the default is a form field:

variantUse
field (default)inside a form, stays neutral when set
filterin a toolbar, tints a set value
filter-cardthe same inside a filter panel
PropDefaultPurpose
modelValue / emptyValue0 / 0the value, and the one that means "nothing chosen" — pass "" for string filters
options{}{value: label} or [{value, label, depth?}]
fetchOptions / fetchDelay / loadingLabelnull / 150let the server answer each search; options then only holds the preloaded first page
selectedLabel''the label of a value outside that first page — without it a deep link shows the placeholder
searchValuesfalsesearch the values too, not only the labels
keepAncestorsfalsein a list with depth, a match keeps what it sits under, so searching narrows a tree
maxOptions60how many entries the list may show at once; zero shows all
clearOptiontruethe row that resets the choice
menuWidth / menuHeight288 / 240room for long or deeply named lists
placeholder, searchPlaceholder, allLabel, emptyLabelthe words

Slots option and selected take over the rendering.

TreeSelect

A hierarchy, searchable by path. Either hand it options, or an optionsUrl — then it fetches once per URL and caches, so several rows sharing a tree cost one request.

PropDefaultPurpose
modelValuenullthe chosen leaf
options / optionsUrlnull / ''the tree, or where to get it
selectedPath / separator'' / »what the trigger shows, and how a path reads
countLabelFornulla function turning an entry into a trailing count
clearable / clearLabelfalsean X on the trigger
resultLimit200how many matches a search may show
width / listHeight480 / 20remthe menu's room
placeholder, searchPlaceholder, loadingLabel, errorLabel, retryLabel, emptyLabel, hintLabelthe words

Emits update:modelValue, open, close. The trigger is a slot, so it fits a toolbar as well as a table cell.

MultiSelect

Several values from one list. Shows the first previewLimit choices, each cut at previewChars, then "+n".

PropDefault
modelValue[]
options{}
previewLimit / previewChars3 / 14
variantfilter
menuWidth / menuHeight288 / 320
placeholder, allLabel, emptyLabel

OptionStrip

A micro switcher: one of n, or a switch per option.

PropDefaultPurpose
options[]{ value, label, hint?, count? }
modelValuenullone value, or an array while multiple
multiplefalseevery option becomes its own on/off
clearablefalsepicking the taken one lets go of it again; ignored while multiple
variantpillspills is a row of switches, segmented reads as one question with n answers

Search and chrome

SearchInput

modelValue, placeholder, clearLabelupdate:modelValue (wire to onSearchInput) and clear (to clearSearch).

FilterPanel

The panel beside the table: titleclose. It docks itself where the viewport allows and floats over the table where it does not.

Pagination

DataTable renders it already; use it standalone only outside a table.

PropDefaultPurpose
metarequired, the response meta
perPagerequired
perPageOptions[5, 10, 30, 50, 100, 200]
pageCount7how many page buttons — a window that slides rather than grows, so buttons do not move under the pointer
compactfalsedrops the page jumper for narrow places

Emits page-change, per-page-change.

ColumnPicker

columns (the register), visible (the keys currently shown), changed (whether that differs from the default), label, resetLabel. Emits update with the new keys and reset.

A column register is one array of { key, label, default, locked }locked columns cannot be hidden.

Selection

useSelection(rows, { key = 'id', matchedTotal }) — row selection with two scopes.

page collects the ids that were ticked. matching means "everything the current filter answers with", which is a promise about rows that were never loaded, so it holds the ones ticked off again instead. That is what makes acting on a whole filtered result possible without carrying thousands of ids around.

ReturnsMeaning
scope'page' or 'matching'
isMatchingScopethe wider scope is active
canSelectMatchingthere are more matches than rows on screen — otherwise offering it is pointless
matchedhow many the filter matches (from matchedTotal)
ids / excludedIdsthe ticked ones, or the ticked-off ones — the other is empty
count, anyhow many are selected either way
allOnPage, someOnPagefor the header box's checked and indeterminate state
has(id), set(id, on), toggle(id)per row
togglePage()the header box: fills or empties the current page
selectMatching(), selectPage()switch scope
remove(id), clear()after deleting a row, or to start over

matchedTotal is usually computed(() => meta.value.total ?? 0). Without it the wider scope stays unavailable and a table behaves exactly as before.

js
const matchedTotal = computed(() => Number(meta.value.total ?? 0));
const selection = useSelection(rows, { matchedTotal });

A selection describes a result

It cannot outlive the filter that produced it — watch your filter keys and clear() when they change, or "all matches" comes to mean something else than what the user agreed to.

The scope travels to the server as an intent, never as ids:

js
const body = selection.isMatchingScope.value
    ? { scope: 'matching', filter: { …state }, except: selection.excludedIds.value, expectedCount: selection.count.value }
    : { ids: selection.ids.value };

Sending expectedCount lets the endpoint refuse a set that has grown or shrunk since it was agreed to, instead of applying the action to whatever it is now. The package does not prescribe this shape — it hands over the intent, the application owns its endpoint.

SelectionBox

The checkbox itself: checked, indeterminate, disabled, labelchange. It stops the click from reaching the row, so a selectable row can still be expandable.

Saved views

useViewProfiles

Keeps a user's saved views and the one currently applied.

OptionPurpose
state, defaults, keysthe table state, and which keys a view stores
storeUrl, profileUrlwhere to create, and the single-profile URL with a __REF__ placeholder
stateKeywhich state key holds the applied view's reference, view by default
initial, sharedwhat the server sent: the user's own views, plus one opened from a link
plainkeys stored as they are, without the filter shape
applycalled with a payload the user picked — the table decides how to adopt it
onErrorso the view can say what failed

Returns profiles, active, changed, dirty, busy, payload(), save, replace, rename, remove, open, reset, write.

ViewProfileMenu

profiles, active, changed, dirty, busy, labels (every word it needs) → apply, reset, save, update, rename, remove, copy. Wire each event to the composable's method of the same name.

The PHP side

The three endpoints behind the menu ship with the package:

php
use Aaix\LaravelIslandsDatagrid\Concerns\HandlesViewProfiles;
use Aaix\LaravelIslandsDatagrid\ViewProfiles\ViewProfileStore;

class InvoicesIslandController extends Controller
{
    use HandlesViewProfiles;

    protected function viewProfiles(): ViewProfileStore
    {
        return InvoiceViewProfiles::store();
    }
}

storeViewProfile, updateViewProfile and destroyViewProfile come from the trait — point three routes at them and override authorizeViewProfiles(). Ownership, the per-user limit and the public reference are the store's business.

A ViewProfileSchema declares what a saved payload may carry — text, id, flag, tristate, date, choice, keyList, each with its allowed values — and sanitize() drops the rest, so a view shared by link cannot smuggle anything into the next reader's table.

Released under the MIT License.