diff --git a/addon/components/dropdown-button.hbs b/addon/components/dropdown-button.hbs index d8eb3cc0..03c72c5c 100644 --- a/addon/components/dropdown-button.hbs +++ b/addon/components/dropdown-button.hbs @@ -15,7 +15,7 @@ {{did-insert this.onInsert}} as |dd| > - + {{#if @buttonComponent}} {{component @buttonComponent diff --git a/addon/components/dropdown-button.js b/addon/components/dropdown-button.js index 2ec1cb05..3b55ee60 100644 --- a/addon/components/dropdown-button.js +++ b/addon/components/dropdown-button.js @@ -72,7 +72,8 @@ export default class DropdownButtonComponent extends Component { this._onInsertFired = true; } - @action onArgsChanged(el, [disabled = false, visible = true, permission = null]) { + @action onArgsChanged(el, [disabled = false, visible = true, permission = null, buttonComponentArgs = {}]) { + this.buttonComponentArgs = buttonComponentArgs; this.visible = visible; this.disabled = disabled; if (!disabled && permission) { diff --git a/addon/components/filter/checkbox.hbs b/addon/components/filter/checkbox.hbs new file mode 100644 index 00000000..239b6b86 --- /dev/null +++ b/addon/components/filter/checkbox.hbs @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/addon/components/filter/checkbox.js b/addon/components/filter/checkbox.js new file mode 100644 index 00000000..f3127da0 --- /dev/null +++ b/addon/components/filter/checkbox.js @@ -0,0 +1,23 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; +import toBoolean from '@fleetbase/ember-core/utils/to-boolean'; + +export default class FilterCheckboxComponent extends Component { + @tracked value = false; + + constructor(owner, { value = false }) { + super(...arguments); + this.value = toBoolean(value); + } + + @action onChange(checked) { + const { onChange, filter } = this.args; + + this.value = checked; + + if (typeof onChange === 'function') { + onChange(filter, checked); + } + } +} diff --git a/addon/components/filters-picker.hbs b/addon/components/filters-picker.hbs index 2f97c22e..4bc7413a 100644 --- a/addon/components/filters-picker.hbs +++ b/addon/components/filters-picker.hbs @@ -29,7 +29,9 @@
{{#each this.filters as |filter|}}
- + {{#unless filter.noFilterLabel}} + + {{/unless}} {{component filter.filterComponent value=filter.filterValue diff --git a/addon/components/filters-picker.js b/addon/components/filters-picker.js index fbb54e5b..a9406bdb 100644 --- a/addon/components/filters-picker.js +++ b/addon/components/filters-picker.js @@ -1,183 +1,116 @@ import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; -import { set, action } from '@ember/object'; -import { filter, gt } from '@ember/object/computed'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; import { isArray } from '@ember/array'; -import { later } from '@ember/runloop'; import getUrlParam from '../utils/get-url-param'; export default class FiltersPickerComponent extends Component { - /** - * Inject router to handle param changes via `transitionTo` - * - * @memberof FiltersPickerComponent - */ @service hostRouter; - - /** - * Array of filters created from columns argument. - * - * @memberof FiltersPickerComponent - */ @tracked filters = []; - /** - * Filters which are active and should be applied. - * - * @memberof FiltersPickerComponent - */ - @filter('filters.@each.isFilterActive', (filter) => filter.isFilterActive === true) activeFilters; - - /** - * Computed property that determines if any filters are set. - * - * @memberof FiltersPickerComponent - */ - @gt('activeFilters.length', 0) hasFilters; - - /** - * Creates an instance of FiltersPickerComponent. - * @memberof FiltersPickerComponent - */ + get activeFilters() { + return this.filters.filter((f) => f.isFilterActive); + } + + get hasFilters() { + return this.activeFilters.length > 0; + } + constructor() { super(...arguments); - this.updateFilters(); + + this.#rebuildFilters(); // initial state + + // Refresh whenever the route (→ query-params) changes + this._routeHandler = () => this.#rebuildFilters(); + this.hostRouter.on('routeDidChange', this._routeHandler); } - /** - * Creates and updates filters via map - * - * @param {null|Function} onColumn - * @memberof FiltersPickerComponent - */ - @action updateFilters(onColumn) { - this.filters = this.args.columns - .filter((column) => column.filterable) - .map((column, trueIndex) => { - // add true index to column - column = { ...column, trueIndex }; - - // set the column param - column.param = column.filterParam ?? column.valuePath; - - // get the active param if any and update filter - const activeParam = getUrlParam(column.param); - - // update if an activeParam exists - if (activeParam) { - column.isFilterActive = true; - - if (isArray(activeParam) && activeParam.length === 0) { - column.isFilterActive = false; - } - - column.filterValue = activeParam; - } + willDestroy() { + super.willDestroy(...arguments); + this.hostRouter.off('routeDidChange', this._routeHandler); + } + + #readUrlValue(param) { + const raw = getUrlParam(param); // string | string[] | undefined + if (isArray(raw)) { + return raw.length ? raw : undefined; + } + return raw === '' ? undefined : raw; + } + + #rebuildFilters(onColumn) { + const cols = this.args.columns ?? []; + + this.filters = cols + .filter((c) => c.filterable) + .map((column, index) => { + const param = column.filterParam ?? column.valuePath; + const value = this.#readUrlValue(param); + const active = value != null; // null & undefined only + + const filterCol = { + ...column, + trueIndex: index, + param, + filterValue: value, + isFilterActive: active, + }; - // callback to modify column from hook if (typeof onColumn === 'function') { - onColumn(column, trueIndex, activeParam); + onColumn(filterCol, index, value); } - return column; + return filterCol; }); return this; } - /** - * Triggers the apply callback for the filters picker. - * - * @memberof FiltersPickerComponent - */ @action applyFilters() { - const { onApply } = this.args; - - // run `onApply()` callback - if (typeof onApply === 'function') { - onApply(); + if (typeof this.args.onApply === 'function') { + this.args.onApply(); } - - // manually run update filters after apply with slight 300ms delay to update activeFilters - later( - this, - () => { - this.updateFilters(); - }, - 150 - ); } - /** - * Updates an individual filter/column value. - * - * @param {String} key - * @param {*} value - * @memberof FiltersPickerComponent - */ @action updateFilterValue({ param }, value) { - const { onChange } = this.args; - - // run `onChange()` callback - if (typeof onChange === 'function') { - onChange(param, value); + if (typeof this.args.onChange === 'function') { + this.args.onChange(param, value); } } - /** - * Callback to clear a single filter/column value. - * - * @param {String} key - * @memberof FiltersPickerComponent - */ @action clearFilterValue({ param }) { - const { onFilterClear } = this.args; - - // update filters - this.updateFilters((column) => { - if (column.param !== param) { - return; - } - - // clear column values - set(column, 'filterValue', undefined); - set(column, 'isFilterActive', false); - }); - - // run `onFilterClear()` callback - if (typeof onFilterClear === 'function') { - onFilterClear(param); + if (typeof this.args.onFilterClear === 'function') { + this.args.onFilterClear(param); } } - /** - * Used to clear all filter/column values and URL params. - * - * @memberof FiltersPickerComponent - */ - @action clearFilters() { - const { onClear } = this.args; - const currentRouteName = this.hostRouter.currentRouteName; - const currentQueryParams = { ...this.hostRouter.currentRoute.queryParams }; - - // update filters - this.updateFilters((column) => { - const paramKey = column.filterParam ?? column.valuePath; - delete currentQueryParams[paramKey]; - delete currentQueryParams[`${paramKey}[]`]; - - // reset column values - set(column, 'filterValue', undefined); - set(column, 'isFilterActive', false); - }); - - // transition to cleared params with router service - this.hostRouter.transitionTo(currentRouteName, { queryParams: currentQueryParams }); - - // run `onClear()` callback - if (typeof onClear === 'function') { - onClear(...arguments); + @action async clearFilters(...args) { + if (typeof this.args.onClear === 'function') { + this.args.onClear(...args); + } + + // Build a clean query-param bag + const qp = { ...this.hostRouter.currentRoute.queryParams }; + (this.args.columns ?? []) + .filter((c) => c.filterable) + .forEach((c) => { + const key = c.filterParam ?? c.valuePath; + delete qp[key]; + delete qp[`${key}[]`]; + }); + + // Transition – routeDidChange listener will rebuild afterwards + try { + await this.hostRouter.transitionTo(this.hostRouter.currentRouteName, { + queryParams: qp, + }); + } catch (error) { + // Ignore only the "transition aborted" case + if (error?.name !== 'TransitionAborted') { + throw error; // real error → rethrow + } } } } diff --git a/addon/components/filters-picker/button.hbs b/addon/components/filters-picker/button.hbs index 68d3b515..998e4ac4 100644 --- a/addon/components/filters-picker/button.hbs +++ b/addon/components/filters-picker/button.hbs @@ -1,7 +1,7 @@ - \ No newline at end of file diff --git a/addon/components/filters-picker/button.js b/addon/components/filters-picker/button.js new file mode 100644 index 00000000..57c3a788 --- /dev/null +++ b/addon/components/filters-picker/button.js @@ -0,0 +1,16 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; + +export default class FiltersPickerButtonComponent extends Component { + @tracked buttonComponentArgs = {}; + + constructor(owner, { buttonComponentArgs = {} }) { + super(...arguments); + this.buttonComponentArgs = buttonComponentArgs; + } + + @action handleComponentArgsUpdate(el, [buttonComponentArgs = {}]) { + this.buttonComponentArgs = buttonComponentArgs; + } +} diff --git a/addon/components/modals/bulk-action-model.hbs b/addon/components/modals/bulk-action-model.hbs index e2c7730c..1dbbf325 100644 --- a/addon/components/modals/bulk-action-model.hbs +++ b/addon/components/modals/bulk-action-model.hbs @@ -22,7 +22,7 @@ {{pluralize @options.modelName}}? {{/if}} -
+

{{#if @options.message}} {{@options.message}} @@ -36,14 +36,27 @@

-
    +
    {{yield @options}}
    +
      {{#each @options.selected as |selected|}} -
    • - {{n-a (get selected @options.modelNamePath)}} +
    • +
      + {{#if @options.modelNameRenderComponent}} + {{component @options.modalNameRenderComponent options=@options}} + {{else}} + {{#if @options.resolveModelName}} + {{n-a selected.list_resolved_name}} + {{else}} + {{n-a (get selected @options.modelNamePath)}} + {{/if}} + {{/if}} +
      - - - +
      + + + +
    • {{/each}}
    diff --git a/addon/components/modals/import-form.hbs b/addon/components/modals/import-form.hbs index 601c9537..bb34e5b1 100644 --- a/addon/components/modals/import-form.hbs +++ b/addon/components/modals/import-form.hbs @@ -1,5 +1,5 @@ -