@fiscozen/tab 0.1.11 → 1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @fiscozen/tab
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 98d491d: Updated FzTabs to reflect new design system specs
8
+
9
+ ### BREAKING CHANGES
10
+
11
+ `FzTabs` doesn't automatically handle tab overflow; it is now controlled by the user via the `tabStyle` prop. Now is developer responsibility to handle the overflow of the tabs based on his use cases.
12
+
13
+ ### NEW FEATURES
14
+ - New `tabStyle="fullWidth"` variant: the tab container stretches to fill the full available width and each tab expands equally with centered text
15
+ - The `tone` prop (`'neutral' | 'alert'`) is now set on individual `FzTab` components instead of the `FzTabs` container, allowing specific tabs to be highlighted independently
16
+ - New `environment` prop (`'backoffice' | 'frontoffice'`) for sizing, replacing the deprecated `size` prop
17
+ - New `tabStyle` prop (`'scroll' | 'picker' | 'fullWidth'`) to control tab style and overflow behavior
18
+ - The `change` event now also emits the selected button DOM element as a second argument: `(title: string, element: HTMLElement | null)`
19
+
20
+ ### DEPRECATED FEATURES
21
+ - The `size` prop is deprecated: use `environment` instead
22
+ - The `horizontalOverflow` prop is deprecated: use `tabStyle` instead
23
+
24
+ ### DOCUMENTATION
25
+ - Updated `FzTabs.mdx` documentation to reflect the new specifications
26
+
27
+ ## 0.1.12
28
+
29
+ ### Patch Changes
30
+
31
+ - 1a2df8c: Move @fiscozen/icons from dependencies to peerDependencies. Consumers now need to install @fiscozen/icons explicitly. This decouples icon updates from component version bumps.
32
+ - Updated dependencies [1a2df8c]
33
+ - @fiscozen/badge@1.0.1
34
+
3
35
  ## 0.1.11
4
36
 
5
37
  ### Patch Changes
