picocrank 1.25.1 → 1.26.0

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.
Files changed (44) hide show
  1. package/eslint.config.js +68 -0
  2. package/package.json +13 -3
  3. package/router.js +74 -0
  4. package/styles/dark-theme.css +21 -9
  5. package/styles/picocrank-extensions.css +131 -2
  6. package/supplemental-themes/catppuccin-latte-frappe/theme.css +12 -22
  7. package/supplemental-themes/dracula-alucard/theme.css +12 -22
  8. package/supplemental-themes/gruvbox-dark-light/theme.css +12 -22
  9. package/supplemental-themes/waffles/theme.css +2 -12
  10. package/vue/components/Breadcrumbs.vue +192 -45
  11. package/vue/components/Calendar.vue +93 -180
  12. package/vue/components/DangerZone.vue +186 -0
  13. package/vue/components/Header.vue +22 -6
  14. package/vue/components/Login.vue +6 -7
  15. package/vue/components/NavigationGrid.vue +26 -204
  16. package/vue/components/NotificationBlock.vue +120 -0
  17. package/vue/components/NotificationPopup.vue +44 -41
  18. package/vue/components/QuickSearch.vue +21 -47
  19. package/vue/components/RadioGroup.vue +4 -4
  20. package/vue/components/ReadOnlyTextArea.vue +1 -1
  21. package/vue/components/SidebarNavLink.vue +3 -3
  22. package/vue/components/Table.vue +109 -81
  23. package/vue/components/TableColumnFilterPopover.vue +7 -17
  24. package/vue/components/TableColumnOptionsPopover.vue +22 -27
  25. package/vue/components/Tabs.vue +366 -82
  26. package/vue/components/TopBar.vue +2 -2
  27. package/vue/composables/tableColumnPriorities.js +33 -0
  28. package/vue/composables/tableFilters.js +1 -1
  29. package/vue/data/navSectionHubs.js +196 -0
  30. package/vue/data/routeBreadcrumbs.js +90 -0
  31. package/vue/examples/Admin.vue +12 -12
  32. package/vue/examples/App.vue +27 -36
  33. package/vue/examples/CalendarExample.vue +32 -22
  34. package/vue/examples/ExampleHeaderThemeSwitcher.vue +1 -1
  35. package/vue/examples/Login.vue +0 -3
  36. package/vue/examples/LoginExample.vue +2 -14
  37. package/vue/examples/NavSectionHub.vue +36 -0
  38. package/vue/examples/PageStructure.vue +6 -3
  39. package/vue/examples/StatusExample.vue +248 -129
  40. package/vue/examples/TableExample.vue +68 -67
  41. package/vue/examples/TabsExample.vue +200 -255
  42. package/vue/examples/ThemePreview.vue +21 -23
  43. package/vue/examples/ViewItem.vue +32 -5
  44. package/vue/examples/Welcome.vue +3 -112
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <Section
3
- title="Table Example"
3
+ :title="tableSectionTitle"
4
4
  :icon="TableIcon"
5
5
  subtitle="Sortable columns, pagination, and column filters. Right-click or long-press a column header to filter."
6
6
  :padding="false"
@@ -15,8 +15,10 @@
15
15
  :column-options="enabledFeatures.includes('columnOptions')"
16
16
  :loading="enabledFeatures.includes('loading')"
17
17
  :horizontal-scroll="layout === 'scroll'"
18
+ :responsive-columns="layout === 'priorities'"
18
19
  :sticky-cols="stickyCols"
19
20
  v-model:column-visibility="columnVisibility"
21
+ v-model:layout-label="tableLayoutLabel"
20
22
  :default-column-visibility="defaultColumnVisibility"
21
23
  v-bind="tableListeners"
22
24
  >
@@ -26,14 +28,14 @@
26
28
  </router-link>
27
29
  </template>
28
30
 
29
- <template #cell-city="{ row, value }">
31
+ <template #cell-city="{ value }">
30
32
  <span class="subtle">
31
33
  {{ value }}
32
34
  </span>
33
35
  </template>
34
36
 
35
37
  <template #cell-status="{ value }">
36
- <span class="tag fg-good">{{ value }}</span>
38
+ <span class="tag" :class="statusTagClass(value)">{{ value }}</span>
37
39
  </template>
38
40
  </Table>
39
41
  </Section>
@@ -49,14 +51,23 @@
49
51
 
50
52
  <FormLayout>
51
53
  <FormField label="Data" fake description="Connect or disconnect row data for the table above. Saved column layouts persist in localStorage for this table.">
