@citizenplane/pimp 13.0.0 → 14.0.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 (66) hide show
  1. package/dist/pimp.es.js +4804 -4585
  2. package/dist/pimp.umd.js +44 -44
  3. package/dist/style.css +1 -1
  4. package/package.json +1 -1
  5. package/src/components/CpAccordion.vue +163 -0
  6. package/src/components/CpAccordionGroup.vue +51 -0
  7. package/src/components/CpBadge.vue +2 -2
  8. package/src/components/CpButton.vue +4 -3
  9. package/src/components/CpButtonGroup.vue +30 -2
  10. package/src/components/CpDate.vue +5 -4
  11. package/src/components/CpInput.vue +4 -4
  12. package/src/components/CpItemActions.vue +11 -9
  13. package/src/components/CpMultiselect.vue +3 -3
  14. package/src/components/CpPartnerBadge.vue +3 -2
  15. package/src/components/CpSelect.vue +5 -5
  16. package/src/components/CpTable.vue +2 -2
  17. package/src/components/CpTelInput.vue +4 -4
  18. package/src/components/CpTextarea.vue +3 -3
  19. package/src/components/CpTransitionCounter.vue +77 -0
  20. package/src/components/CpTransitionListItems.vue +41 -0
  21. package/src/components/CpTransitionSize.vue +88 -0
  22. package/src/components/CpTransitionSlide.vue +44 -0
  23. package/src/components/CpTransitionTabContent.vue +70 -0
  24. package/src/components/index.ts +16 -2
  25. package/src/constants/Sizes.ts +1 -11
  26. package/src/constants/index.ts +1 -1
  27. package/src/stories/BaseInputLabel.stories.ts +1 -1
  28. package/src/stories/CpAccordion.stories.ts +282 -0
  29. package/src/stories/CpAccordionGroup.stories.ts +223 -0
  30. package/src/stories/CpAirlineLogo.stories.ts +1 -1
  31. package/src/stories/CpAlert.stories.ts +1 -1
  32. package/src/stories/CpBadge.stories.ts +2 -4
  33. package/src/stories/CpButton.stories.ts +147 -146
  34. package/src/stories/CpCheckbox.stories.ts +1 -1
  35. package/src/stories/CpContextualMenu.stories.ts +1 -1
  36. package/src/stories/CpDate.stories.ts +1 -1
  37. package/src/stories/CpDatepicker.stories.ts +1 -1
  38. package/src/stories/CpDialog.stories.ts +1 -1
  39. package/src/stories/CpHeading.stories.ts +1 -1
  40. package/src/stories/CpIcon.stories.ts +1 -1
  41. package/src/stories/CpInput.stories.ts +2 -4
  42. package/src/stories/CpItemActions.stories.ts +1 -1
  43. package/src/stories/CpLoader.stories.ts +1 -1
  44. package/src/stories/CpMenuItem.stories.ts +1 -1
  45. package/src/stories/CpMultiselect.stories.ts +1 -1
  46. package/src/stories/CpPartnerBadge.stories.ts +3 -3
  47. package/src/stories/CpRadio.stories.ts +1 -1
  48. package/src/stories/CpSelect.stories.ts +4 -6
  49. package/src/stories/CpSelectMenu.stories.ts +1 -1
  50. package/src/stories/CpSelectableButton.stories.ts +4 -6
  51. package/src/stories/CpSwitch.stories.ts +1 -1
  52. package/src/stories/CpTable.stories.ts +1 -1
  53. package/src/stories/CpTableEmptyState.stories.ts +1 -1
  54. package/src/stories/CpTabs.stories.ts +1 -1
  55. package/src/stories/CpTelInput.stories.ts +1 -1
  56. package/src/stories/CpTextarea.stories.ts +1 -1
  57. package/src/stories/CpToast.stories.ts +1 -1
  58. package/src/stories/CpTooltip.stories.ts +1 -1
  59. package/src/stories/CpTransitionCounter.stories.ts +66 -0
  60. package/src/stories/{TransitionExpand.stories.ts → CpTransitionExpand.stories.ts} +11 -11
  61. package/src/stories/CpTransitionListItems.stories.ts +147 -0
  62. package/src/stories/CpTransitionSize.stories.ts +147 -0
  63. package/src/stories/CpTransitionSlide.stories.ts +65 -0
  64. package/src/stories/CpTransitionTabContent.stories.ts +78 -0
  65. package/src/stories/documentationStyles.ts +16 -0
  66. /package/src/components/{TransitionExpand.vue → CpTransitionExpand.vue} +0 -0
