@pequity/squirrel 9.0.0 → 10.0.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.
@@ -9,8 +9,8 @@ const index = require("./index.js");
9
9
  const _hoisted_1 = { class: "slot-wrapper empty:hidden" };
10
10
  const btnClasses = {
11
11
  slots: {
12
- button: "relative inline-block whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50",
13
- content: "flex items-center justify-center has-[.slot-wrapper:empty]:gap-0",
12
+ button: "relative inline-flex whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50",
13
+ content: "flex items-center justify-center has-[.slot-wrapper:empty]:gap-0 w-full",
14
14
  loader: "absolute bottom-0 left-0 right-0 top-0 flex items-center justify-center font-medium",
15
15
  icon: "shrink-0"
16
16
  },
@@ -42,24 +42,31 @@ const btnClasses = {
42
42
  loader: "text-p-red-50"
43
43
  },
44
44
  success: { button: "bg-p-green-40 text-white hover:bg-p-green-50", loader: "text-white" },
45
+ black: { button: "bg-night text-white hover:bg-p-gray-80", loader: "text-white" },
46
+ "black-outline-gradient": {
47
+ button: "bg-gradient-to-r from-[#17c0e8] via-[#8e8bda] to-[#f65ece] hover:bg-gradient-to-l p-px group",
48
+ content: "bg-night rounded-[calc(theme(borderRadius.DEFAULT)-1px)] text-white group-hover:bg-p-gray-80 transition-colors duration-300",
49
+ icon: "-mx-px",
50
+ loader: "text-white"
51
+ },
45
52
  "primary-link": { button: "bg-transparent text-primary underline hover:text-accent", loader: "text-p-blue-60" },
46
53
  "secondary-ghost": { button: "text-on-surface hover:bg-p-gray-20", loader: "text-p-purple-60" },
47
54
  "secondary-ghost-dark": { button: "text-white hover:bg-p-purple-50", loader: "text-p-blue-15" }
48
55
  },
49
56
  size: {
50
57
  sm: {
51
- button: "px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 text-sm leading-5 h-8",
52
- content: "gap-1",
58
+ button: "text-sm leading-5 h-8",
59
+ content: "px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 gap-1",
53
60
  icon: "text-base p-0.5"
54
61
  },
55
62
  md: {
56
- button: "px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 text-base h-10",
57
- content: "gap-2",
63
+ button: "text-base h-10",
64
+ content: "px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 gap-2",
58
65
  icon: "text-xl"
59
66
  },
60
67
  lg: {
61
- button: "px-6 has-[.slot-wrapper:empty]:px-3 py-3 text-lg leading-6 h-12",
62
- content: "gap-2.5",
68
+ button: "text-lg leading-6 h-12",
69
+ content: "px-6 has-[.slot-wrapper:empty]:px-3 py-3 gap-2.5",
63
70
  icon: "text-2xl"
64
71
  }
65
72
  }
