vueless 0.0.674 → 0.0.676

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 (88) hide show
  1. package/bin/commands/create.js +77 -0
  2. package/bin/commands/index.js +2 -0
  3. package/bin/commands/init.js +4 -8
  4. package/bin/constants.js +2 -5
  5. package/bin/index.js +1 -1
  6. package/bin/utils/dataUtils.js +53 -0
  7. package/bin/utils/formatUtil.js +48 -0
  8. package/composables/useUI.ts +12 -1
  9. package/constants.js +6 -0
  10. package/package.json +2 -2
  11. package/plugin-vite.js +3 -1
  12. package/types.ts +1 -1
  13. package/ui.boilerplate/storybook/stories.ts +1 -1
  14. package/ui.boilerplate/types.ts +1 -1
  15. package/ui.button/UButton.vue +9 -6
  16. package/ui.button/types.ts +1 -1
  17. package/ui.button-link/ULink.vue +37 -62
  18. package/ui.button-link/config.ts +8 -35
  19. package/ui.button-link/storybook/stories.ts +0 -22
  20. package/ui.button-link/types.ts +1 -11
  21. package/ui.button-toggle/types.ts +1 -1
  22. package/ui.button-toggle-item/types.ts +1 -1
  23. package/ui.container-accordion/types.ts +1 -1
  24. package/ui.container-card/types.ts +1 -1
  25. package/ui.container-col/types.ts +1 -1
  26. package/ui.container-divider/types.ts +1 -1
  27. package/ui.container-group/types.ts +1 -1
  28. package/ui.container-groups/types.ts +1 -1
  29. package/ui.container-modal/UModal.vue +21 -20
  30. package/ui.container-modal/config.ts +3 -2
  31. package/ui.container-modal/types.ts +1 -1
  32. package/ui.container-modal-confirm/types.ts +1 -1
  33. package/ui.container-page/UPage.vue +31 -19
  34. package/ui.container-page/config.ts +1 -0
  35. package/ui.container-page/types.ts +1 -1
  36. package/ui.container-row/types.ts +1 -1
  37. package/ui.data-list/types.ts +1 -1
  38. package/ui.data-table/types.ts +1 -1
  39. package/ui.dropdown-badge/types.ts +1 -1
  40. package/ui.dropdown-button/types.ts +1 -1
  41. package/ui.dropdown-link/UDropdownLink.vue +29 -33
  42. package/ui.dropdown-link/config.ts +3 -3
  43. package/ui.dropdown-link/types.ts +1 -1
  44. package/ui.dropdown-list/types.ts +1 -1
  45. package/ui.form-calendar/types.ts +1 -1
  46. package/ui.form-checkbox/types.ts +1 -1
  47. package/ui.form-checkbox-group/types.ts +1 -1
  48. package/ui.form-checkbox-multi-state/types.ts +1 -1
  49. package/ui.form-date-picker/types.ts +1 -1
  50. package/ui.form-date-picker-range/types.ts +1 -1
  51. package/ui.form-input/types.ts +1 -1
  52. package/ui.form-input-file/types.ts +1 -1
  53. package/ui.form-input-money/types.ts +1 -1
  54. package/ui.form-input-number/types.ts +1 -1
  55. package/ui.form-input-rating/types.ts +1 -1
  56. package/ui.form-input-search/types.ts +1 -1
  57. package/ui.form-label/types.ts +1 -1
  58. package/ui.form-radio/types.ts +1 -1
  59. package/ui.form-radio-group/types.ts +1 -1
  60. package/ui.form-select/USelect.vue +8 -6
  61. package/ui.form-select/types.ts +1 -1
  62. package/ui.form-switch/types.ts +1 -1
  63. package/ui.form-textarea/types.ts +1 -1
  64. package/ui.image-avatar/types.ts +1 -1
  65. package/ui.image-icon/UIcon.vue +2 -18
  66. package/ui.image-icon/storybook/stories.ts +0 -3
  67. package/ui.image-icon/types.ts +1 -13
  68. package/ui.loader/types.ts +1 -1
  69. package/ui.loader-overlay/types.ts +1 -1
  70. package/ui.loader-progress/types.ts +1 -1
  71. package/ui.navigation-pagination/types.ts +1 -1
  72. package/ui.navigation-progress/types.ts +2 -2
  73. package/ui.navigation-tab/types.ts +1 -1
  74. package/ui.navigation-tabs/types.ts +1 -1
  75. package/ui.other-dot/types.ts +1 -1
  76. package/ui.other-theme-color-toggle/storybook/stories.ts +1 -1
  77. package/ui.other-theme-color-toggle/types.ts +1 -1
  78. package/ui.text-alert/types.ts +1 -1
  79. package/ui.text-badge/types.ts +1 -1
  80. package/ui.text-block/types.ts +1 -1
  81. package/ui.text-empty/types.ts +1 -1
  82. package/ui.text-file/types.ts +1 -1
  83. package/ui.text-files/types.ts +1 -1
  84. package/ui.text-header/types.ts +1 -1
  85. package/ui.text-money/types.ts +1 -1
  86. package/ui.text-notify/types.ts +1 -1
  87. package/ui.text-number/types.ts +1 -1
  88. package/utils/theme.ts +5 -1