@@ -6,7 +6,7 @@ import CpButton from '@/components/CpButton.vue'
6
6
  import CpContextualMenu from '@/components/CpContextualMenu.vue'
7
7
 
8
8
  const meta = {
9
- title: 'CpContextualMenu',
9
+ title: 'Menu/CpContextualMenu',
10
10
  component: CpContextualMenu,
11
11
  parameters: {
12
12
  docs: {
@@ -5,7 +5,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpDate from '@/components/CpDate.vue'
6
6
 
7
7
  const meta = {
8
- title: 'CpDate',
8
+ title: 'Form/CpDate',
9
9
  component: CpDate,
10
10
  argTypes: {
11
11
  size: {
@@ -5,7 +5,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpDatepicker from '@/components/CpDatepicker.vue'
6
6
 
7
7
  const meta = {
8
- title: 'CpDatepicker',
8
+ title: 'Form/CpDatepicker',
9
9
  component: CpDatepicker,
10
10
 
11
11
  argTypes: {
@@ -5,7 +5,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpDialog from '@/components/CpDialog.vue'
6
6
 
7
7
  const meta = {
8
- title: 'CpDialog',
8
+ title: 'Navigation/CpDialog',
9
9
  component: CpDialog,
10
10
  argTypes: {
11
11
  maxWidth: {
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
3
3
  import CpHeading from '@/components/CpHeading.vue'
4
4
 
5
5
  const meta = {
6
- title: 'CpHeading',
6
+ title: 'Visual/CpHeading',
7
7
  component: CpHeading,
8
8
  argTypes: {
9
9
  headingLevel: {
@@ -30,7 +30,7 @@ const iconsOptions = [
30
30
  ].sort()
31
31
 
32
32
  const meta = {
33
- title: 'CpIcon',
33
+ title: 'Visual/CpIcon',
34
34
  component: CpIcon,
35
35
  argTypes: {
36
36
  type: {
@@ -5,10 +5,8 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpIcon from '@/components/CpIcon.vue'
6
6
  import CpInput from '@/components/CpInput.vue'
7
7
 
8
- import { Sizes } from '@/constants'
9
-
10
8
  const meta = {
11
- title: 'CpInput',
9
+ title: 'Form/CpInput',
12
10
  component: CpInput,
13
11
  argTypes: {
14
12
  modelValue: {
@@ -63,7 +61,7 @@ export const Default: Story = {
63
61
  args: {
64
62
  label: 'Input Label',
65
63
  placeholder: 'Enter text here',
66
- size: Sizes.MD,
64
+ size: 'md',
67
65
  type: 'text',
68
66
  required: false,
69
67
  disabled: false,
@@ -6,7 +6,7 @@ import type { MenuItem } from 'primevue/menuitem'
6
6
  import CpItemActions from '@/components/CpItemActions.vue'
7
7
 
8
8
  const meta = {
9
- title: 'CpItemActions',
9
+ title: 'Menu/CpItemActions',
10
10
  component: CpItemActions,
11
11
  argTypes: {
12
12
  actions: {
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
3
3
  import CpLoader from '@/components/CpLoader.vue'
4
4
 
5
5
  const meta = {
6
- title: 'CpLoader',
6
+ title: 'Visual/CpLoader',
7
7
  component: CpLoader,
8
8
  argTypes: {
9
9
  size: {
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
3
3
  import CpMenuItem from '@/components/CpMenuItem.vue'
4
4
 
5
5
  const meta = {
6
- title: 'CpMenuItem',
6
+ title: 'Menu/CpMenuItem',
7
7
  component: CpMenuItem,
8
8
  argTypes: {
9
9
  label: {
@@ -51,7 +51,7 @@ const airlineOptions: IOption[] = [
51
51
  ]
52
52
 
53
53
  const meta = {
54
- title: 'CpMultiSelect',
54
+ title: 'Form/CpMultiSelect',
55
55
  component: CpMultiselect,
56
56
  argTypes: {
57
57
  label: {
@@ -2,10 +2,10 @@ import type { Meta, StoryObj } from '@storybook/vue3'
2
2
 
3
3
  import CpPartnerBadge from '@/components/CpPartnerBadge.vue'
4
4
 
5
- import { PartnerTypes, Sizes } from '@/constants'
5
+ import { PartnerTypes } from '@/constants'
6
6
 
7
7
  const meta = {
8
- title: 'CpPartnerBadge',
8
+ title: 'Visual/CpPartnerBadge',
9
9
  component: CpPartnerBadge,
10
10
  parameters: {
11
11
  docs: {
@@ -27,7 +27,7 @@ const meta = {
27
27
  },
28
28
  size: {
29
29
  control: 'select',
30
- options: Object.values(Sizes),
30
+ options: ['2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl'],
31
31
  description: 'The size of the badge',
32
32
  table: {
33
33
  type: { summary: 'string' },
@@ -5,7 +5,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpRadio from '@/components/CpRadio.vue'
6
6
 
7
7
  const meta = {
8
- title: 'CpRadio',
8
+ title: 'Form/CpRadio',
9
9
  component: CpRadio,
10
10
  argTypes: {
11
11
  modelValue: {
@@ -5,10 +5,8 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpInput from '@/components/CpInput.vue'
6
6
  import CpSelect from '@/components/CpSelect.vue'
7
7
 
8
- import { Sizes } from '@/constants'
9
-
10
8
  const meta = {
11
- title: 'CpSelect',
9
+ title: 'Form/CpSelect',
12
10
  component: CpSelect,
13
11
  argTypes: {
14
12
  modelValue: {
@@ -91,7 +89,7 @@ export const Default: Story = {
91
89
  disabled: false,
92
90
  isInvalid: false,
93
91
  errorMessage: '',
94
- size: Sizes.MD,
92
+ size: 'md',
95
93
  autocomplete: 'on',
96
94
  name: 'select-field',
97
95
  },
@@ -137,14 +135,14 @@ export const Disabled: Story = {
137
135
  export const Small: Story = {
138
136
  args: {
139
137
  ...Default.args,
140
- size: Sizes.SM,
138
+ size: 'sm',
141
139
  },
142
140
  }
143
141
 
144
142
  export const Large: Story = {
145
143
  args: {
146
144
  ...Default.args,
147
- size: Sizes.LG,
145
+ size: 'lg',
148
146
  },
149
147
  }
150
148
 
@@ -5,7 +5,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpSelectMenu from '@/components/CpSelectMenu.vue'
6
6
 
7
7
  const meta = {
8
- title: 'CpSelectMenu',
8
+ title: 'Form/CpSelectMenu',
9
9
  component: CpSelectMenu,
10
10
  argTypes: {
11
11
  values: {
@@ -4,10 +4,8 @@ import type { Meta, StoryObj } from '@storybook/vue3'
4
4
 
5
5
  import CpSelectableButton from '@/components/CpSelectableButton.vue'
6
6
 
7
- import { Sizes } from '@/constants'
8
-
9
7
  const meta = {
10
- title: 'CpSelectableButton',
8
+ title: 'Form/CpSelectableButton',
11
9
  component: CpSelectableButton,
12
10
  argTypes: {
13
11
  appearance: {
@@ -45,7 +43,7 @@ type Story = StoryObj<typeof meta>
45
43
  export const Default: Story = {
46
44
  args: {
47
45
  appearance: 'primary',
48
- size: Sizes.MD,
46
+ size: 'md',
49
47
  disabled: false,
50
48
  label: 'Label',
51
49
  isSelected: false,
@@ -69,7 +67,7 @@ export const Default: Story = {
69
67
  export const WithIcons: Story = {
70
68
  args: {
71
69
  appearance: 'primary',
72
- size: Sizes.MD,
70
+ size: 'md',
73
71
  disabled: false,
74
72
  label: 'Label',
75
73
  isSelected: false,
@@ -100,7 +98,7 @@ export const WithIcons: Story = {
100
98
  export const FullWidth: Story = {
101
99
  args: {
102
100
  appearance: 'secondary',
103
- size: Sizes.MD,
101
+ size: 'md',
104
102
  disabled: false,
105
103
  label: 'Label',
106
104
  isSelected: true,
@@ -5,7 +5,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpSwitch from '@/components/CpSwitch.vue'
6
6
 
7
7
  const meta = {
8
- title: 'CpSwitch',
8
+ title: 'Form/CpSwitch',
9
9
  component: CpSwitch,
10
10
  argTypes: {
11
11
  autofocus: {
@@ -8,7 +8,7 @@ import CpTable from '@/components/CpTable.vue'
8
8
  import { PAGINATION_FORMATS } from '@/constants'
9
9
 
10
10
  const meta = {
11
- title: 'CpTable',
11
+ title: 'Data display/CpTable',
12
12
  component: CpTable,
13
13
  parameters: {
14
14
  backgrounds: {
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
3
3
  import CpTableEmptyState from '@/components/CpTableEmptyState.vue'
4
4
 
5
5
  const meta = {
6
- title: 'CpTableEmptyState',
6
+ title: 'Data display/CpTableEmptyState',
7
7
  component: CpTableEmptyState,
8
8
  argTypes: {
9
9
  placeholder: {
@@ -6,7 +6,7 @@ import type { Args, Meta, StoryObj } from '@storybook/vue3'
6
6
  import CpTabs from '@/components/CpTabs.vue'
7
7
 
8
8
  const meta = {
9
- title: 'CpTabs',
9
+ title: 'Navigation/CpTabs',
10
10
  component: CpTabs,
11
11
  argTypes: {
12
12
  defaultActiveIndex: {
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
3
3
  import CpTelInput from '@/components/CpTelInput.vue'
4
4
 
5
5
  const meta = {
6
- title: 'CpTelInput',
6
+ title: 'Form/CpTelInput',
7
7
  component: CpTelInput,
8
8
  argTypes: {
9
9
  telModel: {
@@ -5,7 +5,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
5
5
  import CpTextarea from '@/components/CpTextarea.vue'
6
6
 
7
7
  const meta = {
8
- title: 'CpTextarea',
8
+ title: 'Form/CpTextarea',
9
9
  component: CpTextarea,
10
10
  argTypes: {
11
11
  modelValue: {
@@ -8,7 +8,7 @@ import { CpToastTypes } from '@/constants/CpToastTypes'
8
8
  import CpToast from '@/components/CpToast.vue'
9
9
 
10
10
  const meta: Meta<typeof CpToast> = {
11
- title: 'CpToast',
11
+ title: 'Feedback/CpToast',
12
12
  component: CpToast,
13
13
  argTypes: {
14
14
  position: {
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/vue3'
3
3
  import CpTooltip from '@/components/CpTooltip.vue'
4
4
 
5
5
  const meta = {
6
- title: 'CpTooltip',
6
+ title: 'Feedback/CpTooltip',
7
7
  component: CpTooltip,
8
8
  argTypes: {
9
9
  content: {
@@ -0,0 +1,66 @@
1
+ import { computed, ref } from 'vue'
2
+
3
+ import type { Meta, StoryObj } from '@storybook/vue3'
4
+
5
+ import CpButton from '@/components/CpButton.vue'
6
+ import CpIcon from '@/components/CpIcon.vue'
7
+ import CpTransitionCounter from '@/components/CpTransitionCounter.vue'
8
+
9
+ const meta = {
10
+ title: 'Transitions/CpTransitionCounter',
11
+ component: CpTransitionCounter,
12
+ argTypes: {
13
+ duration: {
14
+ control: { type: 'number', min: 0, max: 2000, step: 50 },
15
+ },
16
+ counterNumber: { table: { disable: true } },
17
+ },
18
+ } satisfies Meta<typeof CpTransitionCounter>
19
+
20
+ export default meta
21
+ type Story = StoryObj<typeof meta>
22
+
23
+ const rowStyle = 'padding: 24px; display: inline-flex; align-items: center; justify-content: center; gap: 24px;'
24
+
25
+ const counterTextStyle = 'font-weight: bold; font-size: 4.5rem; line-height: 1; font-variant-numeric: tabular-nums;'
26
+
27
+ export const Default: Story = {
28
+ args: {
29
+ duration: 150,
30
+ },
31
+ render: (args) => ({
32
+ components: { CpTransitionCounter, CpButton, CpIcon },
33
+ setup() {
34
+ const count = ref(1)
35
+
36
+ const increment = () => count.value++
37
+ const decrement = () => {
38
+ if (count.value <= 0) return
39
+ count.value--
40
+ }
41
+
42
+ const isDecrementDisabled = computed(() => count.value <= 1)
43
+
44
+ return { args, count, increment, decrement, isDecrementDisabled }
45
+ },
46
+ template: `
47
+ <div style="${rowStyle}">
48
+ <CpButton appearance="tertiary" color="accent" size="lg" :disabled="isDecrementDisabled" @click="decrement">
49
+ <template #leading-icon>
50
+ <CpIcon type="minus" />
51
+ </template>
52
+ </CpButton>
53
+ <CpTransitionCounter :counter-number="count" :duration="args.duration">
54
+ <span style="${counterTextStyle}">
55
+ {{ count }}
56
+ </span>
57
+ </CpTransitionCounter>
58
+ <CpButton appearance="tertiary" color="accent" size="lg" @click="increment">
59
+ <template #leading-icon>
60
+ <CpIcon type="plus" />
61
+ </template>
62
+ </CpButton>
63
+ </div>
64
+ `,
65
+ }),
66
+ }
@@ -2,15 +2,15 @@ import { ref } from 'vue'
2
2
 
3
3
  import type { Meta, StoryObj } from '@storybook/vue3'
4
4
 
5
- import TransitionExpand from '@/components/TransitionExpand.vue'
5
+ import CpTransitionExpand from '@/components/CpTransitionExpand.vue'
6
6
 
7
7
  const meta = {
8
- title: 'TransitionExpand',
9
- component: TransitionExpand,
8
+ title: 'Transitions/CpTransitionExpand',
9
+ component: CpTransitionExpand,
10
10
  argTypes: {
11
11
  // No props to document as this is a utility component
12
12
  },
13
- } satisfies Meta<typeof TransitionExpand>
13
+ } satisfies Meta<typeof CpTransitionExpand>
14
14
 
15
15
  export default meta
16
16
  type Story = StoryObj<typeof meta>
@@ -19,7 +19,7 @@ const wrapperStyle = 'display: flex; flex-direction: column; align-items: center
19
19
 
20
20
  export const Default: Story = {
21
21
  render: () => ({
22
- components: { TransitionExpand },
22
+ components: { CpTransitionExpand },
23
23
  setup() {
24
24
  const isExpanded = ref(false)
25
25
  return { isExpanded }
@@ -34,7 +34,7 @@ export const Default: Story = {
34
34
  {{ isExpanded ? 'Collapse' : 'Expand' }}
35
35
  </CpButton>
36
36
 
37
- <TransitionExpand>
37
+ <CpTransitionExpand>
38
38
  <div v-if="isExpanded" style="
39
39
  background: #F3F4F6;
40
40
  border-radius: 6px;
@@ -42,10 +42,10 @@ export const Default: Story = {
42
42
  <h3 style="margin: 0 0 8px 0;">Expanded Content</h3>
43
43
  <p style="margin: 0;">
44
44
  This content will smoothly expand and collapse with a nice animation.
45
- The height transition is handled automatically by the TransitionExpand component.
45
+ The height transition is handled automatically by the CpTransitionExpand component.
46
46
  </p>
47
47
  </div>
48
- </TransitionExpand>
48
+ </CpTransitionExpand>
49
49
  </div>
50
50
  `,
51
51
  }),
@@ -53,7 +53,7 @@ export const Default: Story = {
53
53
 
54
54
  export const WithLongContent: Story = {
55
55
  render: () => ({
56
- components: { TransitionExpand },
56
+ components: { CpTransitionExpand },
57
57
  setup() {
58
58
  const isExpanded = ref(false)
59
59
  return { isExpanded }
@@ -68,7 +68,7 @@ export const WithLongContent: Story = {
68
68
  {{ isExpanded ? 'Collapse' : 'Expand' }}
69
69
  </CpButton>
70
70
 
71
- <TransitionExpand>
71
+ <CpTransitionExpand>
72
72
  <div v-if="isExpanded" style="
73
73
  background: #F3F4F6;
74
74
  border-radius: 6px;
@@ -80,7 +80,7 @@ export const WithLongContent: Story = {
80
80
  nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
81
81
  </p>
82
82
  </div>
83
- </TransitionExpand>
83
+ </CpTransitionExpand>
84
84
  </div>
85
85
  `,
86
86
  }),
@@ -0,0 +1,147 @@
1
+ import { ref } from 'vue'
2
+
3
+ import type { Meta, StoryObj } from '@storybook/vue3'
4
+ import type { MenuItem } from 'primevue/menuitem'
5
+
6
+ import CpButton from '@/components/CpButton.vue'
7
+ import CpIcon from '@/components/CpIcon.vue'
8
+ import CpItemActions from '@/components/CpItemActions.vue'
9
+ import CpTransitionListItems from '@/components/CpTransitionListItems.vue'
10
+
11
+ interface LineItem {
12
+ id: string
13
+ label: string
14
+ }
15
+
16
+ const meta = {
17
+ title: 'Transitions/CpTransitionListItems',
18
+ component: CpTransitionListItems,
19
+ argTypes: {
20
+ disableOnLoad: {
21
+ control: 'boolean',
22
+ description: 'Skips enter transition on first paint',
23
+ },
24
+ },
25
+ } satisfies Meta<typeof CpTransitionListItems>
26
+
27
+ export default meta
28
+ type Story = StoryObj<typeof meta>
29
+
30
+ const rowStyle = `
31
+ display: flex;
32
+ align-items: center;
33
+ min-height: 48px;
34
+ min-width: 100%;
35
+ padding: 16px;
36
+ margin-bottom: 12px;
37
+ border: 1px solid #e5e7eb;
38
+ border-radius: 12px;
39
+ background: #fff;
40
+ box-shadow: var(--cp-shadows-sm);
41
+ `
42
+
43
+ const fabWrapperStyle = {
44
+ position: 'fixed',
45
+ bottom: '50px',
46
+ right: '50px',
47
+ zIndex: 2,
48
+ display: 'flex',
49
+ flexDirection: 'column',
50
+ gap: '12px',
51
+ alignItems: 'flex-end',
52
+ }
53
+
54
+ export const Default: Story = {
55
+ args: {
56
+ disableOnLoad: false,
57
+ },
58
+ render: (args) => ({
59
+ components: { CpTransitionListItems, CpItemActions, CpButton, CpIcon },
60
+ setup() {
61
+ let idSeq = 0
62
+ const nextId = () => {
63
+ idSeq += 1
64
+ return `line-${idSeq}`
65
+ }
66
+
67
+ const items = ref<LineItem[]>([
68
+ { id: nextId(), label: 'Line 1' },
69
+ { id: nextId(), label: 'Line 2' },
70
+ { id: nextId(), label: 'Line 3' },
71
+ ])
72
+
73
+ const removeItem = (id: string) => {
74
+ items.value = items.value.filter((i) => i.id !== id)
75
+ }
76
+
77
+ const addLine = () => {
78
+ items.value.push({
79
+ id: nextId(),
80
+ label: `Line ${items.value.length + 1}`,
81
+ })
82
+ }
83
+
84
+ const shuffleItems = () => {
85
+ const arr = [...items.value]
86
+ for (let i = arr.length - 1; i > 0; i--) {
87
+ const j = Math.floor(Math.random() * (i + 1))
88
+ ;[arr[i], arr[j]] = [arr[j], arr[i]]
89
+ }
90
+ items.value = arr
91
+ }
92
+
93
+ const deleteActionsFor = (id: string): MenuItem[] => [
94
+ {
95
+ icon: 'trash-2',
96
+ label: 'Delete',
97
+ isCritical: true,
98
+ command: () => removeItem(id),
99
+ },
100
+ ]
101
+
102
+ return { args, items, addLine, shuffleItems, deleteActionsFor, rowStyle, fabWrapperStyle }
103
+ },
104
+ template: `
105
+ <div style="padding: 24px; min-width: 400px; min-height: 280px; padding-bottom: 120px;">
106
+ <div style="position: relative;">
107
+ <CpTransitionListItems v-bind="args">
108
+ <div
109
+ v-for="item in items"
110
+ :key="item.id"
111
+ cp-item-actions-trigger
112
+ :style="rowStyle"
113
+ >
114
+ <span style="font-size: 15px;">{{ item.label }}</span>
115
+ <CpItemActions :actions="deleteActionsFor(item.id)" style="right: var(--cp-spacing-lg)" />
116
+ </div>
117
+ </CpTransitionListItems>
118
+ </div>
119
+ <div :style="fabWrapperStyle">
120
+ <CpButton
121
+ appearance="secondary"
122
+ size="lg"
123
+ is-square
124
+ aria-label="Shuffle order"
125
+ @click="shuffleItems"
126
+ >
127
+ <template #leading-icon>
128
+ <CpIcon type="refresh-cw" />
129
+ </template>
130
+ </CpButton>
131
+ <CpButton
132
+ appearance="primary"
133
+ color="accent"
134
+ size="lg"
135
+ is-square
136
+ aria-label="Add line"
137
+ @click="addLine"
138
+ >
139
+ <template #leading-icon>
140
+ <CpIcon type="plus" />
141
+ </template>
142
+ </CpButton>
143
+ </div>
144
+ </div>
145
+ `,
146
+ }),
147
+ }