fds-vue-core 7.1.6 → 7.1.7
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/components.d.ts +2 -2
- package/dist/components/Form/FdsTextarea/FdsTextarea.stories.d.ts +1 -0
- package/dist/components/Form/FdsTextarea/FdsTextarea.vue.d.ts +4 -2
- package/dist/components/Form/FdsTextarea/types.d.ts +10 -0
- package/dist/fds-vue-core.cjs.js +66 -20
- package/dist/fds-vue-core.cjs.js.map +1 -1
- package/dist/fds-vue-core.css +1 -1
- package/dist/fds-vue-core.es.js +66 -20
- package/dist/fds-vue-core.es.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/components/Form/FdsTextarea/FdsTextarea.stories.ts +16 -0
- package/src/components/Form/FdsTextarea/FdsTextarea.vue +62 -22
- package/src/components/Form/FdsTextarea/types.ts +11 -0
- package/src/index.ts +1 -1
- package/src/lang/en.json +1 -0
- package/src/lang/sv.json +1 -0
- package/src/plugin/useFdsI18n.ts +21 -5
package/components.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ import type { FdsCheckboxProps } from './src/components/Form/FdsCheckbox/types'
|
|
|
31
31
|
import type { FdsInputProps } from './src/components/Form/FdsInput/types'
|
|
32
32
|
import type { FdsRadioProps } from './src/components/Form/FdsRadio/types'
|
|
33
33
|
import type { FdsSelectProps } from './src/components/Form/FdsSelect/types'
|
|
34
|
-
import type { FdsTextareaProps } from './src/components/Form/FdsTextarea/types'
|
|
34
|
+
import type { FdsTextareaEmits, FdsTextareaProps } from './src/components/Form/FdsTextarea/types'
|
|
35
35
|
import type { FdsTableProps } from './src/components/Table/FdsTable/types'
|
|
36
36
|
import type { FdsTableHeadProps } from './src/components/Table/FdsTableHead/types'
|
|
37
37
|
import type { FdsTabsProps } from './src/components/Tabs/FdsTabs/types'
|
|
@@ -61,7 +61,7 @@ declare module 'vue' {
|
|
|
61
61
|
FdsRadio: DefineComponent<FdsRadioProps>
|
|
62
62
|
FdsInput: DefineComponent<FdsInputProps>
|
|
63
63
|
FdsCheckbox: DefineComponent<FdsCheckboxProps>
|
|
64
|
-
FdsTextarea: DefineComponent<FdsTextareaProps>
|
|
64
|
+
FdsTextarea: DefineComponent<FdsTextareaProps, {}, {}, {}, {}, {}, {}, FdsTextareaEmits>
|
|
65
65
|
FdsSelect: DefineComponent<FdsSelectProps>
|
|
66
66
|
FdsTable: DefineComponent<FdsTableProps>
|
|
67
67
|
FdsTableHead: DefineComponent<FdsTableHeadProps>
|
|
@@ -4,19 +4,21 @@ type __VLS_PublicProps = {
|
|
|
4
4
|
modelValue?: string;
|
|
5
5
|
} & __VLS_Props;
|
|
6
6
|
declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
7
|
-
"update:modelValue": (value: string) => any;
|
|
8
|
-
} & {
|
|
9
7
|
input: (ev: Event) => any;
|
|
10
8
|
blur: (ev: FocusEvent) => any;
|
|
11
9
|
change: (ev: Event) => any;
|
|
10
|
+
valid: (value: boolean | null) => any;
|
|
12
11
|
"update:value": (value: string) => any;
|
|
12
|
+
"update:modelValue": (value: string) => any;
|
|
13
13
|
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
14
14
|
onInput?: ((ev: Event) => any) | undefined;
|
|
15
15
|
onBlur?: ((ev: FocusEvent) => any) | undefined;
|
|
16
16
|
onChange?: ((ev: Event) => any) | undefined;
|
|
17
|
+
onValid?: ((value: boolean | null) => any) | undefined;
|
|
17
18
|
"onUpdate:value"?: ((value: string) => any) | undefined;
|
|
18
19
|
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
19
20
|
}>, {
|
|
21
|
+
maxlength: number;
|
|
20
22
|
value: string;
|
|
21
23
|
dataTestid: string;
|
|
22
24
|
label: string;
|
|
@@ -11,5 +11,15 @@ export interface FdsTextareaProps extends Omit</* @vue-ignore */ TextareaHTMLAtt
|
|
|
11
11
|
};
|
|
12
12
|
dataTestid?: string;
|
|
13
13
|
inputClass?: string | Record<string, boolean> | Array<string | Record<string, boolean>>;
|
|
14
|
+
/** Character limit for counter/validation. Does not set native maxlength on the textarea. */
|
|
15
|
+
maxlength?: number;
|
|
14
16
|
value?: string;
|
|
15
17
|
}
|
|
18
|
+
export interface FdsTextareaEmits {
|
|
19
|
+
input: [ev: Event];
|
|
20
|
+
change: [ev: Event];
|
|
21
|
+
'update:value': [value: string];
|
|
22
|
+
blur: [ev: FocusEvent];
|
|
23
|
+
/** Emitted when maxlength is set: true/false for within/over limit, null when maxlength is unset. */
|
|
24
|
+
valid: [value: boolean | null];
|
|
25
|
+
}
|
package/dist/fds-vue-core.cjs.js
CHANGED
|
@@ -83,6 +83,7 @@ const en = {
|
|
|
83
83
|
"FdsSearchSelectPro.loadingMore": "Loading more...",
|
|
84
84
|
"FdsSearchSelectPro.showMore": "Show more",
|
|
85
85
|
"FdsSearchSelectPro.unspecified": "Unspecified",
|
|
86
|
+
"FdsTextarea.characters": "characters",
|
|
86
87
|
"FdsTreeView.childrenCountPrefix": "(+",
|
|
87
88
|
"FdsTreeView.closePopover": "Close",
|
|
88
89
|
"FdsTreeView.collapseNode": "Collapse {title}",
|
|
@@ -144,6 +145,7 @@ const sv = {
|
|
|
144
145
|
"FdsSearchSelectPro.loadingMore": "Hämtar fler...",
|
|
145
146
|
"FdsSearchSelectPro.showMore": "Visa fler",
|
|
146
147
|
"FdsSearchSelectPro.unspecified": "Ospecificerat",
|
|
148
|
+
"FdsTextarea.characters": "tecken",
|
|
147
149
|
"FdsTreeView.childrenCountPrefix": "(+",
|
|
148
150
|
"FdsTreeView.closePopover": "Stäng",
|
|
149
151
|
"FdsTreeView.collapseNode": "Fäll ihop {title}",
|
|
@@ -187,15 +189,26 @@ const useFdsI18n = () => {
|
|
|
187
189
|
if (!values) return message2;
|
|
188
190
|
return Object.entries(values).reduce((acc, [name, value]) => acc.split(`{${name}}`).join(String(value)), message2);
|
|
189
191
|
};
|
|
192
|
+
const silentTranslateOptions = { missingWarn: false, fallbackWarn: false };
|
|
193
|
+
const translateFromDictionary = (key, values) => {
|
|
194
|
+
const value = dictionary.value[key];
|
|
195
|
+
if (typeof value !== "string") return void 0;
|
|
196
|
+
return interpolate(value, values);
|
|
197
|
+
};
|
|
190
198
|
const t = (key, values) => {
|
|
191
199
|
const translateFn = i18n?.global?.t ?? i18n?.t;
|
|
192
200
|
if (typeof translateFn === "function") {
|
|
193
|
-
|
|
194
|
-
if (
|
|
201
|
+
let translated;
|
|
202
|
+
if (values != null && Object.keys(values).length > 0) {
|
|
203
|
+
translated = translateFn(key, values, silentTranslateOptions);
|
|
204
|
+
} else {
|
|
205
|
+
translated = translateFn(key, "", silentTranslateOptions);
|
|
206
|
+
}
|
|
207
|
+
if (typeof translated === "string" && translated !== key && translated !== "") {
|
|
208
|
+
return translated;
|
|
209
|
+
}
|
|
195
210
|
}
|
|
196
|
-
|
|
197
|
-
if (typeof value !== "string") return "";
|
|
198
|
-
return interpolate(value, values);
|
|
211
|
+
return translateFromDictionary(key, values) ?? key;
|
|
199
212
|
};
|
|
200
213
|
return {
|
|
201
214
|
i18n,
|
|
@@ -16211,7 +16224,7 @@ const _hoisted_3$1 = { class: "relative" };
|
|
|
16211
16224
|
const _hoisted_4$1 = ["value"];
|
|
16212
16225
|
const _hoisted_5$1 = {
|
|
16213
16226
|
key: 2,
|
|
16214
|
-
class: "text-red-
|
|
16227
|
+
class: "text-red-700 text-right font-bold mt-1"
|
|
16215
16228
|
};
|
|
16216
16229
|
const _sfc_main$8 = /* @__PURE__ */ vue.defineComponent({
|
|
16217
16230
|
...{
|
|
@@ -16228,13 +16241,15 @@ const _sfc_main$8 = /* @__PURE__ */ vue.defineComponent({
|
|
|
16228
16241
|
modelModifiers: { default: () => ({}) },
|
|
16229
16242
|
dataTestid: { default: void 0 },
|
|
16230
16243
|
inputClass: { default: void 0 },
|
|
16244
|
+
maxlength: { default: void 0 },
|
|
16231
16245
|
value: { default: void 0 }
|
|
16232
16246
|
}, {
|
|
16233
16247
|
"modelValue": { default: void 0, required: false },
|
|
16234
16248
|
"modelModifiers": {}
|
|
16235
16249
|
}),
|
|
16236
|
-
emits: /* @__PURE__ */ vue.mergeModels(["input", "change", "update:value", "blur"], ["update:modelValue"]),
|
|
16250
|
+
emits: /* @__PURE__ */ vue.mergeModels(["input", "change", "update:value", "blur", "valid"], ["update:modelValue"]),
|
|
16237
16251
|
setup(__props, { emit: __emit }) {
|
|
16252
|
+
const { t } = useFdsI18n();
|
|
16238
16253
|
const modelValue = vue.useModel(__props, "modelValue");
|
|
16239
16254
|
const props = __props;
|
|
16240
16255
|
const emit = __emit;
|
|
@@ -16252,7 +16267,7 @@ const _sfc_main$8 = /* @__PURE__ */ vue.defineComponent({
|
|
|
16252
16267
|
return eventHandlers;
|
|
16253
16268
|
});
|
|
16254
16269
|
const textareaAttrs = vue.computed(() => {
|
|
16255
|
-
const { class: _, id: _id, rows: _rows, ...rest } = attrs;
|
|
16270
|
+
const { class: _, id: _id, rows: _rows, maxlength: _maxlength, ...rest } = attrs;
|
|
16256
16271
|
const filtered = {};
|
|
16257
16272
|
for (const key in rest) {
|
|
16258
16273
|
if (!key.startsWith("on") || typeof rest[key] !== "function") {
|
|
@@ -16263,33 +16278,61 @@ const _sfc_main$8 = /* @__PURE__ */ vue.defineComponent({
|
|
|
16263
16278
|
...filtered,
|
|
16264
16279
|
id: textareaId.value,
|
|
16265
16280
|
rows: rows.value ?? 4,
|
|
16266
|
-
"aria-invalid": props.valid === false ? true : void 0,
|
|
16281
|
+
"aria-invalid": props.valid === false || isOverMaxLength.value ? true : void 0,
|
|
16267
16282
|
"data-testid": props.dataTestid ?? void 0
|
|
16268
16283
|
};
|
|
16269
16284
|
});
|
|
16270
16285
|
const isLazy = vue.computed(() => props.modelModifiers?.lazy === true);
|
|
16286
|
+
const internalValue = vue.computed({
|
|
16287
|
+
get: () => modelValue.value !== void 0 ? modelValue.value : props.value ?? "",
|
|
16288
|
+
set: (newValue) => {
|
|
16289
|
+
if (modelValue.value !== void 0) {
|
|
16290
|
+
modelValue.value = newValue;
|
|
16291
|
+
}
|
|
16292
|
+
emit("update:value", newValue);
|
|
16293
|
+
}
|
|
16294
|
+
});
|
|
16295
|
+
const liveValue = vue.ref("");
|
|
16296
|
+
vue.watch(
|
|
16297
|
+
() => internalValue.value,
|
|
16298
|
+
(value) => {
|
|
16299
|
+
if (value !== liveValue.value) {
|
|
16300
|
+
liveValue.value = value;
|
|
16301
|
+
}
|
|
16302
|
+
},
|
|
16303
|
+
{ immediate: true }
|
|
16304
|
+
);
|
|
16271
16305
|
const showInvalidMessage = vue.computed(() => props.valid === false && !props.optional && props.invalidMessage);
|
|
16272
16306
|
const isInvalid = vue.computed(() => props.valid === false && !props.optional && !disabled.value);
|
|
16273
16307
|
const isValid2 = vue.computed(() => props.valid === true);
|
|
16308
|
+
const characterCount = vue.computed(() => liveValue.value.length);
|
|
16309
|
+
const showCharacterCount = vue.computed(() => props.maxlength != null);
|
|
16310
|
+
const isOverMaxLength = vue.computed(() => props.maxlength != null && characterCount.value > props.maxlength);
|
|
16311
|
+
const maxLengthValid = vue.computed(() => {
|
|
16312
|
+
if (props.maxlength == null) {
|
|
16313
|
+
return null;
|
|
16314
|
+
}
|
|
16315
|
+
return characterCount.value <= props.maxlength;
|
|
16316
|
+
});
|
|
16317
|
+
vue.watch(
|
|
16318
|
+
maxLengthValid,
|
|
16319
|
+
(value) => {
|
|
16320
|
+
emit("valid", value);
|
|
16321
|
+
},
|
|
16322
|
+
{ immediate: true }
|
|
16323
|
+
);
|
|
16274
16324
|
const inputClasses = vue.computed(() => [
|
|
16275
16325
|
"block w-full rounded-md border border-gray-500 px-3 py-2",
|
|
16276
16326
|
"focus:outline-2 focus:outline-blue-500 focus:border-transparent",
|
|
16277
16327
|
disabled.value ? "text-gray-800 outline-dashed outline-2 -outline-offset-2 outline-gray-400 cursor-not-allowed border-transparent bg-gray-50" : "bg-white",
|
|
16278
16328
|
isInvalid.value && "outline-2 -outline-offset-2 outline-red-600",
|
|
16329
|
+
isOverMaxLength.value && !disabled.value && "outline-2 -outline-offset-2 outline-red-600",
|
|
16279
16330
|
props.inputClass
|
|
16280
16331
|
]);
|
|
16281
16332
|
const validationIconClasses = vue.computed(() => ["absolute right-3 top-3 flex items-center gap-2 pointer-events-none"]);
|
|
16282
|
-
const internalValue = vue.computed({
|
|
16283
|
-
get: () => modelValue.value !== void 0 ? modelValue.value : props.value ?? "",
|
|
16284
|
-
set: (newValue) => {
|
|
16285
|
-
if (modelValue.value !== void 0) {
|
|
16286
|
-
modelValue.value = newValue;
|
|
16287
|
-
}
|
|
16288
|
-
emit("update:value", newValue);
|
|
16289
|
-
}
|
|
16290
|
-
});
|
|
16291
16333
|
function handleInputChange(ev) {
|
|
16292
16334
|
const target = ev.target;
|
|
16335
|
+
liveValue.value = target.value;
|
|
16293
16336
|
if (isLazy.value && ev.type === "input") {
|
|
16294
16337
|
return;
|
|
16295
16338
|
}
|
|
@@ -16313,7 +16356,7 @@ const _sfc_main$8 = /* @__PURE__ */ vue.defineComponent({
|
|
|
16313
16356
|
__props.meta ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$1, vue.toDisplayString(__props.meta), 1)) : vue.createCommentVNode("", true),
|
|
16314
16357
|
vue.createElementVNode("div", _hoisted_3$1, [
|
|
16315
16358
|
vue.createElementVNode("textarea", vue.mergeProps({
|
|
16316
|
-
value:
|
|
16359
|
+
value: liveValue.value,
|
|
16317
16360
|
class: inputClasses.value
|
|
16318
16361
|
}, textareaAttrs.value, {
|
|
16319
16362
|
onInput: handleInputChange,
|
|
@@ -16334,7 +16377,10 @@ const _sfc_main$8 = /* @__PURE__ */ vue.defineComponent({
|
|
|
16334
16377
|
})) : vue.createCommentVNode("", true)
|
|
16335
16378
|
], 2)
|
|
16336
16379
|
]),
|
|
16337
|
-
showInvalidMessage.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_5$1, vue.toDisplayString(__props.invalidMessage), 1)) : vue.
|
|
16380
|
+
showInvalidMessage.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_5$1, vue.toDisplayString(__props.invalidMessage), 1)) : showCharacterCount.value ? (vue.openBlock(), vue.createElementBlock("p", {
|
|
16381
|
+
key: 3,
|
|
16382
|
+
class: vue.normalizeClass(["mt-1 text-right font-bold", isOverMaxLength.value ? "text-red-700" : "text-gray-900"])
|
|
16383
|
+
}, vue.toDisplayString(characterCount.value) + " / " + vue.toDisplayString(__props.maxlength) + " " + vue.toDisplayString(vue.unref(t)("FdsTextarea.characters")), 3)) : vue.createCommentVNode("", true)
|
|
16338
16384
|
], 2);
|
|
16339
16385
|
};
|
|
16340
16386
|
}
|