@@ -0,0 +1,77 @@
1
+ /* eslint-disable no-console */
2
+ import { existsSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { cwd } from "node:process";
5
+ import { cp, readFile, writeFile, rename } from "node:fs/promises";
6
+ import { styleText } from "node:util";
7
+
8
+ import { getDirFiles } from "../../utils/node/helper.js";
9
+ import { replaceRelativeImports } from "../utils/formatUtil.js";
10
+ import { getLastStorybookId } from "../utils/dataUtils.js";
11
+
12
+ import { SRC_COMPONENTS_PATH, COMPONENTS_PATH } from "../constants.js";
13
+
14
+ import { COMPONENTS, VUELESS_DIR, VUELESS_LOCAL_DIR } from "../../constants.js";
15
+
16
+ const boilerplateName = "UBoilerplate";
17
+ const boilerplatePath = path.join(cwd(), VUELESS_DIR, "ui.boilerplate");
18
+
19
+ export async function createVuelessComponent(options) {
20
+ const [componentName] = options;
21
+
22
+ if (!componentName) {
23
+ throw new Error("Component name is required");
24
+ }
25
+
26
+ const isSrcDir = existsSync(path.join(cwd(), VUELESS_LOCAL_DIR));
27
+ const destPath = isSrcDir
28
+ ? path.join(cwd(), SRC_COMPONENTS_PATH, componentName)
29
+ : path.join(cwd(), COMPONENTS_PATH, componentName);
30
+
31
+ const isComponentExists = componentName in COMPONENTS || existsSync(destPath);
32
+
33
+ if (isComponentExists) {
34
+ throw new Error(`Component with name ${componentName} alrady exists`);
35
+ }
36
+
37
+ await cp(boilerplatePath, destPath, { recursive: true });
38
+
39
+ await modifyCreatedComponent(destPath, componentName);
40
+
41
+ const successMessage = styleText(
42
+ "green",
43
+ `Success: ${componentName} was created in ${destPath} directory`,
44
+ );
45
+
46
+ console.log(successMessage);
47
+ }
48
+
49
+ async function modifyCreatedComponent(destPath, componentName) {
50
+ const destFiles = await getDirFiles(destPath, "");
51
+ const lastStorybookId = await getLastStorybookId();
52
+
53
+ for await (const filePath of destFiles) {
54
+ const fileContent = await readFile(filePath, "utf-8");
55
+
56
+ let updatedContent = replaceRelativeImports(componentName, filePath, fileContent);
57
+ let targetPath = filePath;
58
+
59
+ if (filePath.endsWith("constants.ts")) {
60
+ updatedContent = updatedContent.replace(boilerplateName, componentName);
61
+ }
62
+
63
+ if (filePath.endsWith("stories.ts")) {
64
+ updatedContent = updatedContent
65
+ .replaceAll(boilerplateName, componentName)
66
+ .replace("{{component_id}}", String(lastStorybookId + 10));
67
+ }
68
+
69
+ if (targetPath.endsWith(`${boilerplateName}.vue`)) {
70
+ targetPath = targetPath.replace(boilerplateName, componentName);
71
+
72
+ await rename(filePath, targetPath);
73
+ }
74
+
75
+ await writeFile(targetPath, updatedContent);
76
+ }
77
+ }
@@ -1,5 +1,7 @@
1
1
  import { vuelssInit } from "./init.js";
2
+ import { createVuelessComponent } from "./create.js";
2
3
 
3
4
  export const commands = {
4
5
  init: vuelssInit,
6
+ create: createVuelessComponent,
5
7
  };
@@ -5,14 +5,10 @@ import path from "node:path";
5
5
  import { writeFile } from "node:fs/promises";
6
6
  import { styleText } from "node:util";
7
7
 
8
- import {
9
- DEFAULT_VUELESS_CONFIG_NAME,
10
- DEFAULT_VUELESS_CONFIG_CONTNET,
11
- TYPESCRIPT_EXT,
12
- JAVASCRIPT_EXT,
13
- } from "../constants.js";
14
-
15
- const destPath = path.join(cwd(), DEFAULT_VUELESS_CONFIG_NAME);
8
+ import { DEFAULT_VUELESS_CONFIG_CONTNET } from "../constants.js";
9
+ import { JAVASCRIPT_EXT, TYPESCRIPT_EXT, VUELESS_CONFIG_FILE_NAME } from "../../constants.js";
10
+
11
+ const destPath = path.join(cwd(), VUELESS_CONFIG_FILE_NAME);
16
12
 
17
13
  const vuelessInitOptions = ["--ts", "--js"];
18
14
 
package/bin/constants.js CHANGED
@@ -1,8 +1,5 @@
1
- export const DEFAULT_VUELESS_CONFIG_NAME = "vueless.config.js";
2
- export const DEFAULT_EXIT_CODE = 0;
3
- export const FAILURE_CODE = 1;
4
- export const TYPESCRIPT_EXT = ".ts";
5
- export const JAVASCRIPT_EXT = ".js";
1
+ export const SRC_COMPONENTS_PATH = "/src/components";
2
+ export const COMPONENTS_PATH = "/components";
6
3
  export const DEFAULT_VUELESS_CONFIG_CONTNET = `
7
4
  export default {
8
5
  /**
package/bin/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { commands } from "./commands/index.js";
6
6
 
7
- import { DEFAULT_EXIT_CODE, FAILURE_CODE } from "./constants.js";
7
+ import { DEFAULT_EXIT_CODE, FAILURE_CODE } from "../constants.js";
8
8
 
9
9
  const [command, ...options] = process.argv.slice(2);
10
10
 
@@ -0,0 +1,53 @@
1
+ import { existsSync } from "node:fs";
2
+ import { cwd } from "node:process";
3
+ import path from "node:path";
4
+ import { readFile } from "node:fs/promises";
5
+
6
+ import { getDirFiles } from "../../utils/node/helper.js";
7
+
8
+ import { SRC_COMPONENTS_PATH, COMPONENTS_PATH } from "../constants.js";
9
+ import { VUELESS_DIR } from "../../constants.js";
10
+
11
+ const storiesName = "stories.ts";
12
+
13
+ export async function getLastStorybookId() {
14
+ const srcComponentsDir = path.join(cwd(), SRC_COMPONENTS_PATH);
15
+ const componentsDir = path.join(cwd(), COMPONENTS_PATH);
16
+ const vuelessPackagePath = path.join(cwd(), VUELESS_DIR);
17
+ const isSrcComponentsDir = existsSync(srcComponentsDir);
18
+ const isComponentsDir = existsSync(componentsDir);
19
+
20
+ const stories = await getDirFiles(vuelessPackagePath, storiesName);
21
+
22
+ if (isSrcComponentsDir) {
23
+ const srcComponentsDirStories = await getDirFiles(srcComponentsDir, storiesName);
24
+
25
+ stories.push(...srcComponentsDirStories);
26
+ }
27
+
28
+ if (isComponentsDir) {
29
+ const componentsDirStories = await getDirFiles(componentsDir, storiesName);
30
+
31
+ stories.push(...componentsDirStories);
32
+ }
33
+
34
+ let id = 200000;
35
+
36
+ for await (const storyPath of stories) {
37
+ const storyContent = await readFile(storyPath, "utf-8");
38
+
39
+ const storyIdLine = storyContent.split("\n").find((line, idx, array) => {
40
+ return line.includes("id:") && idx && array[idx - 1].includes("export default");
41
+ });
42
+
43
+ if (!storyIdLine) continue;
44
+
45
+ const currentId = parseInt(storyIdLine.split(" ").at(-1).replaceAll('"', ""));
46
+
47
+ if (currentId > id && !Number.isNaN(currentId)) {
48
+ id = currentId;
49
+ }
50
+ }
51
+
52
+ return id;
53
+ }
@@ -0,0 +1,48 @@
1
+ import path from "node:path";
2
+
3
+ export function replaceRelativeImports(componentName, filePath, fileContent) {
4
+ const isTopLevelFile = path.dirname(filePath).endsWith(componentName);
5
+ const contentLines = fileContent.split("\n");
6
+
7
+ return contentLines.map((line) => replaceRelativeLineImports(line, isTopLevelFile)).join("\n");
8
+ }
9
+
10
+ function replaceRelativeLineImports(line, isTopLevelFile) {
11
+ const importRegex = /import\s+(?:[\w\s{},*]+)\s+from\s+(['"])(\.\.?\/.*?)(\.[tj]s)?\1(?!\?)/g;
12
+
13
+ const isTopLevelLocalImport = isTopLevelFile && !line.includes("../");
14
+ const isInnerLocalImport =
15
+ !isTopLevelFile && (line.includes("../") || line.includes("./")) && !line.includes("../../");
16
+
17
+ if (isTopLevelLocalImport || isInnerLocalImport) {
18
+ return line;
19
+ }
20
+
21
+ return line.replace(importRegex, (match, quote, oldPath, ext) => {
22
+ const isDefaultImport = match.includes("{");
23
+
24
+ if (!isDefaultImport) {
25
+ match = defaultToNamedImport(match);
26
+ }
27
+
28
+ return match.replace(oldPath + (ext || ""), "vueless");
29
+ });
30
+ }
31
+
32
+ function defaultToNamedImport(importString) {
33
+ const splittedImport = importString.split(" ");
34
+
35
+ return splittedImport
36
+ .map((importStringToken) => {
37
+ if (importStringToken.includes("import")) {
38
+ return `${importStringToken} { `;
39
+ }
40
+
41
+ if (importStringToken.includes("from")) {
42
+ return ` } ${importStringToken} `;
43
+ }
44
+
45
+ return importStringToken;
46
+ })
47
+ .join("");
48
+ }
@@ -241,7 +241,18 @@ export default function useUI<T>(
241
241
  return vuelessAttrs;
242
242
  }
243
243
 
244
- return { config, getKeysAttrs, ...getKeysAttrs(mutatedProps) } as UseUI<T>;
244
+ /**
245
+ * Get data test attribute value if exist.
246
+ */
247
+ function getDataTest(suffix?: string) {
248
+ if (!props.dataTest) {
249
+ return null;
250
+ }
251
+
252
+ return suffix ? `${props.dataTest}-${suffix}` : props.dataTest;
253
+ }
254
+
255
+ return { config, getDataTest, ...getKeysAttrs(mutatedProps) } as UseUI<T>;
245
256
  }
246
257
 
247
258
  /**
package/constants.js CHANGED
@@ -221,6 +221,10 @@ export const VUELESS_ICONS_LOCAL_DIR = `src/${ICONS_DIR}`;
221
221
  export const VUELESS_ICONS_CACHED_DIR = `${VUELESS_CACHE_DIR}/${ICONS_DIR}`;
222
222
  export const VUELESS_CONFIGS_CACHED_DIR = `${VUELESS_CACHE_DIR}/configs`;
223
223
 
224
+ /* System error codes */
225
+ export const DEFAULT_EXIT_CODE = 0;
226
+ export const FAILURE_CODE = 1;
227
+
224
228
  /* Other */
225
229
  export const PX_IN_REM = 16;
226
230
  export const NESTED_COMPONENT_PATTERN_REG_EXP = /\{(U[^}]*)}/;
@@ -229,3 +233,5 @@ export const DYNAMIC_COLOR_PATTERN = "{color}";
229
233
  export const TAILWIND_COLOR_OPACITY_DELIMITER = "/";
230
234
  export const TAILWIND_VARIANT_DELIMITER = ":";
231
235
  export const TAILWIND_VARIANT_DELIMITER_REG_EXP = /:(?![^[]*])/;
236
+ export const JAVASCRIPT_EXT = ".js";
237
+ export const TYPESCRIPT_EXT = ".ts";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.674",
3
+ "version": "0.0.676",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
6
6
  "keywords": [
@@ -33,7 +33,7 @@
33
33
  "build": "npm run pre:start && storybook build --docs",
34
34
  "preview": "vite preview --host --outDir=storybook-static",
35
35
  "ts:check": "vue-tsc --build --force",
36
- "release:prepare": "npm run pre:start && rm -rf dist && mkdir -p dist && cp -r src/. package.json LICENSE README.md dist/ && cp -r bin dist/bin",
36
+ "release:prepare": "npm run pre:start && rm -rf dist && mkdir -p dist && cp -r src/. package.json LICENSE README.md dist/",
37
37
  "release:beta": "release-it --ci --npm.publish --preRelease=beta --increment=prerelease",
38
38
  "release:patch": "release-it patch --ci --npm.publish",
39
39
  "release:minor": "release-it minor --ci --npm.publish --git.tag --github.release",
package/plugin-vite.js CHANGED
@@ -14,6 +14,8 @@ import { setCustomPropTypes, removeCustomPropTypes } from "./utils/node/dynamicP
14
14
  import { buildWebTypes } from "./utils/node/webTypes.js";
15
15
  import { hideHiddenStories, showHiddenStories } from "./utils/node/dynamicStories.js";
16
16
 
17
+ import { DEFAULT_EXIT_CODE } from "./constants.js";
18
+
17
19
  /* Automatically importing Vueless components on demand */
18
20
  export const VuelessUnpluginComponents = (options) =>
19
21
  UnpluginVueComponents({
@@ -48,7 +50,7 @@ export const Vueless = function (options = {}) {
48
50
  clearTailwindSafelist(debug);
49
51
 
50
52
  /* stop command line process */
51
- process.exit(0);
53
+ process.exit(DEFAULT_EXIT_CODE);
52
54
  });
53
55
 
54
56
  return {
package/types.ts CHANGED
@@ -297,7 +297,7 @@ export type MutatedProps = ComputedRef<UnknownObject>;
297
297
 
298
298
  export type UseUI<T> = {
299
299
  config: Ref<T & ComponentConfig<T>>;
300
- getKeysAttrs: (mutatedProps?: MutatedProps) => KeysAttrs<T>;
300
+ getDataTest: (suffix?: string) => string | null;
301
301
  } & KeysAttrs<T>;
302
302
 
303
303
  export type KeysAttrs<T> = Record<
@@ -10,7 +10,7 @@ interface UBoilerplateArgs extends Props {
10
10
  }
11
11
 
12
12
  export default {
13
- id: "110010",
13
+ id: "{{component_id}}",
14
14
  title: "Custom / UBoilerplate",
15
15
  component: UBoilerplate,
16
16
  args: {
@@ -14,5 +14,5 @@ export interface Props {
14
14
  /**
15
15
  * Data-test attribute for automated testing.
16
16
  */
17
- dataTest?: string;
17
+ dataTest?: string | null;
18
18
  }
@@ -67,10 +67,8 @@ const mutatedProps = computed(() => ({
67
67
  label: Boolean(props.label),
68
68
  }));
69
69
 
70
- const { buttonAttrs, loaderAttrs, leftIconAttrs, rightIconAttrs, centerIconAttrs } = useUI<Config>(
71
- defaultConfig,
72
- mutatedProps,
73
- );
70
+ const { getDataTest, buttonAttrs, loaderAttrs, leftIconAttrs, rightIconAttrs, centerIconAttrs } =
71
+ useUI<Config>(defaultConfig, mutatedProps);
74
72
  </script>
75
73
 
76
74
  <template>
@@ -82,10 +80,15 @@ const { buttonAttrs, loaderAttrs, leftIconAttrs, rightIconAttrs, centerIconAttrs
82
80
  v-bind="buttonAttrs"
83
81
  :style="buttonStyle"
84
82
  :tabindex="!loading ? tabindex : -1"
85
- :data-test="dataTest"
83
+ :data-test="getDataTest()"
86
84
  >
87
85
  <template v-if="loading">
88
- <ULoader :loading="loading" color="inherit" v-bind="loaderAttrs" />
86
+ <ULoader
87
+ :loading="loading"
88
+ color="inherit"
89
+ v-bind="loaderAttrs"
90
+ :data-test="getDataTest('loader')"
91
+ />
89
92
  </template>
90
93
 
91
94
  <template v-else>
@@ -118,5 +118,5 @@ export interface Props {
118
118
  /**
119
119
  * Data-test attribute for automated testing.
120
120
  */
121
- dataTest?: string;
121
+ dataTest?: string | null;
122
122
  }
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, ref, useSlots } from "vue";
2
+ import { computed, useSlots } from "vue";
3
3
  import { RouterLink, useLink } from "vue-router";
4
4
 
5
5
  import useUI from "../composables/useUI.ts";
@@ -71,13 +71,6 @@ const useLinkOptions = {
71
71
 
72
72
  const { route, isActive, isExactActive } = useLink(useLinkOptions);
73
73
 
74
- const wrapperRef = ref(null);
75
-
76
- const wrapperActiveClasses = computed(() => [
77
- isActive.value && props.wrapperActiveClass,
78
- isExactActive.value && props.wrapperExactActiveClass,
79
- ]);
80
-
81
74
  const linkActiveClasses = computed(() => [
82
75
  isActive.value && props.activeClass,
83
76
  isExactActive.value && props.exactActiveClass,
@@ -117,14 +110,6 @@ function onBlur(event: FocusEvent) {
117
110
  emit("blur", event);
118
111
  }
119
112
 
120
- defineExpose({
121
- /**
122
- * A reference to the link wrapper element for direct DOM manipulation.
123
- * @property {HTMLElement}
124
- */
125
- wrapperRef,
126
- });
127
-
128
113
  /**
129
114
  * Get element / nested component attributes for each config token ✨
130
115
  * Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
@@ -134,53 +119,43 @@ const mutatedProps = computed(() => ({
134
119
  defaultSlot: hasSlotContent(slots["default"]),
135
120
  }));
136
121
 
137
- const { wrapperAttrs, linkAttrs } = useUI<Config>(defaultConfig, mutatedProps, "link");
122
+ const { getDataTest, linkAttrs } = useUI<Config>(defaultConfig, mutatedProps);
138
123
  </script>
139
124
 
140
125
  <template>
141
- <div v-bind="wrapperAttrs" ref="wrapperRef" :class="wrapperActiveClasses" tabindex="-1">
142
- <!-- @slot Use it to add something before the label. -->
143
- <slot name="left" />
144
-
145
- <router-link
146
- v-if="isPresentRoute"
147
- :to="route"
148
- :target="targetValue"
149
- v-bind="linkAttrs"
150
- :class="linkActiveClasses"
151
- :data-test="dataTest"
152
- tabindex="0"
153
- @blur="onBlur"
154
- @focus="onFocus"
155
- @click="onClick"
156
- @keydown="onKeydown"
157
- @mouseover="onMouseover"
158
- >
159
- <!-- @slot Use it replace the label. -->
160
- <slot>
161
- {{ label }}
162
- </slot>
163
- </router-link>
164
-
165
- <a
166
- v-else
167
- :href="prefixedHref"
168
- :target="targetValue"
169
- v-bind="linkAttrs"
170
- :class="linkActiveClasses"
171
- :data-test="dataTest"
172
- tabindex="0"
173
- @blur="onBlur"
174
- @focus="onFocus"
175
- @click="onClick"
176
- @keydown="onKeydown"
177
- @mouseover="onMouseover"
178
- >
179
- <!-- @slot Use it replace the label. -->
180
- <slot>{{ label }}</slot>
181
- </a>
182
-
183
- <!-- @slot Use it to add something after the label. -->
184
- <slot name="right" />
185
- </div>
126
+ <router-link
127
+ v-if="isPresentRoute"
128
+ :to="route"
129
+ :target="targetValue"
130
+ v-bind="linkAttrs"
131
+ :class="linkActiveClasses"
132
+ :data-test="getDataTest()"
133
+ tabindex="0"
134
+ @blur="onBlur"
135
+ @focus="onFocus"
136
+ @click="onClick"
137
+ @keydown="onKeydown"
138
+ @mouseover="onMouseover"
139
+ >
140
+ <!-- @slot Use it replace the label. -->
141
+ <slot>{{ label }}</slot>
142
+ </router-link>
143
+
144
+ <a
145
+ v-else
146
+ :href="prefixedHref"
147
+ :target="targetValue"
148
+ v-bind="linkAttrs"
149
+ :class="linkActiveClasses"
150
+ :data-test="getDataTest()"
151
+ tabindex="0"
152
+ @blur="onBlur"
153
+ @focus="onFocus"
154
+ @click="onClick"
155
+ @keydown="onKeydown"
156
+ @mouseover="onMouseover"
157
+ >
158
+ <!-- @slot Use it replace the label. -->
159
+ <slot>{{ label }}</slot>
160
+ </a>
186
161
  </template>
@@ -1,40 +1,12 @@
1
1
  export default /*tw*/ {
2
- wrapper: {
3
- base: `
4
- w-fit inline-flex items-center rounded transition focus-visible:outline-none
5
- focus-within:ring-dynamic focus-within:ring-offset-4 focus-within:ring-{color}-700/15
6
- `,
7
- variants: {
8
- size: {
9
- sm: "gap-1",
10
- md: "gap-1",
11
- lg: "gap-1.5",
12
- },
13
- color: {
14
- grayscale: "focus-within:ring-gray-700/15",
15
- white: "focus-within:ring-white/15",
16
- },
17
- disabled: {
18
- true: "!ring-0 !ring-offset-0 cursor-not-allowed",
19
- },
20
- defaultSlot: {
21
- true: "flex items-center focus-within:ring-0 focus-within:ring-offset-0",
22
- },
23
- ring: {
24
- false: "!ring-0 !ring-offset-0",
25
- },
26
- block: {
27
- true: "w-full",
28
- },
29
- },
30
- },
31
2
  link: {
32
3
  base: `
33
- inline-block cursor-pointer !leading-none transition
4
+ inline-block cursor-pointer !leading-none rounded transition
34
5
  text-{color}-600 decoration-{color}-600 underline-offset-4
35
6
  hover:text-{color}-700 hover:decoration-{color}-700
36
7
  focus:text-{color}-700 focus:decoration-{color}-700 focus:outline-0
37
8
  active:text-{color}-800 active:decoration-{color}-800
9
+ focus:ring-dynamic focus:ring-offset-4 focus:ring-{color}-700/15
38
10
  `,
39
11
  variants: {
40
12
  size: {
@@ -57,10 +29,10 @@ export default /*tw*/ {
57
29
  grayscale: `
58
30
  text-gray-900 decoration-gray-900
59
31
  hover:text-gray-800 hover:decoration-gray-800
60
- focus:text-gray-800 focus:decoration-gray-800
32
+ focus:text-gray-800 focus:decoration-gray-800 focus:ring-gray-700/15
61
33
  active:text-gray-700 active:decoration-gray-700
62
34
  `,
63
- white: "decoration-white text-white",
35
+ white: "decoration-white text-white focus:ring-white/15",
64
36
  },
65
37
  defaultSlot: {
66
38
  true: "flex items-center no-underline hover:no-underline",
@@ -71,6 +43,9 @@ export default /*tw*/ {
71
43
  disabled: {
72
44
  true: "text-gray-400 pointer-events-none",
73
45
  },
46
+ ring: {
47
+ false: "!ring-0 !ring-offset-0",
48
+ },
74
49
  block: {
75
50
  true: "w-full",
76
51
  },
@@ -83,11 +58,9 @@ export default /*tw*/ {
83
58
  ariaCurrentValue: "page",
84
59
  activeClass: "",
85
60
  exactActiveClass: "",
86
- wrapperActiveClass: "",
87
- wrapperExactActiveClass: "",
88
61
  underlined: undefined,
89
62
  block: false,
90
- ring: true,
63
+ ring: false,
91
64
  dashed: false,
92
65
  disabled: false,
93
66
  targetBlank: false,
@@ -196,25 +196,3 @@ DefaultSlot.args = {
196
196
  </template>
197
197
  `,
198
198
  };
