@citizenplane/pimp 14.0.3 → 14.1.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@citizenplane/pimp",
3
- "version": "14.0.3",
3
+ "version": "14.1.1",
4
4
  "scripts": {
5
5
  "dev": "storybook dev -p 8080",
6
6
  "build-storybook": "storybook build --output-dir ./docs",
@@ -28,7 +28,7 @@
28
28
  :option-label
29
29
  :placeholder
30
30
  :pt="passThroughConfig"
31
- :suggestions="options"
31
+ :suggestions="dynamicOptions"
32
32
  :typeahead
33
33
  @click="handleClick"
34
34
  @complete="handleSearch"
@@ -126,6 +126,7 @@ interface Props {
126
126
  modelValue?: Record<string, unknown>[] | Record<string, unknown> | string[] | null
127
127
  multiple?: boolean
128
128
  name?: string
129
+ onSearch?: (query: string) => void
129
130
  optionDisabled?: string | ((option: unknown) => boolean)
130
131
  optionLabel?: string
131
132
  options?: unknown[]
@@ -141,7 +142,7 @@ const props = withDefaults(defineProps<Props>(), {
141
142
  label: '',
142
143
  name: '',
143
144
  placeholder: '',
144
- optionLabel: undefined,
145
+ optionLabel: 'name',
145
146
  trackBy: undefined,
146
147
  emptyMessage: 'No results found',
147
148
  errorMessage: '',
@@ -149,6 +150,7 @@ const props = withDefaults(defineProps<Props>(), {
149
150
  optionDisabled: 'disabled',
150
151
  options: () => [],
151
152
  size: 'md',
153
+ onSearch: undefined,
152
154
  })
153
155
 
154
156
  const emit = defineEmits<Emits>()
@@ -218,7 +220,27 @@ const displayClearButton = computed(() => {
218
220
  return props.isClearable && !isEmpty(selectModel.value)
219
221
  })
220
222
 
221
- const handleSearch = (event: { query: string }) => emit('search', event.query)
223
+ const hasRegisteredOnSearch = computed(() => props.onSearch !== undefined)
224
+
225
+ const handleSearch = (event: { query: string }) => {
226
+ if (hasRegisteredOnSearch.value) {
227
+ return emit('search', event.query)
228
+ }
229
+
230
+ // If no onSearch is registered, toggle the dropdown (if hidden) to show the options
231
+ if (!isDropdownOpen.value) {
232
+ toggleDropdown()
233
+ }
234
+ }
235
+
236
+ const dynamicOptions = computed(() => {
237
+ if (hasRegisteredOnSearch.value) return props.options
238
+
239
+ return props.options.filter((option: unknown) => {
240
+ const optionLabel = typeof option === 'object' ? (option as Record<string, unknown>)[props.optionLabel] : option
241
+ return (optionLabel as string).toLowerCase().includes(searchQuery.value.toLowerCase())
242
+ })
243
+ })
222
244
  const handleClear = () => (selectModel.value = null)
223
245
  const handleOverlayShown = () => emit('overlayShown')
224
246
  const handleOverlayHidden = () => emit('overlayHidden')
@@ -77,6 +77,7 @@
77
77
  <cp-icon size="16" :type="option.icon" />
78
78
  </button>
79
79
  <button
80
+ v-if="hasMoreQuickActionsThanLimit"
80
81
  class="cpTable__action cpTable__action--isDefault"
81
82
  type="button"
82
83
  @click.stop="handleContextMenu({ rowData, rowIndex }, $event)"
@@ -209,7 +210,7 @@ const cpTableContainer = ref<HTMLElement | null>(null)
209
210
  const contextualMenu = ref<InstanceType<typeof CpContextualMenu>>()
210
211
 
211
212
  const hasRowOptions = computed(() => props.enableRowOptions && props.rowOptions.length)
212
- const hasMoreQuickActionsThanLimit = computed(() => props.rowOptions.length >= props.quickOptionsLimit)
213
+ const hasMoreQuickActionsThanLimit = computed(() => props.rowOptions.length > props.quickOptionsLimit)
213
214
 
214
215
  const quickOptions = computed(() => {
215
216
  if (!props.enableRowOptions || !props.rowOptions.length || props.quickOptionsLimit === 0) return []
@@ -113,43 +113,23 @@ const meta = {
113
113
  export default meta
114
114
  type Story = StoryObj<typeof meta>
115
115
 
116
- export const Single: Story = {
116
+ export const DefaultSearchHandler: Story = {
117
117
  args: {
118
118
  placeholder: 'Select a supplier',
119
119
  multiple: false,
120
120
  options: supplierOptions,
121
- optionLabel: 'name',
122
121
  trackBy: 'id',
123
122
  isClearable: true,
124
123
  },
125
124
  render: (args) => ({
126
125
  components: { CpMultiselect },
127
126
  setup() {
128
- const searchQuery = ref('')
129
- const isLoading = ref(false)
130
-
131
- const originalOptions = ref(args.options)
132
- const dynamicOptions = ref(toValue(originalOptions))
133
127
  const selectedSupplier = ref(null)
134
-
135
- const handleSearch = async (query: string) => {
136
- isLoading.value = true
137
- searchQuery.value = query
138
-
139
- await new Promise((resolve) => setTimeout(resolve, 500))
140
-
141
- dynamicOptions.value = originalOptions.value?.filter((option) => {
142
- return (option as IOption).name.toLowerCase().includes(searchQuery.value.toLowerCase())
143
- })
144
-
145
- isLoading.value = false
146
- }
147
-
148
- return { args, selectedSupplier, dynamicOptions, handleSearch, isLoading }
128
+ return { args, selectedSupplier }
149
129
  },
150
130
  template: `
151
131
  <div style="padding: 20px;width: 25rem;">
152
- <CpMultiselect v-model="selectedSupplier" v-bind="args" :options="dynamicOptions" :is-loading="isLoading" @search="handleSearch">
132
+ <CpMultiselect v-model="selectedSupplier" v-bind="args">
153
133
  <template #prefix>
154
134
  <cp-partner-badge type="supplier" size="sm" />
155
135
  </template>
@@ -219,6 +219,54 @@ export const WithCustomRowOptions: Story = {
219
219
  }),
220
220
  }
221
221
 
222
+ export const WithoutDefaultAction: Story = {
223
+ args: {
224
+ ...Default.args,
225
+ enableRowOptions: true,
226
+ quickOptionsLimit: 2,
227
+ enableColumnEdition: true,
228
+ },
229
+ render: (args) => ({
230
+ components: { CpTable },
231
+ setup() {
232
+ const isEditLoading = ref(false)
233
+
234
+ const rowOptions = computed(() => [
235
+ {
236
+ id: 'see',
237
+ label: 'See',
238
+ icon: 'eye',
239
+ action: () => console.log('See'),
240
+ },
241
+ {
242
+ id: 'delete',
243
+ label: 'Delete',
244
+ icon: 'trash',
245
+ isCritical: true,
246
+ action: () => console.log('Delete'),
247
+ },
248
+ ])
249
+
250
+ return { args, isEditLoading, rowOptions }
251
+ },
252
+ template: `
253
+ <CpTable v-bind="args" :row-options="rowOptions">
254
+ <template #status="{ cell }">
255
+ <span :style="{
256
+ padding: '4px 8px',
257
+ borderRadius: '4px',
258
+ fontSize: '12px',
259
+ backgroundColor: cell === 'Active' ? '#10B981' : '#EF4444',
260
+ color: 'white'
261
+ }">
262
+ {{ cell }}
263
+ </span>
264
+ </template>
265
+ </CpTable>
266
+ `,
267
+ }),
268
+ }
269
+
222
270
  export const WithOnlyDefaultAction: Story = {
223
271
  args: {
224
272
  ...Default.args,