@nexdom/uimed-vue 2.0.0-beta.55 → 2.0.0-beta.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -625,8 +625,8 @@ declare const __VLS_export$16: import("vue", { with: { "resolution-mode": "impor
625
625
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
626
626
  }>, {
627
627
  variant: TextFieldVariant;
628
- type: TextFieldType;
629
628
  clearable: boolean;
629
+ type: TextFieldType;
630
630
  }, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>;
631
631
  //#endregion
632
632
  //#region src/components/inputs/checkbox/types.d.ts
@@ -1595,7 +1595,21 @@ type DataSetProps<T extends object> = {
1595
1595
  */
1596
1596
  itemsPerPage?: number;
1597
1597
  /**
1598
- * Shows a search field above the records, bound to `v-model:search`.
1598
+ * List of actions about the whole list, such as creating a record, displayed
1599
+ * as buttons at the start of the bar above the records, before the search
1600
+ * field. Same shape as `USection`'s actions.
1601
+ *
1602
+ * With the search field, the actions take the first half of the bar,
1603
+ * wrapping within it, or a line of their own when the data set isn't wide
1604
+ * enough for two records side by side. On extra small screens, each action
1605
+ * takes a line of its own and the full width. When undefined or empty, and
1606
+ * `searchable` is false, no bar is rendered.
1607
+ */
1608
+ actions?: SectionAction[];
1609
+ /**
1610
+ * Shows a search field at the end of the bar above the records, bound to
1611
+ * `v-model:search`. It's 240px wide, the same as a card's minimum width. On
1612
+ * extra small screens, it takes a line of its own and the full width.
1599
1613
  * @default false
1600
1614
  */
1601
1615
  searchable?: boolean;
@@ -1640,13 +1654,19 @@ type DataSetSlots<T extends object> = {
1640
1654
  //#region src/components/data-sets/data-set/data-set.vue.d.ts
1641
1655
  /**
1642
1656
  * Data set component to list records as a responsive grid of cards, with
1643
- * client-side pagination and an optional search field. Each record of the
1644
- * current page is rendered by the default slot, usually with `UDataSetItem`.
1657
+ * client-side pagination, and an optional bar above the records with actions
1658
+ * about the list and a search field. Each record of the current page is
1659
+ * rendered by the default slot, usually with `UDataSetItem`.
1645
1660
  *
1646
1661
  * @example
1647
1662
  * ```vue
1648
1663
  * <template>
1649
- * <u-data-set :items="patients" :columns="4" searchable>
1664
+ * <u-data-set
1665
+ * :items="patients"
1666
+ * :columns="4"
1667
+ * :actions="[{ label: 'Novo paciente', onClick: create }]"
1668
+ * searchable
1669
+ * >
1650
1670
  * <template #default="{ item }">
1651
1671
  * <u-data-set-item :title="item.name" :subtitle="item.plan" />
1652
1672
  * </template>
@@ -3,7 +3,7 @@ import { a as messages, c as colorToVuetifyTextColor, n as owners$1, r as reques
3
3
  import { useLocale, useMask } from "vuetify";
4
4
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, guardReactiveProps, inject, isRef, mergeModels, mergeProps, nextTick, normalizeClass, normalizeProps, onMounted, onScopeDispose, onUnmounted, openBlock, provide, readonly, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, toValue, unref, useId, useModel, useSlots, useTemplateRef, watch, withCtx, withKeys, withModifiers } from "vue";
5
5
  import { pt } from "vuetify/locale";
6
- import { VApp, VAppBar, VAppBarNavIcon, VAppBarTitle, VAutocomplete, VAvatar, VBadge, VBtn, VCard, VCardActions, VCardItem, VCardText, VCheckboxBtn, VCol, VCombobox, VContainer, VDataIterator, VDatePicker, VDialog, VDivider, VEmptyState, VExpansionPanel, VExpansionPanelText, VExpansionPanelTitle, VExpansionPanels, VFooter, VForm, VIcon, VIconBtn, VImg, VList, VListGroup, VListItem, VListItemAction, VListSubheader, VLocaleProvider, VMain, VMenu, VNavigationDrawer, VPagination, VProgressCircular, VProgressLinear, VRow, VSkeletonLoader, VSnackbarQueue, VTable, VTextField, VTimePicker } from "vuetify/components";
6
+ import { VApp, VAppBar, VAppBarNavIcon, VAppBarTitle, VAutocomplete, VAvatar, VBadge, VBtn, VCard, VCardActions, VCardItem, VCardText, VCheckboxBtn, VCol, VCombobox, VContainer, VDataIterator, VDatePicker, VDefaultsProvider, VDialog, VDivider, VEmptyState, VExpansionPanel, VExpansionPanelText, VExpansionPanelTitle, VExpansionPanels, VFooter, VForm, VIcon, VIconBtn, VImg, VList, VListGroup, VListItem, VListItemAction, VListSubheader, VLocaleProvider, VMain, VMenu, VNavigationDrawer, VPagination, VProgressCircular, VProgressLinear, VRow, VSkeletonLoader, VSnackbarQueue, VTable, VTextField, VTimePicker } from "vuetify/components";
7
7
  import { formatDateTime, isEmail, isEmpty, isPhone, isUrl, toInitials } from "@nexdom/shared/utils";
8
8
  import { VHighlight } from "vuetify/labs/VHighlight";
9
9
  import { RouterLink } from "vue-router";
@@ -2984,6 +2984,15 @@ function useDataSetColumnCols(columns) {
2984
2984
  return computed(() => dataSetColumnsToColumnCols[toValue(columns)]);
2985
2985
  }
2986
2986
  /**
2987
+ * Width, out of the bar's 12 columns, of the actions and of the search field
2988
+ * above the records of a data set `width` pixels wide: they share the bar in
2989
+ * halves (`"6"`) where two cards fit side by side, so the search field, as wide
2990
+ * as a card, fits in its half, and take a line each (`"12"`) otherwise.
2991
+ */
2992
+ function useDataSetBarCols(width) {
2993
+ return computed(() => getDataSetFittingColumns(2, toValue(width)) === 2 ? "6" : "12");
2994
+ }
2995
+ /**
2987
2996
  * Maps the record properties considered by the search to Vuetify's filter
2988
2997
  * keys. An empty list means every property, just like an undefined one.
2989
2998
  */
@@ -3019,6 +3028,92 @@ function useDataSetSearchPageReset(search, page) {
3019
3028
  });
3020
3029
  }
3021
3030
  //#endregion
3031
+ //#region src/components/data-sets/data-set-bar.vue
3032
+ var data_set_bar_default = /* @__PURE__ */ defineComponent({
3033
+ inheritAttrs: false,
3034
+ __name: "data-set-bar",
3035
+ props: /*@__PURE__*/ mergeModels({
3036
+ actions: {
3037
+ type: Array,
3038
+ required: false
3039
+ },
3040
+ searchable: {
3041
+ type: Boolean,
3042
+ required: false
3043
+ },
3044
+ cols: {
3045
+ type: String,
3046
+ required: true
3047
+ }
3048
+ }, {
3049
+ "modelValue": {
3050
+ type: String,
3051
+ default: ""
3052
+ },
3053
+ "modelModifiers": {}
3054
+ }),
3055
+ emits: ["update:modelValue"],
3056
+ setup(__props) {
3057
+ const props = __props;
3058
+ const search = useModel(__props, "modelValue");
3059
+ const searchFieldDefaults = { VTextField: {
3060
+ hideDetails: "auto",
3061
+ minWidth: 240
3062
+ } };
3063
+ return (_ctx, _cache) => {
3064
+ return openBlock(), createBlock(row_default, { "align-y": "end" }, {
3065
+ default: withCtx(() => [
3066
+ createCommentVNode(" The search field leaves room above its text for the floating label, so its text, icon and\n underline sit below its middle. Centered on it, the actions would look higher than the\n field, so they line up with its underline instead "),
3067
+ createCommentVNode(" No column grows to push the search field to the end of the line, so the actions and the\n search field share it in two columns, and the search field is aligned to the end of its\n own "),
3068
+ props.actions?.length ? (openBlock(), createBlock(column_default, {
3069
+ key: 0,
3070
+ cols: props.searchable ? props.cols : "12"
3071
+ }, {
3072
+ default: withCtx(() => [createVNode(row_default, null, {
3073
+ default: withCtx(() => [createCommentVNode(" Each action takes the full width of its column, which only stretches it below sm "), (openBlock(true), createElementBlock(Fragment, null, renderList(props.actions, ({ label, onClick, ...action }, index) => {
3074
+ return openBlock(), createBlock(column_default, {
3075
+ key: index,
3076
+ cols: "auto"
3077
+ }, {
3078
+ default: withCtx(() => [createVNode(button_default, mergeProps({ "full-width": "" }, { ref_for: true }, action, { onClick }), {
3079
+ default: withCtx(() => [createTextVNode(toDisplayString(label), 1)]),
3080
+ _: 2
3081
+ }, 1040, ["onClick"])]),
3082
+ _: 2
3083
+ }, 1024);
3084
+ }), 128))]),
3085
+ _: 1
3086
+ })]),
3087
+ _: 1
3088
+ }, 8, ["cols"])) : createCommentVNode("v-if", true),
3089
+ props.searchable ? (openBlock(), createBlock(column_default, {
3090
+ key: 1,
3091
+ cols: props.actions?.length ? props.cols : "12"
3092
+ }, {
3093
+ default: withCtx(() => [createVNode(row_default, { "align-x": "end" }, {
3094
+ default: withCtx(() => [createVNode(column_default, { cols: "auto" }, {
3095
+ default: withCtx(() => [createCommentVNode(" Hides the empty messages area, which would misalign the field with the actions, and\n keeps the field from shrinking to its label, which is all a column of its own width\n would leave it "), createVNode(unref(VDefaultsProvider), { defaults: searchFieldDefaults }, {
3096
+ default: withCtx(() => [createVNode(text_field_default, {
3097
+ modelValue: search.value,
3098
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
3099
+ type: "search",
3100
+ label: "Pesquisar"
3101
+ }, null, 8, ["modelValue"])]),
3102
+ _: 1
3103
+ })]),
3104
+ _: 1
3105
+ })]),
3106
+ _: 1
3107
+ })]),
3108
+ _: 1
3109
+ }, 8, ["cols"])) : createCommentVNode("v-if", true)
3110
+ ]),
3111
+ _: 1
3112
+ });
3113
+ };
3114
+ }
3115
+ });
3116
+ //#endregion
3022
3117
  //#region src/components/data-sets/data-set/data-set.vue
3023
3118
  var data_set_default = /* @__PURE__ */ defineComponent({
3024
3119
  inheritAttrs: false,
@@ -3038,6 +3133,10 @@ var data_set_default = /* @__PURE__ */ defineComponent({
3038
3133
  required: false,
3039
3134
  default: 10
3040
3135
  },
3136
+ actions: {
3137
+ type: Array,
3138
+ required: false
3139
+ },
3041
3140
  searchable: {
3042
3141
  type: Boolean,
3043
3142
  required: false
@@ -3078,7 +3177,9 @@ var data_set_default = /* @__PURE__ */ defineComponent({
3078
3177
  const page = useModel(__props, "page");
3079
3178
  const search = useModel(__props, "search");
3080
3179
  const iterator = useTemplateRef("iterator");
3081
- const columnCols = useDataSetColumnCols(useDataSetFittingColumns(() => props.columns, useDataSetWidth(() => iterator.value?.$el)));
3180
+ const width = useDataSetWidth(() => iterator.value?.$el);
3181
+ const columnCols = useDataSetColumnCols(useDataSetFittingColumns(() => props.columns, width));
3182
+ const barColumnCols = useDataSetBarCols(width);
3082
3183
  const filterKeys = useDataSetFilterKeys(() => props.searchKeys);
3083
3184
  useDataSetSearchPageReset(search, page);
3084
3185
  return (_ctx, _cache) => {
@@ -3087,64 +3188,75 @@ var data_set_default = /* @__PURE__ */ defineComponent({
3087
3188
  type: "card",
3088
3189
  "full-width": ""
3089
3190
  }, {
3090
- default: withCtx(() => [createVNode(unref(VDataIterator), {
3091
- ref_key: "iterator",
3092
- ref: iterator,
3093
- page: page.value,
3094
- "onUpdate:page": _cache[2] || (_cache[2] = ($event) => page.value = $event),
3095
- items: props.items,
3096
- "items-per-page": props.itemsPerPage,
3097
- search: search.value,
3098
- "filter-keys": unref(filterKeys),
3099
- "data-testid": props.dataTestid
3100
- }, {
3101
- header: withCtx(() => [props.searchable ? (openBlock(), createBlock(text_field_default, {
3102
- key: 0,
3103
- modelValue: search.value,
3104
- "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
3105
- type: "search",
3106
- label: "Pesquisar"
3107
- }, null, 8, ["modelValue"])) : createCommentVNode("v-if", true)]),
3108
- default: withCtx(({ items: pageItems }) => [createCommentVNode(" Stretched, so the items of the same line (e.g. full-height cards) share its height "), createVNode(row_default, {
3109
- list: "",
3110
- "align-y": "stretch"
3111
- }, {
3112
- default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(pageItems, (pageItem, index) => {
3113
- return openBlock(), createBlock(column_default, {
3114
- key: index,
3115
- cols: unref(columnCols),
3116
- "list-item": ""
3191
+ default: withCtx(() => [createCommentVNode(" The bar and the records take a line each of the same row, so the grid spaces them as it\n spaces its lines. The bar stays out of the iterator's header, where nothing would space\n it from the records "), createVNode(row_default, { "data-testid": props.dataTestid }, {
3192
+ default: withCtx(() => [props.actions?.length || props.searchable ? (openBlock(), createBlock(column_default, { key: 0 }, {
3193
+ default: withCtx(() => [createVNode(data_set_bar_default, {
3194
+ modelValue: search.value,
3195
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
3196
+ actions: props.actions,
3197
+ searchable: props.searchable,
3198
+ cols: unref(barColumnCols)
3199
+ }, null, 8, [
3200
+ "modelValue",
3201
+ "actions",
3202
+ "searchable",
3203
+ "cols"
3204
+ ])]),
3205
+ _: 1
3206
+ })) : createCommentVNode("v-if", true), createVNode(column_default, null, {
3207
+ default: withCtx(() => [createVNode(unref(VDataIterator), {
3208
+ ref_key: "iterator",
3209
+ ref: iterator,
3210
+ page: page.value,
3211
+ "onUpdate:page": _cache[2] || (_cache[2] = ($event) => page.value = $event),
3212
+ items: props.items,
3213
+ "items-per-page": props.itemsPerPage,
3214
+ search: search.value,
3215
+ "filter-keys": unref(filterKeys)
3216
+ }, {
3217
+ default: withCtx(({ items: pageItems }) => [createCommentVNode(" Stretched, so the items of the same line (e.g. full-height cards) share its height "), createVNode(row_default, {
3218
+ list: "",
3219
+ "align-y": "stretch"
3117
3220
  }, {
3118
- default: withCtx(() => [renderSlot(_ctx.$slots, "default", {
3119
- item: pageItem.raw,
3120
- index
3121
- })]),
3221
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(pageItems, (pageItem, index) => {
3222
+ return openBlock(), createBlock(column_default, {
3223
+ key: index,
3224
+ cols: unref(columnCols),
3225
+ "list-item": ""
3226
+ }, {
3227
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default", {
3228
+ item: pageItem.raw,
3229
+ index
3230
+ })]),
3231
+ _: 2
3232
+ }, 1032, ["cols"]);
3233
+ }), 128))]),
3122
3234
  _: 2
3123
- }, 1032, ["cols"]);
3124
- }), 128))]),
3125
- _: 2
3126
- }, 1024)]),
3127
- "no-data": withCtx(() => [createVNode(unref(VEmptyState), { text: props.noDataText }, null, 8, ["text"])]),
3128
- footer: withCtx(({ pageCount }) => [pageCount > 1 ? (openBlock(), createBlock(unref(VPagination), {
3129
- key: 0,
3130
- modelValue: page.value,
3131
- "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => page.value = $event),
3132
- length: pageCount,
3133
- "total-visible": unref(getDataSetTotalVisible)(pageCount)
3134
- }, null, 8, [
3135
- "modelValue",
3136
- "length",
3137
- "total-visible"
3138
- ])) : createCommentVNode("v-if", true)]),
3235
+ }, 1024)]),
3236
+ "no-data": withCtx(() => [createVNode(unref(VEmptyState), { text: props.noDataText }, null, 8, ["text"])]),
3237
+ footer: withCtx(({ pageCount }) => [pageCount > 1 ? (openBlock(), createBlock(unref(VPagination), {
3238
+ key: 0,
3239
+ modelValue: page.value,
3240
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => page.value = $event),
3241
+ length: pageCount,
3242
+ "total-visible": unref(getDataSetTotalVisible)(pageCount)
3243
+ }, null, 8, [
3244
+ "modelValue",
3245
+ "length",
3246
+ "total-visible"
3247
+ ])) : createCommentVNode("v-if", true)]),
3248
+ _: 3
3249
+ }, 8, [
3250
+ "page",
3251
+ "items",
3252
+ "items-per-page",
3253
+ "search",
3254
+ "filter-keys"
3255
+ ])]),
3256
+ _: 3
3257
+ })]),
3139
3258
  _: 3
3140
- }, 8, [
3141
- "page",
3142
- "items",
3143
- "items-per-page",
3144
- "search",
3145
- "filter-keys",
3146
- "data-testid"
3147
- ])]),
3259
+ }, 8, ["data-testid"])]),
3148
3260
  _: 3
3149
3261
  }, 8, ["loading"]);
3150
3262
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexdom/uimed-vue",
3
- "version": "2.0.0-beta.55",
3
+ "version": "2.0.0-beta.56",
4
4
  "description": "A ui framework for NEXDOM applications.",
5
5
  "keywords": [
6
6
  "lib",