@@ -64,7 +64,7 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
64
64
  (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(_ctx.items, (item, index) => {
65
65
  return vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", {
66
66
  key: String(item[_ctx.itemValue]),
67
- class: vue.normalizeClass(["inline-flex", { "*:px-0 *:py-0": _ctx.noPadding, grow: _ctx.grow }])
67
+ class: vue.normalizeClass(["inline-flex", { "[&_*]:px-0 [&_*]:py-0": _ctx.noPadding, grow: _ctx.grow }])
68
68
  }, [
69
69
  vue.createVNode(pBtn_vue_vue_type_script_setup_true_lang._sfc_main, {
70
70
  size: _ctx.size,
@@ -8,8 +8,8 @@ import { T } from "./index.js";
8
8
  const _hoisted_1 = { class: "slot-wrapper empty:hidden" };
9
9
  const btnClasses = {
10
10
  slots: {
11
- button: "relative inline-block whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50",
12
- content: "flex items-center justify-center has-[.slot-wrapper:empty]:gap-0",
11
+ button: "relative inline-flex whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50",
12
+ content: "flex items-center justify-center has-[.slot-wrapper:empty]:gap-0 w-full",
13
13
  loader: "absolute bottom-0 left-0 right-0 top-0 flex items-center justify-center font-medium",
14
14
  icon: "shrink-0"
15
15
  },
@@ -41,24 +41,31 @@ const btnClasses = {
41
41
  loader: "text-p-red-50"
42
42
  },
43
43
  success: { button: "bg-p-green-40 text-white hover:bg-p-green-50", loader: "text-white" },
44
+ black: { button: "bg-night text-white hover:bg-p-gray-80", loader: "text-white" },
45
+ "black-outline-gradient": {
46
+ button: "bg-gradient-to-r from-[#17c0e8] via-[#8e8bda] to-[#f65ece] hover:bg-gradient-to-l p-px group",
47
+ content: "bg-night rounded-[calc(theme(borderRadius.DEFAULT)-1px)] text-white group-hover:bg-p-gray-80 transition-colors duration-300",
48
+ icon: "-mx-px",
49
+ loader: "text-white"
50
+ },
44
51
  "primary-link": { button: "bg-transparent text-primary underline hover:text-accent", loader: "text-p-blue-60" },
45
52
  "secondary-ghost": { button: "text-on-surface hover:bg-p-gray-20", loader: "text-p-purple-60" },
46
53
  "secondary-ghost-dark": { button: "text-white hover:bg-p-purple-50", loader: "text-p-blue-15" }
47
54
  },
48
55
  size: {
49
56
  sm: {
50
- button: "px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 text-sm leading-5 h-8",
51
- content: "gap-1",
57
+ button: "text-sm leading-5 h-8",
58
+ content: "px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 gap-1",
52
59
  icon: "text-base p-0.5"
53
60
  },
54
61
  md: {
55
- button: "px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 text-base h-10",
56
- content: "gap-2",
62
+ button: "text-base h-10",
63
+ content: "px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 gap-2",
57
64
  icon: "text-xl"
58
65
  },
59
66
  lg: {
60
- button: "px-6 has-[.slot-wrapper:empty]:px-3 py-3 text-lg leading-6 h-12",
61
- content: "gap-2.5",
67
+ button: "text-lg leading-6 h-12",
68
+ content: "px-6 has-[.slot-wrapper:empty]:px-3 py-3 gap-2.5",
62
69
  icon: "text-2xl"
63
70
  }
64
71
  }
@@ -63,7 +63,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
63
63
  (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.items, (item, index) => {
64
64
  return withDirectives((openBlock(), createElementBlock("div", {
65
65
  key: String(item[_ctx.itemValue]),
66
- class: normalizeClass(["inline-flex", { "*:px-0 *:py-0": _ctx.noPadding, grow: _ctx.grow }])
66
+ class: normalizeClass(["inline-flex", { "[&_*]:px-0 [&_*]:py-0": _ctx.noPadding, grow: _ctx.grow }])
67
67
  }, [
68
68
  createVNode(_sfc_main$1, {
69
69
  size: _ctx.size,
@@ -41,6 +41,16 @@ declare const btn: import("tailwind-variants").TVReturnType<{
41
41
  readonly button: "bg-p-green-40 text-white hover:bg-p-green-50";
42
42
  readonly loader: "text-white";
43
43
  };
44
+ readonly black: {
45
+ readonly button: "bg-night text-white hover:bg-p-gray-80";
46
+ readonly loader: "text-white";
47
+ };
48
+ readonly 'black-outline-gradient': {
49
+ readonly button: "bg-gradient-to-r from-[#17c0e8] via-[#8e8bda] to-[#f65ece] hover:bg-gradient-to-l p-px group";
50
+ readonly content: "bg-night rounded-[calc(theme(borderRadius.DEFAULT)-1px)] text-white group-hover:bg-p-gray-80 transition-colors duration-300";
51
+ readonly icon: "-mx-px";
52
+ readonly loader: "text-white";
53
+ };
44
54
  readonly 'primary-link': {
45
55
  readonly button: "bg-transparent text-primary underline hover:text-accent";
46
56
  readonly loader: "text-p-blue-60";
@@ -56,24 +66,24 @@ declare const btn: import("tailwind-variants").TVReturnType<{
56
66
  };
57
67
  readonly size: {
58
68
  readonly sm: {
59
- readonly button: "px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 text-sm leading-5 h-8";
60
- readonly content: "gap-1";
69
+ readonly button: "text-sm leading-5 h-8";
70
+ readonly content: "px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 gap-1";
61
71
  readonly icon: "text-base p-0.5";
62
72
  };
63
73
  readonly md: {
64
- readonly button: "px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 text-base h-10";
65
- readonly content: "gap-2";
74
+ readonly button: "text-base h-10";
75
+ readonly content: "px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 gap-2";
66
76
  readonly icon: "text-xl";
67
77
  };
68
78
  readonly lg: {
69
- readonly button: "px-6 has-[.slot-wrapper:empty]:px-3 py-3 text-lg leading-6 h-12";
70
- readonly content: "gap-2.5";
79
+ readonly button: "text-lg leading-6 h-12";
80
+ readonly content: "px-6 has-[.slot-wrapper:empty]:px-3 py-3 gap-2.5";
71
81
  readonly icon: "text-2xl";
72
82
  };
73
83
  };
74
84
  }, {
75
- readonly button: "relative inline-block whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50";
76
- readonly content: "flex items-center justify-center has-[.slot-wrapper:empty]:gap-0";
85
+ readonly button: "relative inline-flex whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50";
86
+ readonly content: "flex items-center justify-center has-[.slot-wrapper:empty]:gap-0 w-full";
77
87
  readonly loader: "absolute bottom-0 left-0 right-0 top-0 flex items-center justify-center font-medium";
78
88
  readonly icon: "shrink-0";
79
89
  }, undefined, {
@@ -110,6 +120,16 @@ declare const btn: import("tailwind-variants").TVReturnType<{
110
120
  readonly button: "bg-p-green-40 text-white hover:bg-p-green-50";
111
121
  readonly loader: "text-white";
112
122
  };
123
+ readonly black: {
124
+ readonly button: "bg-night text-white hover:bg-p-gray-80";
125
+ readonly loader: "text-white";
126
+ };
127
+ readonly 'black-outline-gradient': {
128
+ readonly button: "bg-gradient-to-r from-[#17c0e8] via-[#8e8bda] to-[#f65ece] hover:bg-gradient-to-l p-px group";
129
+ readonly content: "bg-night rounded-[calc(theme(borderRadius.DEFAULT)-1px)] text-white group-hover:bg-p-gray-80 transition-colors duration-300";
130
+ readonly icon: "-mx-px";
131
+ readonly loader: "text-white";
132
+ };
113
133
  readonly 'primary-link': {
114
134
  readonly button: "bg-transparent text-primary underline hover:text-accent";
115
135
  readonly loader: "text-p-blue-60";
@@ -125,24 +145,24 @@ declare const btn: import("tailwind-variants").TVReturnType<{
125
145
  };
126
146
  readonly size: {
127
147
  readonly sm: {
128
- readonly button: "px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 text-sm leading-5 h-8";
129
- readonly content: "gap-1";
148
+ readonly button: "text-sm leading-5 h-8";
149
+ readonly content: "px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 gap-1";
130
150
  readonly icon: "text-base p-0.5";
131
151
  };
132
152
  readonly md: {
133
- readonly button: "px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 text-base h-10";
134
- readonly content: "gap-2";
153
+ readonly button: "text-base h-10";
154
+ readonly content: "px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 gap-2";
135
155
  readonly icon: "text-xl";
136
156
  };
137
157
  readonly lg: {
138
- readonly button: "px-6 has-[.slot-wrapper:empty]:px-3 py-3 text-lg leading-6 h-12";
139
- readonly content: "gap-2.5";
158
+ readonly button: "text-lg leading-6 h-12";
159
+ readonly content: "px-6 has-[.slot-wrapper:empty]:px-3 py-3 gap-2.5";
140
160
  readonly icon: "text-2xl";
141
161
  };
142
162
  };
143
163
  }, {
144
- readonly button: "relative inline-block whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50";
145
- readonly content: "flex items-center justify-center has-[.slot-wrapper:empty]:gap-0";
164
+ readonly button: "relative inline-flex whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50";
165
+ readonly content: "flex items-center justify-center has-[.slot-wrapper:empty]:gap-0 w-full";
146
166
  readonly loader: "absolute bottom-0 left-0 right-0 top-0 flex items-center justify-center font-medium";
147
167
  readonly icon: "shrink-0";
148
168
  }, import("tailwind-variants").TVReturnType<{
@@ -179,6 +199,16 @@ declare const btn: import("tailwind-variants").TVReturnType<{
179
199
  readonly button: "bg-p-green-40 text-white hover:bg-p-green-50";
180
200
  readonly loader: "text-white";
181
201
  };
202
+ readonly black: {
203
+ readonly button: "bg-night text-white hover:bg-p-gray-80";
204
+ readonly loader: "text-white";
205
+ };
206
+ readonly 'black-outline-gradient': {
207
+ readonly button: "bg-gradient-to-r from-[#17c0e8] via-[#8e8bda] to-[#f65ece] hover:bg-gradient-to-l p-px group";
208
+ readonly content: "bg-night rounded-[calc(theme(borderRadius.DEFAULT)-1px)] text-white group-hover:bg-p-gray-80 transition-colors duration-300";
209
+ readonly icon: "-mx-px";
210
+ readonly loader: "text-white";
211
+ };
182
212
  readonly 'primary-link': {
183
213
  readonly button: "bg-transparent text-primary underline hover:text-accent";
184
214
  readonly loader: "text-p-blue-60";
@@ -194,24 +224,24 @@ declare const btn: import("tailwind-variants").TVReturnType<{
194
224
  };
195
225
  readonly size: {
196
226
  readonly sm: {
197
- readonly button: "px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 text-sm leading-5 h-8";
198
- readonly content: "gap-1";
227
+ readonly button: "text-sm leading-5 h-8";
228
+ readonly content: "px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 gap-1";
199
229
  readonly icon: "text-base p-0.5";
200
230
  };
201
231
  readonly md: {
202
- readonly button: "px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 text-base h-10";
203
- readonly content: "gap-2";
232
+ readonly button: "text-base h-10";
233
+ readonly content: "px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 gap-2";
204
234
  readonly icon: "text-xl";
205
235
  };
206
236
  readonly lg: {
207
- readonly button: "px-6 has-[.slot-wrapper:empty]:px-3 py-3 text-lg leading-6 h-12";
208
- readonly content: "gap-2.5";
237
+ readonly button: "text-lg leading-6 h-12";
238
+ readonly content: "px-6 has-[.slot-wrapper:empty]:px-3 py-3 gap-2.5";
209
239
  readonly icon: "text-2xl";
210
240
  };
211
241
  };
212
242
  }, {
213
- readonly button: "relative inline-block whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50";
214
- readonly content: "flex items-center justify-center has-[.slot-wrapper:empty]:gap-0";
243
+ readonly button: "relative inline-flex whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50";
244
+ readonly content: "flex items-center justify-center has-[.slot-wrapper:empty]:gap-0 w-full";
215
245
  readonly loader: "absolute bottom-0 left-0 right-0 top-0 flex items-center justify-center font-medium";
216
246
  readonly icon: "shrink-0";
217
247
  }, undefined, unknown, unknown, undefined>>;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@pequity/squirrel",
3
3
  "description": "Squirrel component library",
4
- "version": "9.0.0",
5
- "packageManager": "pnpm@10.15.1",
4
+ "version": "10.0.1",
5
+ "packageManager": "pnpm@10.17.1",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "preinstall": "npx only-allow pnpm",
@@ -51,19 +51,19 @@
51
51
  "devDependencies": {
52
52
  "@commitlint/cli": "^19.8.1",
53
53
  "@commitlint/config-conventional": "^19.8.1",
54
- "@pequity/eslint-config": "^2.0.3",
54
+ "@pequity/eslint-config": "^2.0.4",
55
55
  "@playwright/test": "^1.55.0",
56
56
  "@semantic-release/changelog": "^6.0.3",
57
57
  "@semantic-release/git": "^10.0.1",
58
- "@storybook/addon-a11y": "^9.1.5",
59
- "@storybook/addon-docs": "^9.1.5",
60
- "@storybook/addon-links": "^9.1.5",
61
- "@storybook/addon-vitest": "^9.1.5",
62
- "@storybook/vue3-vite": "^9.1.5",
58
+ "@storybook/addon-a11y": "^9.1.7",
59
+ "@storybook/addon-docs": "^9.1.7",
60
+ "@storybook/addon-links": "^9.1.7",
61
+ "@storybook/addon-vitest": "^9.1.7",
62
+ "@storybook/vue3-vite": "^9.1.7",
63
63
  "@tanstack/vue-virtual": "3.13.12",
64
64
  "@types/jsdom": "^21.1.7",
65
65
  "@types/lodash-es": "^4.17.12",
66
- "@types/node": "^24.3.1",
66
+ "@types/node": "^24.5.2",
67
67
  "@vitejs/plugin-vue": "^6.0.1",
68
68
  "@vitest/browser": "3.2.4",
69
69
  "@vitest/coverage-v8": "^3.2.4",
@@ -71,14 +71,14 @@
71
71
  "@vue/test-utils": "^2.4.6",
72
72
  "@vuepic/vue-datepicker": "11.0.2",
73
73
  "autoprefixer": "^10.4.21",
74
- "eslint": "^9.35.0",
75
- "eslint-plugin-storybook": "^9.1.5",
74
+ "eslint": "^9.36.0",
75
+ "eslint-plugin-storybook": "^9.1.7",
76
76
  "floating-vue": "5.2.2",
77
77
  "glob": "^11.0.3",
78
78
  "husky": "^9.1.7",
79
- "iconify-icon": "^3.0.0",
80
- "jsdom": "^26.1.0",
81
- "lint-staged": "^16.1.6",
79
+ "iconify-icon": "^3.0.1",
80
+ "jsdom": "^27.0.0",
81
+ "lint-staged": "^16.2.0",
82
82
  "lodash-es": "4.17.21",
83
83
  "make-coverage-badge": "^1.2.0",
84
84
  "playwright": "^1.55.0",
@@ -87,19 +87,19 @@
87
87
  "prettier-plugin-tailwindcss": "^0.6.14",
88
88
  "resolve-tspaths": "^0.8.23",
89
89
  "rimraf": "^6.0.1",
90
- "sass": "^1.92.1",
91
- "semantic-release": "^24.2.8",
92
- "storybook": "^9.1.5",
90
+ "sass": "^1.93.1",
91
+ "semantic-release": "^24.2.9",
92
+ "storybook": "^9.1.7",
93
93
  "svgo": "^4.0.0",
94
94
  "tailwindcss": "^3.4.17",
95
95
  "typescript": "5.9.2",
96
- "vite": "^7.1.5",
96
+ "vite": "^7.1.7",
97
97
  "vitest": "^3.2.4",
98
98
  "vue": "3.5.21",
99
99
  "vue-currency-input": "3.2.1",
100
100
  "vue-router": "4.5.1",
101
101
  "vue-toastification": "2.0.0-rc.5",
102
- "vue-tsc": "3.0.6"
102
+ "vue-tsc": "3.0.8"
103
103
  },
104
104
  "dependencies": {
105
105
  "tailwind-merge": "^3.3.1",
@@ -110,8 +110,8 @@ describe('PActionBar.vue', () => {
110
110
  expect(dismissIcon.exists()).toBe(true);
111
111
  expect(actionBarDiv.find('.text-xs').text()).toBe('Clear All');
112
112
 
113
- const actionBtn = wrapper.find('button.inline-block');
114
- expect(actionBtn.exists()).toBe(true);
113
+ const actionBtn = buttons.find((button: VueWrapper) => button.text().includes('Say Hi'));
114
+ expect(actionBtn).toBeDefined();
115
115
  expect(actionBtn.text()).toContain('Say Hi');
116
116
  });
117
117
 
@@ -18,7 +18,7 @@ const ELEMENTS_MAP = {
18
18
 
19
19
  const DEFAULT_CLASSES_ARRAY = [
20
20
  'relative',
21
- 'inline-block',
21
+ 'inline-flex',
22
22
  'whitespace-nowrap',
23
23
  'outline-none',
24
24
  'disabled:opacity-50',
@@ -72,6 +72,19 @@ describe('PBtn.vue', () => {
72
72
  ['text-p-red-50', 'bg-surface', 'ring-1', 'ring-inset', 'ring-p-red-20', 'hover:bg-p-red-10/30'],
73
73
  ],
74
74
  ['success', ['text-white', 'bg-p-green-40', 'hover:bg-p-green-50']],
75
+ ['black', ['text-white', 'bg-night', 'hover:bg-p-gray-80']],
76
+ [
77
+ 'black-outline-gradient',
78
+ [
79
+ 'bg-gradient-to-r',
80
+ 'from-[#17c0e8]',
81
+ 'via-[#8e8bda]',
82
+ 'to-[#f65ece]',
83
+ 'hover:bg-gradient-to-l',
84
+ 'p-px',
85
+ 'group',
86
+ ],
87
+ ],
75
88
  ['primary-link', ['text-primary', 'bg-transparent', 'hover:text-accent', 'underline']],
76
89
  ['secondary-ghost', ['text-on-surface', 'hover:bg-p-gray-20']],
77
90
  ])(`renders a ${el} of type %s`, async (type, classes) => {
@@ -96,9 +109,49 @@ describe('PBtn.vue', () => {
96
109
  });
97
110
 
98
111
  it.each([
99
- ['sm', ['flex', 'items-center', 'justify-center', 'has-[.slot-wrapper:empty]:gap-0', 'gap-1']],
100
- ['md', ['flex', 'items-center', 'justify-center', 'has-[.slot-wrapper:empty]:gap-0', 'gap-2']],
101
- ['lg', ['flex', 'items-center', 'justify-center', 'has-[.slot-wrapper:empty]:gap-0', 'gap-2.5']],
112
+ [
113
+ 'sm',
114
+ [
115
+ 'flex',
116
+ 'items-center',
117
+ 'justify-center',
118
+ 'has-[.slot-wrapper:empty]:gap-0',
119
+ 'w-full',
120
+ 'px-3',
121
+ 'has-[.slot-wrapper:empty]:px-1.5',
122
+ 'py-1.5',
123
+ 'gap-1',
124
+ ],
125
+ ],
126
+ [
127
+ 'md',
128
+ [
129
+ 'flex',
130
+ 'items-center',
131
+ 'justify-center',
132
+ 'has-[.slot-wrapper:empty]:gap-0',
133
+ 'w-full',
134
+ 'px-6',
135
+ 'has-[.slot-wrapper:empty]:px-2.5',
136
+ 'has-[.slot-wrapper:empty]:py-2.5',
137
+ 'py-2',
138
+ 'gap-2',
139
+ ],
140
+ ],
141
+ [
142
+ 'lg',
143
+ [
144
+ 'flex',
145
+ 'items-center',
146
+ 'justify-center',
147
+ 'has-[.slot-wrapper:empty]:gap-0',
148
+ 'w-full',
149
+ 'px-6',
150
+ 'has-[.slot-wrapper:empty]:px-3',
151
+ 'py-3',
152
+ 'gap-2.5',
153
+ ],
154
+ ],
102
155
  ])('renders slots for content', async (size, classes) => {
103
156
  const wrapper = createWrapperFor(PBtn, {
104
157
  props: { size },
@@ -178,9 +231,9 @@ describe('PBtn.vue', () => {
178
231
  });
179
232
 
180
233
  it.each([
181
- ['sm', ['py-1.5', 'px-3', 'rounded', 'font-medium', 'text-sm', 'leading-5', 'h-8']],
182
- ['md', ['py-2', 'px-6', 'rounded', 'font-medium', 'text-base', 'h-10']],
183
- ['lg', ['py-3', 'px-6', 'rounded', 'font-medium', 'text-lg', 'leading-6', 'h-12']],
234
+ ['sm', ['rounded', 'font-medium', 'text-sm', 'leading-5', 'h-8']],
235
+ ['md', ['rounded', 'font-medium', 'text-base', 'h-10']],
236
+ ['lg', ['rounded', 'font-medium', 'text-lg', 'leading-6', 'h-12']],
184
237
  ])('renders a button of size %s', async (size, classes) => {
185
238
  const wrapper = createWrapperFor(PBtn, { props: { size }, slots: { default: `button` } });
186
239
 
@@ -294,17 +347,17 @@ describe('PBtn.vue', () => {
294
347
  ['sm', ['shrink-0', 'text-base', 'p-0.5'], ['has-[.slot-wrapper:empty]:px-1.5']],
295
348
  ['md', ['shrink-0', 'text-xl'], ['has-[.slot-wrapper:empty]:px-2.5']],
296
349
  ['lg', ['shrink-0', 'text-2xl'], ['has-[.slot-wrapper:empty]:px-3']],
297
- ])('renders a button with an icon without text of size %s', async (size, classes, buttonClasses) => {
350
+ ])('renders a button with an icon without text of size %s', async (size, classes, contentClasses) => {
298
351
  const wrapper = createWrapperFor(PBtn, {
299
352
  props: { size, icon: 'edit' },
300
353
  global: { stubs: { PIcon: true } },
301
354
  });
302
355
 
303
- const button = wrapper.find('button');
356
+ const content = wrapper.find('div');
304
357
  const icon = wrapper.findComponent({ name: 'PIcon' });
305
358
 
306
359
  expect(classes.every((c) => icon.classes().includes(c))).toBe(true);
307
- expect(buttonClasses.every((c) => button.classes().includes(c))).toBe(true);
360
+ expect(contentClasses.every((c) => content.classes().includes(c))).toBe(true);
308
361
  });
309
362
  });
310
363
  });
@@ -2,6 +2,24 @@ import PBtn from '@squirrel/components/p-btn/p-btn.vue';
2
2
  import { action } from 'storybook/actions';
3
3
  import { expect, within } from 'storybook/test';
4
4
 
5
+ const BUTTON_TYPES = [
6
+ 'primary',
7
+ 'secondary',
8
+ 'primary-outline',
9
+ 'secondary-outline',
10
+ 'secondary-outline-blue',
11
+ 'error',
12
+ 'error-outline',
13
+ 'success',
14
+ 'black',
15
+ 'black-outline-gradient',
16
+ 'primary-link',
17
+ 'secondary-ghost',
18
+ 'secondary-ghost-dark',
19
+ ];
20
+
21
+ const BUTTON_SIZES = ['sm', 'md', 'lg'];
22
+
5
23
  // You can also import an `md` file and use it as a story's description
6
24
  // import PBtnReadme from './PBtn.readme.md';
7
25
 
@@ -14,18 +32,7 @@ export default {
14
32
  argTypes: {
15
33
  type: {
16
34
  control: { type: 'select' },
17
- options: [
18
- 'primary',
19
- 'secondary',
20
- 'primary-outline',
21
- 'secondary-outline',
22
- 'secondary-outline-blue',
23
- 'error',
24
- 'error-outline',
25
- 'success',
26
- 'primary-link',
27
- 'secondary-ghost',
28
- ],
35
+ options: BUTTON_TYPES,
29
36
  },
30
37
  size: {
31
38
  control: { type: 'select' },
@@ -65,7 +72,7 @@ Primary.play = async ({ canvasElement }) => {
65
72
 
66
73
  await expect(primaryButton.innerText).toBe('Primary button');
67
74
  await expect(primaryButton).toHaveClass(
68
- 'relative inline-block outline-none disabled:opacity-50 disabled:cursor-default disabled:pointer-events-none py-2 px-6 rounded font-medium text-base text-surface bg-primary hover:bg-accent active:bg-p-blue-80'
75
+ 'relative inline-flex whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50 bg-primary text-surface hover:bg-accent active:bg-p-blue-80 text-base h-10'
69
76
  );
70
77
  await expect(primaryButton).toHaveStyle('background-color: rgb(0, 0, 120)');
71
78
  };
@@ -107,7 +114,7 @@ export const Icon = {
107
114
  setup() {
108
115
  return { args };
109
116
  },
110
- template: ` <PBtn v-bind="args" icon="archive">Icon Button</PBtn> `,
117
+ template: `<PBtn v-bind="args" icon="archive">Icon Button</PBtn>`,
111
118
  }),
112
119
  args: {
113
120
  size: 'md',
@@ -121,7 +128,7 @@ export const IconRight = {
121
128
  setup() {
122
129
  return { args };
123
130
  },
124
- template: ` <PBtn v-bind="args" icon-right="archive">Icon Button</PBtn> `,
131
+ template: `<PBtn v-bind="args" icon-right="archive">Icon Button</PBtn>`,
125
132
  }),
126
133
  args: {
127
134
  size: 'md',
@@ -135,7 +142,7 @@ export const IconWithoutContent = {
135
142
  setup() {
136
143
  return { args };
137
144
  },
138
- template: ` <PBtn v-bind="args" icon-right="archive"></PBtn> `,
145
+ template: `<PBtn v-bind="args" icon-right="archive"></PBtn>`,
139
146
  }),
140
147
  args: {
141
148
  size: 'md',
@@ -205,3 +212,59 @@ export const Ghost = {
205
212
  default: 'Ghost button',
206
213
  },
207
214
  };
215
+
216
+ export const Black = {
217
+ args: {
218
+ ...Primary.args,
219
+ type: 'black',
220
+ default: 'Black button',
221
+ },
222
+ };
223
+
224
+ export const BlackGradientOutline = {
225
+ args: {
226
+ ...Primary.args,
227
+ type: 'black-outline-gradient',
228
+ default: 'Black gradient outline',
229
+ },
230
+ };
231
+
232
+ export const IconButtonSizes = {
233
+ render: (args) => ({
234
+ components: { PBtn },
235
+ setup() {
236
+ return { args, BUTTON_TYPES, BUTTON_SIZES };
237
+ },
238
+ template: `
239
+ <div class="flex gap-4 items-center p-5 bg-white flex-wrap">
240
+ <template v-for="type in BUTTON_TYPES">
241
+ <PBtn v-for="size in BUTTON_SIZES" :key="type + '-' + size" :size="size" :type="type" icon="edit" :data-testid="type + '-' + size" />
242
+ </template>
243
+ </div>
244
+ `,
245
+ }),
246
+ play: async ({ canvasElement }) => {
247
+ const canvas = within(canvasElement);
248
+
249
+ // Wait a bit for styles to be fully applied
250
+ await new Promise((resolve) => setTimeout(resolve, 250));
251
+
252
+ const sizeConfigs = [
253
+ { size: 'sm', expectedPixels: 32 },
254
+ { size: 'md', expectedPixels: 40 },
255
+ { size: 'lg', expectedPixels: 48 },
256
+ ];
257
+
258
+ // Test icon button dimensions for all button types and sizes
259
+ for (const { size, expectedPixels } of sizeConfigs) {
260
+ for (const type of BUTTON_TYPES) {
261
+ const button = await canvas.getByTestId(`${type}-${size}`);
262
+ const rect = button.getBoundingClientRect();
263
+
264
+ // Test expected exact pixel dimensions
265
+ await expect(rect.height).toBe(expectedPixels);
266
+ await expect(rect.width).toBe(expectedPixels);
267
+ }
268
+ }
269
+ },
270
+ };
@@ -32,8 +32,8 @@ type Icon = InstanceType<typeof PIcon>['$props']['icon'];
32
32
  const btnClasses = {
33
33
  slots: {
34
34
  button:
35
- 'relative inline-block whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50',
36
- content: 'flex items-center justify-center has-[.slot-wrapper:empty]:gap-0',
35
+ 'relative inline-flex whitespace-nowrap rounded font-medium outline-none disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-default aria-disabled:opacity-50',
36
+ content: 'flex items-center justify-center has-[.slot-wrapper:empty]:gap-0 w-full',
37
37
  loader: 'absolute bottom-0 left-0 right-0 top-0 flex items-center justify-center font-medium',
38
38
  icon: 'shrink-0',
39
39
  },
@@ -67,24 +67,32 @@ const btnClasses = {
67
67
  loader: 'text-p-red-50',
68
68
  },
69
69
  success: { button: 'bg-p-green-40 text-white hover:bg-p-green-50', loader: 'text-white' },
70
+ black: { button: 'bg-night text-white hover:bg-p-gray-80', loader: 'text-white' },
71
+ 'black-outline-gradient': {
72
+ button: 'bg-gradient-to-r from-[#17c0e8] via-[#8e8bda] to-[#f65ece] hover:bg-gradient-to-l p-px group',
73
+ content:
74
+ 'bg-night rounded-[calc(theme(borderRadius.DEFAULT)-1px)] text-white group-hover:bg-p-gray-80 transition-colors duration-300',
75
+ icon: '-mx-px',
76
+ loader: 'text-white',
77
+ },
70
78
  'primary-link': { button: 'bg-transparent text-primary underline hover:text-accent', loader: 'text-p-blue-60' },
71
79
  'secondary-ghost': { button: 'text-on-surface hover:bg-p-gray-20', loader: 'text-p-purple-60' },
72
80
  'secondary-ghost-dark': { button: 'text-white hover:bg-p-purple-50', loader: 'text-p-blue-15' },
73
81
  },
74
82
  size: {
75
83
  sm: {
76
- button: 'px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 text-sm leading-5 h-8',
77
- content: 'gap-1',
84
+ button: 'text-sm leading-5 h-8',
85
+ content: 'px-3 has-[.slot-wrapper:empty]:px-1.5 py-1.5 gap-1',
78
86
  icon: 'text-base p-0.5',
79
87
  },
80
88
  md: {
81
- button: 'px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 text-base h-10',
82
- content: 'gap-2',
89
+ button: 'text-base h-10',
90
+ content: 'px-6 has-[.slot-wrapper:empty]:px-2.5 has-[.slot-wrapper:empty]:py-2.5 py-2 gap-2',
83
91
  icon: 'text-xl',
84
92
  },
85
93
  lg: {
86
- button: 'px-6 has-[.slot-wrapper:empty]:px-3 py-3 text-lg leading-6 h-12',
87
- content: 'gap-2.5',
94
+ button: 'text-lg leading-6 h-12',
95
+ content: 'px-6 has-[.slot-wrapper:empty]:px-3 py-3 gap-2.5',
88
96
  icon: 'text-2xl',
89
97
  },
90
98
  },
@@ -246,8 +246,8 @@ describe('PSelectBtn.vue', () => {
246
246
  const buttonDivs = wrapper.findAll('div.inline-flex');
247
247
 
248
248
  buttonDivs.forEach((buttonDiv) => {
249
- expect(buttonDiv.classes()).toContain('*:px-0');
250
- expect(buttonDiv.classes()).toContain('*:py-0');
249
+ expect(buttonDiv.classes()).toContain('[&_*]:px-0');
250
+ expect(buttonDiv.classes()).toContain('[&_*]:py-0');
251
251
  });
252
252
  });
253
253
 
@@ -4,7 +4,7 @@
4
4
  v-for="(item, index) in items"
5
5
  :key="String(item[itemValue])"
6
6
  v-tooltip="{ content: tooltipText(item) }"
7
- :class="['inline-flex', { '*:px-0 *:py-0': noPadding, grow }]"
7
+ :class="['inline-flex', { '[&_*]:px-0 [&_*]:py-0': noPadding, grow }]"
8
8
  >
9
9
  <PBtn
10
10
  :size="size"