199
-
200
- export const LeftAndRightSlots: StoryFn<ULinkArgs> = (args: ULinkArgs) => ({
201
- components: { ULink, UIcon, URow },
202
- setup() {
203
- return { args };
204
- },
205
- template: `
206
- <URow no-mobile>
207
- <ULink label="Download">
208
- <template #left>
209
- <UIcon name="download" size="xs" color="green" />
210
- </template>
211
- </ULink>
212
-
213
- <ULink label="Open">
214
- <template #right>
215
- <UIcon name="open_in_new" size="xs" color="green" />
216
- </template>
217
- </ULink>
218
- </URow>
219
- `,
220
- });
@@ -87,16 +87,6 @@ export interface Props {
87
87
  */
88
88
  exactActiveClass?: string;
89
89
 
90
- /**
91
- * Apply classes to the wrapper div when link route is active or when it matches any parent route.
92
- */
93
- wrapperActiveClass?: string;
94
-
95
- /**
96
- * Apply classes to the wrapper div when link route is active.
97
- */
98
- wrapperExactActiveClass?: string;
99
-
100
90
  /**
101
91
  * Show underline.
102
92
  */
@@ -130,5 +120,5 @@ export interface Props {
130
120
  /**
131
121
  * Data-test attribute for automated testing.
132
122
  */
133
- dataTest?: string;
123
+ dataTest?: string | null;
134
124
  }
@@ -94,5 +94,5 @@ export interface Props {
94
94
  /**
95
95
  * Data-test attribute for automated testing.
96
96
  */
97
- dataTest?: string;
97
+ dataTest?: string | null;
98
98
  }
@@ -56,5 +56,5 @@ export interface Props {
56
56
  /**
57
57
  * Data-test attribute for automated testing.
58
58
  */
59
- dataTest?: string;
59
+ dataTest?: string | null;
60
60
  }
@@ -32,5 +32,5 @@ export interface Props {
32
32
  /**
33
33
  * Data-test attribute for automated testing.
34
34
  */
35
- dataTest?: string;
35
+ dataTest?: string | null;
36
36
  }
@@ -22,5 +22,5 @@ export interface Props {
22
22
  /**
23
23
  * Data-test attribute for automated testing.
24
24
  */
25
- dataTest?: string;
25
+ dataTest?: string | null;
26
26
  }
@@ -52,5 +52,5 @@ export interface Props {
52
52
  /**
53
53
  * Data-test attribute for automated testing.
54
54
  */
55
- dataTest?: string;
55
+ dataTest?: string | null;
56
56
  }