package/package.json CHANGED
@@ -1,19 +1,21 @@
1
1
  {
2
2
  "name": "@fiscozen/tab",
3
- "version": "0.1.11",
3
+ "version": "1.0.0",
4
4
  "description": "Design System Tab component",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
7
7
  "keywords": [],
8
8
  "author": "Cristian Barraco",
9
9
  "dependencies": {
10
- "@fiscozen/composables": "1.0.1",
11
- "@fiscozen/badge": "1.0.0",
12
- "@fiscozen/style": "0.2.0"
10
+ "@fiscozen/badge": "1.0.1",
11
+ "@fiscozen/action": "1.1.1",
12
+ "@fiscozen/style": "0.2.0",
13
+ "@fiscozen/composables": "1.0.1"
13
14
  },
14
15
  "peerDependencies": {
15
16
  "tailwindcss": "^3.4.1",
16
- "vue": "^3.4.13"
17
+ "vue": "^3.4.13",
18
+ "@fiscozen/icons": "^0.1.37"
17
19
  },
18
20
  "devDependencies": {
19
21
  "@rushstack/eslint-patch": "^1.3.3",
@@ -31,10 +33,9 @@
31
33
  "vite-plugin-dts": "^3.8.3",
32
34
  "vitest": "^1.2.0",
33
35
  "vue-tsc": "^1.8.25",
34
- "@fiscozen/eslint-config": "^0.1.0",
35
- "@fiscozen/prettier-config": "^0.1.0",
36
36
  "@fiscozen/tsconfig": "^0.1.0",
37
- "@fiscozen/icons": "0.1.36"
37
+ "@fiscozen/eslint-config": "^0.1.0",
38
+ "@fiscozen/prettier-config": "^0.1.0"
38
39
  },
39
40
  "license": "MIT",
40
41
  "scripts": {
package/src/FzTabs.vue CHANGED
@@ -2,20 +2,29 @@
2
2
  <div :class="computedClassWrapper">
3
3
  <div :class="['flex', computedClass]">
4
4
  <div
5
- :class="[staticTabContainerClass, computedClass]"
5
+ :class="[
6
+ staticTabContainerClass,
7
+ computedClass,
8
+ overflowContainerClass,
9
+ ]"
6
10
  ref="tabContainer"
7
- @wheel="onWheel"
8
11
  >
9
- <template v-if="!horizontalOverflow && isOverflowing">
10
- <FzTabPicker :tabs="tabs" :size="size" />
11
- </template>
12
+ <FzTabPicker
13
+ v-if="effectiveTabStyle === 'picker'"
14
+ :tabs="tabs"
15
+ :environment="effectiveSize"
16
+ />
12
17
  <template v-else>
13
18
  <FzTabButton
14
19
  v-for="tab in tabs"
15
20
  :tab="tab"
16
21
  :key="tab.title"
17
- :size="size"
22
+ :environment="effectiveSize"
18
23
  :maxWidth="tab.maxWidth"
24
+ :tone="tab.tone"
25
+ type="tab"
26
+ :readonly="false"
27
+ :fullWidth="effectiveTabStyle === 'fullWidth'"
19
28
  />
20
29
  </template>
21
30
  <slot name="tabs-container-end" />
@@ -27,24 +36,18 @@
27
36
  </template>
28
37
 
29
38
  <script setup lang="ts">
30
- import {
31
- computed,
32
- ref,
33
- onMounted,
34
- provide,
35
- useSlots,
36
- watch,
37
- VNode,
38
- onBeforeUnmount,
39
- } from "vue";
40
- import { FzTabsProps, FzTabProps } from "./types";
39
+ import { computed, ref, onMounted, provide, useSlots, watch, VNode } from "vue";
40
+ import { FzTabsProps, FzTabProps, FzTabStyle } from "./types";
41
41
  import FzTabPicker from "./components/FzTabPicker.vue";
42
42
  import FzTabButton from "./components/FzTabButton.vue";
43
43
  import FzTab from "./FzTab.vue";
44
+ import { debugWarn, mapSizeToEnvironment } from "./common";
44
45
 
45
46
  const props = withDefaults(defineProps<FzTabsProps>(), {
46
- size: "sm",
47
47
  vertical: false,
48
+ horizontalOverflow: undefined,
49
+ tabStyle: "scroll",
50
+ isDebug: false,
48
51
  });
49
52
 
50
53
  const emit = defineEmits(["change"]);
@@ -52,7 +55,6 @@ const emit = defineEmits(["change"]);
52
55
  const slots = useSlots();
53
56
 
54
57
  const tabContainer = ref<HTMLElement>();
55
- const isOverflowing = ref(false);
56
58
  const selectedTab = ref("");
57
59
  provide("selectedTab", selectedTab);
58
60
 
@@ -80,8 +82,13 @@ const tabs = computed(() => {
80
82
  .filter((el): el is FzTabProps => el != null);
81
83
  });
82
84
 
83
- const staticTabContainerClass =
84
- "tab-container flex rounded-lg gap-8 p-2 bg-grey-100 w-fit max-w-full overflow-x-auto w-full sm:w-auto";
85
+ const staticTabContainerClass = computed(() => {
86
+ const base = "tab-container flex rounded-lg p-2 bg-grey-100";
87
+ if (effectiveTabStyle.value === "fullWidth") {
88
+ return `${base} w-full`;
89
+ }
90
+ return `${base} w-fit max-w-full w-full sm:w-auto`;
91
+ });
85
92
 
86
93
  const computedClass = computed(() => [
87
94
  props.vertical ? "flex-col" : "flex-row",
@@ -92,24 +99,81 @@ const computedClassWrapper = computed(() => [
92
99
  !props.vertical ? "flex-col" : "flex-row",
93
100
  ]);
94
101
 
95
- function onWheel(e: WheelEvent) {
96
- e.preventDefault();
97
- e.stopPropagation();
98
- if (e.deltaY > 0) tabContainer.value!.scrollLeft += 100;
99
- else tabContainer.value!.scrollLeft -= 100;
100
- }
102
+ const overflowContainerClass = computed(() => {
103
+ if (effectiveTabStyle.value === "scroll") {
104
+ return "overflow-x-auto";
105
+ }
106
+ return "";
107
+ });
101
108
 
102
- function updateIsOverflowing() {
103
- if (!tabContainer.value) return false;
109
+ /**
110
+ * Determines the effective size based on environment or deprecated size prop
111
+ * Priority: environment prop > size prop (deprecated) > default 'sm'
112
+ */
113
+ const effectiveSize = computed<"frontoffice" | "backoffice">(() => {
114
+ if (props.environment) {
115
+ return props.environment;
116
+ }
117
+ if (props.size) {
118
+ return mapSizeToEnvironment[props.size];
119
+ }
120
+ return "backoffice";
121
+ });
122
+
123
+ /**
124
+ * Determines the effective overflow mode
125
+ * Priority: overflowMode prop > horizontalOverflow prop (deprecated) > default 'scroll'
126
+ */
127
+ const effectiveTabStyle = computed<FzTabStyle>(() => {
128
+ if (
129
+ props.horizontalOverflow !== undefined &&
130
+ props.horizontalOverflow === false
131
+ )
132
+ return "picker";
133
+
134
+ return props.tabStyle;
135
+ });
136
+
137
+ /**
138
+ * Deprecation warning for size prop
139
+ */
140
+ watch(
141
+ () => props.size,
142
+ (size) => {
143
+ if (size !== undefined) {
144
+ debugWarn(
145
+ `[FzTabs] The "size" prop is deprecated and will be removed in a future version. ` +
146
+ `Please use environment="backoffice" instead of size="${size}".`,
147
+ props.isDebug,
148
+ );
149
+ }
150
+ },
151
+ { immediate: true },
152
+ );
104
153
 
105
- const parent = tabContainer.value.parentElement ?? document.body;
106
- isOverflowing.value = tabContainer.value.scrollWidth > parent.clientWidth;
107
- }
154
+ /**
155
+ * Deprecation warning for horizontalOverflow prop
156
+ */
157
+ watch(
158
+ () => props.horizontalOverflow,
159
+ (horizontalOverflow) => {
160
+ // Only warn if horizontalOverflow is explicitly set to true
161
+ // (false is treated as not set, since the default behavior is no overflow)
162
+ if (horizontalOverflow !== undefined) {
163
+ debugWarn(
164
+ `[FzTabs] The "horizontalOverflow" prop is deprecated and will be removed in a future version. `,
165
+ props.isDebug,
166
+ );
167
+ }
168
+ },
169
+ { immediate: true },
170
+ );
108
171
 
109
172
  onMounted(() => {
110
173
  if (tabs.value.length === 0) {
111
- console.warn(
174
+ debugWarn(
112
175
  "[Fiscozen Design System]: FzTabs must have at least one FzTab child",
176
+ props.isDebug,
113
177
  );
114
178
  } else {
115
179
  const findSelected = tabs.value.find((tab) => tab.initialSelected);
@@ -124,18 +188,12 @@ onMounted(() => {
124
188
  .filter((title, index, self) => self.indexOf(title) !== index);
125
189
 
126
190
  if (duplicateTitles.length) {
127
- console.warn(
191
+ debugWarn(
128
192
  `[Fiscozen Design System]: FzTabs has duplicate titles: ${duplicateTitles.join(", ")}, this may cause unexpected behavior.`,
193
+ props.isDebug,
129
194
  );
130
195
  }
131
196
  }
132
-
133
- updateIsOverflowing();
134
- window.addEventListener("resize", updateIsOverflowing);
135
- });
136
-
137
- onBeforeUnmount(() => {
138
- window.removeEventListener("resize", updateIsOverflowing);
139
197
  });
140
198
 
141
199
  watch(
@@ -148,25 +206,11 @@ watch(
148
206
  tabs.value[0].title;
149
207
  }
150
208
 
151
- const selectedTabElement = tabContainer.value!.querySelector(
209
+ const selectedTabElement = tabContainer.value?.querySelector(
152
210
  `button[title="${selectedTab.value}"]`,
153
211
  );
154
212
 
155
- if (selectedTabElement && isOverflowing.value) {
156
- selectedTabElement.scrollIntoView({
157
- behavior: "smooth",
158
- block: "nearest",
159
- inline: "center",
160
- });
161
- }
162
-
163
- emit("change", selectedTab.value);
213
+ emit("change", selectedTab.value, selectedTabElement);
164
214
  },
165
215
  );
166
216
  </script>
167
- <style scoped>
168
- .tab-container::-webkit-scrollbar {
169
- width: 0em;
170
- height: 0em;
171
- }
172
- </style>