52
- <div class="table-data-actions">
53
- <button type="button" :disabled="dataConnected" @click="dataConnected = true">
54
- Connect data
55
- </button>
56
- <button type="button" class="neutral" :disabled="!dataConnected" @click="dataConnected = false">
57
- Disconnect data
58
- </button>
59
- </div>
54
+ <RadioGroup
55
+ v-model="dataConnected"
56
+ name="table-data-connection"
57
+ variant="boolean"
58
+ :options="dataConnectionOptions"
59
+ />
60
+ </FormField>
61
+
62
+ <FormField
63
+ label="Wide table"
64
+ fake
65
+ description="Adds extra columns (email, department, role, status) to the table."
66
+ >
67
+ <label>
68
+ <input v-model="wideTable" type="checkbox" />
69
+ Show extended columns
70
+ </label>
60
71
  </FormField>
61
72
 
62
73
  <FormField label="Features" fake description="Toggle core table behaviour.">
@@ -70,7 +81,7 @@
70
81
  <FormField
71
82
  label="Responsive layout"
72
83
  fake
73
- description="Column priorities hide lower-priority columns as the table narrows. Horizontal scroll keeps every column visible with optional sticky leading columns."
84
+ description="Column priorities hide lower-priority columns as the table narrows. Horizontal scroll keeps every column visible with optional sticky leading columns. Browser layout leaves column sizing to the viewport."
74
85
  >
75
86
  <RadioGroup
76
87
  v-model="layout"
@@ -91,18 +102,6 @@
91
102
  :options="stickyColOptions"
92
103
  />
93
104
  </FormField>
94
-
95
- <FormField
96
- v-if="layout === 'scroll'"
97
- label="Wide table"
98
- fake
99
- description="Adds extra columns so horizontal scroll is easier to see."
100
- >
101
- <label>
102
- <input v-model="wideTable" type="checkbox" />
103
- Show extended columns
104
- </label>
105
- </FormField>
106
105
  </FormLayout>
107
106
 
108
107
  <div class="table-events">
@@ -120,6 +119,14 @@
120
119
  {{ activeFilterCount }} active filter(s)
121
120
  </p>
122
121
  <p v-else class="event-line subtle">Query: waiting for the first table update…</p>
122
+
123
+ <p v-if="tableLayoutLabel" class="event-line">
124
+ <strong>Layout:</strong>
125
+ {{ tableLayoutLabel }}
126
+ </p>
127
+ <p v-else-if="enabledFeatures.includes('columnOptions')" class="event-line subtle">
128
+ Layout: using developer defaults (change columns, order, or filters to see a preset or “Custom” label).
129
+ </p>
123
130
  </div>
124
131
  </Section>
125
132
 
@@ -139,7 +146,7 @@
139
146
  <template v-for="header in headers" :key="header.key">
140
147
  <dt>{{ header.label || header.key }}</dt>
141
148
  <dd>
142
- <span v-if="header.key === 'status'" class="tag fg-good">
149
+ <span v-if="header.key === 'status'" class="tag" :class="statusTagClass(lastRowClick.row[header.key])">
143
150
  {{ lastRowClick.row[header.key] }}
144
151
  </span>
145
152
  <template v-else>
