@citizenplane/pimp 10.6.2 → 10.8.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@citizenplane/pimp",
3
- "version": "10.6.2",
3
+ "version": "10.8.0",
4
4
  "scripts": {
5
5
  "dev": "storybook dev -p 8080",
6
6
  "build-storybook": "storybook build --output-dir ./docs",
@@ -41,7 +41,8 @@
41
41
  "primevue": "^4.5.4",
42
42
  "vue": "^3.5.27",
43
43
  "vue-bind-once": "^0.2.1",
44
- "vue-tel-input": "^9.5.0"
44
+ "vue-tel-input": "^9.5.0",
45
+ "web-haptics": "^0.0.6"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@babel/core": "^7.28.6",
@@ -8,6 +8,7 @@
8
8
  role="button"
9
9
  tabindex="0"
10
10
  :type="type"
11
+ @click="handleClick"
11
12
  >
12
13
  <span class="cpButton__body">
13
14
  <span v-if="isLoading" class="cpButton__loader"><cp-loader :color="Colors.NEUTRAL" /></span>
@@ -24,8 +25,10 @@
24
25
 
25
26
  <script setup lang="ts">
26
27
  import { computed, useSlots } from 'vue'
28
+ import { useWebHaptics } from 'web-haptics/vue'
27
29
 
28
30
  import { ButtonAppearances, ButtonTags, ButtonTypes } from '@/constants/Button'
31
+ import { Haptics } from '@/constants/Hapitcs'
29
32
 
30
33
  import CpLoader from '@/components/CpLoader.vue'
31
34
 
@@ -36,6 +39,7 @@ interface Props {
36
39
  appearance?: ButtonAppearances
37
40
  color?: Colors
38
41
  disabled?: boolean
42
+ enableHaptics?: boolean
39
43
  isLoading?: boolean
40
44
  isSquare?: boolean
41
45
  size?: Sizes
@@ -46,15 +50,13 @@ interface Props {
46
50
  const props = withDefaults(defineProps<Props>(), {
47
51
  appearance: ButtonAppearances.DEFAULT,
48
52
  color: undefined,
49
- disabled: false,
50
53
  tag: ButtonTags.BUTTON,
51
54
  type: ButtonTypes.BUTTON,
52
- isLoading: false,
53
- isSquare: false,
54
55
  size: Sizes.MD,
55
56
  })
56
57
 
57
58
  const slots = useSlots()
59
+ const { trigger } = useWebHaptics()
58
60
 
59
61
  const capitalizedAppearance = computed(() => capitalizeFirstLetter(props.appearance))
60
62
 
@@ -81,6 +83,12 @@ const dynamicClasses = computed(() => {
81
83
  },
82
84
  ]
83
85
  })
86
+
87
+ const handleClick = () => {
88
+ if (props.enableHaptics) {
89
+ trigger(Haptics.RIGID)
90
+ }
91
+ }
84
92
  </script>
85
93
 
86
94
  <style lang="scss">
@@ -110,6 +110,7 @@ const dynamicClasses = computed(() => {
110
110
  &__body {
111
111
  display: inline-flex;
112
112
  align-items: center;
113
+ justify-content: center;
113
114
  padding: var(--cp-selectable-body-padding);
114
115
  gap: var(--cp-selectable-body-gap);
115
116
  background-color: var(--cp-selectable-body-background-color);
@@ -118,6 +119,7 @@ const dynamicClasses = computed(() => {
118
119
  background-color 0.1s ease-out,
119
120
  box-shadow 0.1s ease-out,
120
121
  color 0.1s ease-out;
122
+ width: 100%;
121
123
  }
122
124
 
123
125
  &__icon i,
@@ -30,6 +30,9 @@
30
30
 
31
31
  <script setup lang="ts">
32
32
  import { computed, useId } from 'vue'
33
+ import { useWebHaptics } from 'web-haptics/vue'
34
+
35
+ import { Haptics } from '@/constants/Hapitcs'
33
36
 
34
37
  import CpTooltip from '@/components/CpTooltip.vue'
35
38
 
@@ -40,6 +43,7 @@ interface Props {
40
43
  autofocus?: boolean
41
44
  color?: string
42
45
  disabled?: boolean
46
+ enableHaptics?: boolean
43
47
  groupName?: string
44
48
  helper?: string
45
49
  isRequired?: boolean
@@ -68,6 +72,8 @@ const props = withDefaults(defineProps<Props>(), {
68
72
 
69
73
  const emit = defineEmits<Emits>()
70
74
 
75
+ const { trigger } = useWebHaptics()
76
+
71
77
  const switchUniqueId = useId()
72
78
 
73
79
  const capitalizedColor = computed(() => {
@@ -87,6 +93,10 @@ const computedClasses = computed(() => {
87
93
  })
88
94
 
89
95
  const handleClick = (value: boolean): void => {
96
+ if (props.enableHaptics) {
97
+ trigger(Haptics.SOFT)
98
+ }
99
+
90
100
  emit('update:modelValue', !value)
91
101
  }
92
102
  </script>
@@ -62,13 +62,18 @@
62
62
 
63
63
  <script setup lang="ts">
64
64
  import Toast from 'primevue/toast'
65
+ import ToastEventBus from 'primevue/toasteventbus'
66
+ import { onMounted } from 'vue'
67
+ import { useWebHaptics } from 'web-haptics/vue'
65
68
 
66
69
  import { CpToastTypes } from '@/constants/CpToastTypes'
70
+ import { Haptics } from '@/constants/Hapitcs'
67
71
 
68
72
  import { capitalizeFirstLetter } from '@/helpers'
69
73
 
70
74
  interface Message {
71
75
  detail?: string
76
+ enableHaptics?: boolean
72
77
  life?: number
73
78
  primaryAction?: {
74
79
  label: string
@@ -87,6 +92,8 @@ interface Message {
87
92
  summary: string
88
93
  }
89
94
 
95
+ onMounted(() => ToastEventBus.on('add', handleToastAdded))
96
+
90
97
  const displayTimer = (message: Message) => message.showTimer && message.life !== undefined
91
98
 
92
99
  const getDynamicClass = (severity: string) => `cpToast--is${capitalizeFirstLetter(severity)}`
@@ -129,6 +136,28 @@ const handleActionClick = (onClick: VoidFunction, closeCallback: VoidFunction) =
129
136
  }
130
137
 
131
138
  const handleMouse = () => true
139
+
140
+ const { trigger } = useWebHaptics()
141
+
142
+ const resolveHapticLevel = (severity: CpToastTypes) => {
143
+ switch (severity) {
144
+ case CpToastTypes.SUCCESS:
145
+ return Haptics.SUCCESS
146
+ case CpToastTypes.WARNING:
147
+ return Haptics.WARNING
148
+ case CpToastTypes.ERROR:
149
+ return Haptics.ERROR
150
+ case CpToastTypes.INFO:
151
+ default:
152
+ return Haptics.SOFT
153
+ }
154
+ }
155
+
156
+ const handleToastAdded = (message: Message) => {
157
+ if (message.enableHaptics) {
158
+ trigger(resolveHapticLevel(message.severity))
159
+ }
160
+ }
132
161
  </script>
133
162
 
134
163
  <style lang="scss">
@@ -0,0 +1,13 @@
1
+ export const Haptics = {
2
+ SUCCESS: 'success',
3
+ WARNING: 'warning',
4
+ ERROR: 'error',
5
+ LIGHT: 'light',
6
+ MEDIUM: 'medium',
7
+ HEAVY: 'heavy',
8
+ SOFT: 'soft',
9
+ RIGID: 'rigid',
10
+ SELECTION: 'selection',
11
+ NUDGE: 'nudge',
12
+ BUZZ: 'buzz',
13
+ }
@@ -112,6 +112,24 @@ export const WithIcons: Story = {
112
112
  }),
113
113
  }
114
114
 
115
+ export const WithHaptics: Story = {
116
+ args: { ...Default.args, enableHaptics: true },
117
+ render: (args) => ({
118
+ components: { CpButton },
119
+ setup() {
120
+ const onClick = () => {
121
+ console.log('Button clicked')
122
+ }
123
+ return { args, onClick }
124
+ },
125
+ template: `
126
+ <CpButton v-bind="args" @click="onClick">
127
+ Button with haptics
128
+ </CpButton>
129
+ `,
130
+ }),
131
+ }
132
+
115
133
  export const Loading: Story = {
116
134
  args: { ...Default.args, isLoading: true },
117
135
  render: defaultRender,
@@ -97,6 +97,38 @@ export const WithIcons: Story = {
97
97
  }),
98
98
  }
99
99
 
100
+ export const FullWidth: Story = {
101
+ args: {
102
+ appearance: 'secondary',
103
+ size: Sizes.MD,
104
+ disabled: false,
105
+ label: 'Label',
106
+ isSelected: true,
107
+ isExpanded: false,
108
+ },
109
+ render: (args) => ({
110
+ components: { CpSelectableButton },
111
+ setup() {
112
+ return { args }
113
+ },
114
+ template: `
115
+ <div style="display: flex; justify-content: center; align-items: center; min-height: 240px; width:500px;">
116
+ <CpSelectableButton
117
+ style="width: 100%;"
118
+ v-bind="args"
119
+ >
120
+ <template #leading-icon>
121
+ <CpIcon type="circle" />
122
+ </template>
123
+ <template #trailing-icon>
124
+ <CpIcon type="circle" />
125
+ </template>
126
+ </CpSelectableButton>
127
+ </div>
128
+ `,
129
+ }),
130
+ }
131
+
100
132
  export const Sizing: Story = {
101
133
  args: {
102
134
  disabled: false,
@@ -83,6 +83,28 @@ export const Default: Story = {
83
83
  }),
84
84
  }
85
85
 
86
+ export const WithHaptics: Story = {
87
+ args: {
88
+ ...Default.args,
89
+ enableHaptics: true,
90
+ },
91
+ render: (args) => ({
92
+ components: { CpSwitch },
93
+ setup() {
94
+ const value = ref(false)
95
+ return { args, value }
96
+ },
97
+ template: `
98
+ <div style="padding: 20px;">
99
+ <CpSwitch
100
+ v-model="value"
101
+ v-bind="args"
102
+ />
103
+ </div>
104
+ `,
105
+ }),
106
+ }
107
+
86
108
  export const Disabled: Story = {
87
109
  args: {
88
110
  ...Default.args,
@@ -19,6 +19,7 @@ const meta: Meta<typeof CpToast> = {
19
19
  args: {
20
20
  position: 'top-right',
21
21
  group: undefined,
22
+ enableHaptics: false,
22
23
  },
23
24
  }
24
25
 
@@ -36,6 +37,7 @@ export const Default: Story = {
36
37
  severity: CpToastTypes.SECONDARY,
37
38
  summary: `Hello i'm a cpToast !`,
38
39
  detail: 'This is a cpToast description.',
40
+ enableHaptics: true,
39
41
  }
40
42
 
41
43
  const notifySecondary = () => toast.add({ ...baseOptions })