@@ -170,15 +177,28 @@ const defaultColumnVisibility = {
170
177
 
171
178
  const enabledFeatures = ref([...defaultFeatures])
172
179
  const columnVisibility = ref({ ...defaultColumnVisibility })
173
- const layout = ref('default')
180
+ const layout = ref('priorities')
174
181
  const stickyColsOption = ref('2')
175
182
  const wideTable = ref(true)
176
183
  const dataConnected = ref(true)
177
184
  const data = ref([])
178
185
  const lastRowClick = ref(null)
179
186
  const lastQuery = ref(null)
187
+ const tableLayoutLabel = ref('')
180
188
  const rowDialogRef = ref(null)
181
189
 
190
+ const tableSectionTitle = computed(() => {
191
+ if (tableLayoutLabel.value) {
192
+ return `Table Example (${tableLayoutLabel.value})`
193
+ }
194
+ return 'Table Example'
195
+ })
196
+
197
+ const dataConnectionOptions = [
198
+ { label: 'Connect data', value: true },
199
+ { label: 'Disconnect data', value: false },
200
+ ]
201
+
182
202
  const featureOptions = [
183
203
  { value: 'pagination', label: 'Pagination' },
184
204
  { value: 'filters', label: 'Column filters' },
@@ -188,9 +208,9 @@ const featureOptions = [
188
208
  ]
189
209
 
190
210
  const layoutOptions = [
191
- { value: 'default', label: 'Default' },
192
211
  { value: 'priorities', label: 'Column priorities' },
193
212
  { value: 'scroll', label: 'Horizontal scroll' },
213
+ { value: 'browser', label: 'Browser' },
194
214
  ]
195
215
 
196
216
  const stickyColOptions = [
@@ -202,27 +222,18 @@ const stickyColOptions = [
202
222
 
203
223
  const baseHeaders = [
204
224
  { key: 'name', label: 'Name', sortable: true, filterable: true, filterType: 'text', width: '20%' },
205
- { key: 'age', label: 'Age', sortable: true, filterable: true, filterType: 'number' },
206
- { key: 'birthYear', label: 'Birth year', sortable: true, filterable: true, filterType: 'number' },
207
- { key: 'city', label: 'City', sortable: true, filterable: true, filterType: 'select', width: '200px' },
225
+ { key: 'age', label: 'Age', sortable: true, filterable: true, filterType: 'number', colPriority: 2 },
226
+ { key: 'birthYear', label: 'Birth year', sortable: true, filterable: true, filterType: 'number', colPriority: 3 },
227
+ { key: 'city', label: 'City', sortable: true, filterable: true, filterType: 'select', width: '200px', colPriority: 1 },
208
228
  ]
209
229
 
210
230
  const extendedHeaders = [
211
231
  { key: 'email', label: 'Email', sortable: true, filterable: true, filterType: 'text' },
212
- { key: 'department', label: 'Department', sortable: true, filterable: true, filterType: 'select' },
213
- { key: 'role', label: 'Role', sortable: true, filterable: true, filterType: 'text' },
214
- { key: 'status', label: 'Status', sortable: true, filterable: true, filterType: 'select' },
232
+ { key: 'department', label: 'Department', sortable: true, filterable: true, filterType: 'select', colPriority: 2 },
233
+ { key: 'role', label: 'Role', sortable: true, filterable: true, filterType: 'text', colPriority: 1 },
234
+ { key: 'status', label: 'Status', sortable: true, filterable: true, filterType: 'select', colPriority: 1 },
215
235
  ]
216
236
 
217
- const priorityByKey = {
218
- age: 2,
219
- birthYear: 3,
220
- city: 1,
221
- department: 2,
222
- role: 1,
223
- status: 1,
224
- }
225
-
226
237
  const stickyCols = computed(() => {
227
238
  if (layout.value !== 'scroll' || stickyColsOption.value === 'none') {
228
239
  return null
@@ -230,25 +241,11 @@ const stickyCols = computed(() => {
230
241
  return Number(stickyColsOption.value)
231
242
  })
232
243
 
233
- const useExtendedColumns = computed(() => layout.value === 'scroll' && wideTable.value)
234
-
235
- const headers = computed(() => {
236
- const columns = useExtendedColumns.value
237
- ? [...baseHeaders, ...extendedHeaders]
238
- : [...baseHeaders]
244
+ const useExtendedColumns = computed(() => wideTable.value)
239
245
 
240
- if (layout.value !== 'priorities') {
241
- return columns
242
- }
243
-
244
- return columns.map((header) => {
245
- const colPriority = priorityByKey[header.key]
246
- if (!colPriority) {
247
- return { ...header }
248
- }
249
- return { ...header, colPriority }
250
- })
251
- })
246
+ const headers = computed(() => (
247
+ useExtendedColumns.value ? [...baseHeaders, ...extendedHeaders] : [...baseHeaders]
248
+ ))
252
249
 
253
250
  const tableData = computed(() => {
254
251
  if (!dataConnected.value) {
@@ -309,10 +306,20 @@ function handleQueryChange(query) {
309
306
  lastQuery.value = query
310
307
  }
311
308
 
309
+ function statusTagClass(value) {
310
+ if (value === 'Active') {
311
+ return 'good'
312
+ }
313
+ if (value === 'Pending') {
314
+ return 'warning'
315
+ }
316
+ return null
317
+ }
318
+
312
319
  function resetControls() {
313
320
  enabledFeatures.value = [...defaultFeatures]
314
321
  columnVisibility.value = { ...defaultColumnVisibility }
315
- layout.value = 'default'
322
+ layout.value = 'priorities'
316
323
  stickyColsOption.value = '2'
317
324
  wideTable.value = true
318
325
  dataConnected.value = true
@@ -332,12 +339,6 @@ onMounted(() => {
332
339
  margin-top: 1.5rem;
333
340
  }
334
341
 
335
- .table-data-actions {
336
- display: flex;
337
- flex-wrap: wrap;
338
- gap: 0.5rem;
339
- }
340
-
341
342
  .table-events h4 {
342
343
  margin-top: 0;
343
344
  }