diff --git a/.gitignore b/.gitignore
index 31fe48f6..20eea3e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,5 @@ coverage
.npmrc
storybook-static
+
+.claude/*
\ No newline at end of file
diff --git a/figma-console-mcp b/figma-console-mcp
deleted file mode 160000
index 61942b0a..00000000
--- a/figma-console-mcp
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 61942b0a0f5c57687820cd8b5d43caa5ddee1c4f
diff --git a/package.json b/package.json
index 97f39612..0a3e454b 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"@mdx-js/react": "^3.1.1",
"@primevue/auto-import-resolver": "4.3.6",
"@primevue/themes": "4.3.6",
+ "@tabler/icons-vue": "^3.41.1",
"@tabler/icons-webfont": "^3.22.0",
"@vee-validate/rules": "^4.7.3",
"lodash": "^4.17.21",
diff --git a/src/plugins/prime/stories/Form/InputText/InputText.mdx b/src/plugins/prime/stories/Form/InputText/InputText.mdx
index fa5dbbb5..ca715b4f 100644
--- a/src/plugins/prime/stories/Form/InputText/InputText.mdx
+++ b/src/plugins/prime/stories/Form/InputText/InputText.mdx
@@ -1,42 +1,45 @@
-import { Meta, Story } from '@storybook/addon-docs/blocks';
+import { Meta, Canvas, Controls, Title, Description } from '@storybook/addon-docs/blocks';
import * as InputTextStories from './InputText.stories';
-import { Template, TemplateWithIcons } from './InputText.template';
-
-# InputText
+
+
-[PrimeVue InputText](https://primevue.org/inputtext), [Макет](https://www.figma.com/design/4TYeki0MDLhfPGJstbIicf/UI-kit-PrimeFace-\(DS\)?node-id=484-5470\&node-type=section\&t=2PSg63p2UfJ4cUzB-0)
+## Варианты использования
-```html dark
-
-```
+### Default
+Базовый пример компонента. Используйте Controls для интерактивного изменения пропсов.
+
-
+#### Список параметров
+
-# С иконками
+### Disabled
+Отключённое состояние.
+
-Логика на работу крестика не прописана в ui-ките, на данный момент должна быть прописана на стороне приложения
+### Readonly
+Режим только для чтения.
+
-```html dark
-
+### FloatLabel
+Интеграция с `FloatLabel` — плавающая метка внутри поля.
+
-
-
-
-
-
-
-
-
+Возможно использование `FloatLabel` и `PBlockInputText` напрямую:
+```vue
+
+
+
+
```
-
+### FloatLabel + Invalid
+FloatLabel с невалидным состоянием.
+
diff --git a/src/plugins/prime/stories/Form/InputText/InputText.stories.js b/src/plugins/prime/stories/Form/InputText/InputText.stories.js
index bb9fd373..69bfb999 100644
--- a/src/plugins/prime/stories/Form/InputText/InputText.stories.js
+++ b/src/plugins/prime/stories/Form/InputText/InputText.stories.js
@@ -1,14 +1,400 @@
-import { Template, TemplateWithIcons } from './InputText.template';
+import PBlockInputText from '@/primeBlocks/PBlockInputText/PBlockInputText.vue';
+import { ref } from 'vue';
+import { Template } from './InputText.template';
-export default {
+/**
+ * Компонент текстового ввода.
+ */
+const meta = {
title: 'Prime/Form/InputText',
+ component: PBlockInputText,
+ tags: ['autodocs'],
+ parameters: {
+ docs: {
+ description: {
+ component: `Обёртка над PrimeVue InputText с поддержкой очистки, размеров и кастомной иконки.
+
+\`\`\`js
+import { PBlockInputText } from '@cdek-it/vue-ui-kit';
+\`\`\``,
+ },
+ },
+ designToken: { disable: false },
+ designTokens: { prefix: '--p-inputtext' },
+ },
+ argTypes: {
+ size: {
+ control: 'select',
+ options: ['small', 'large', 'xlarge'],
+ description:
+ 'Размер поля. `xlarge` — кастомный размер, реализован через CSS-класс `p-inputtext-xlg`.',
+ table: {
+ category: 'Props',
+ type: { summary: "'small' | 'large' | 'xlarge'" },
+ },
+ },
+ showClear: {
+ control: 'boolean',
+ description: 'Показывает иконку очистки при наличии значения',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: 'true' },
+ type: { summary: 'boolean' },
+ },
+ },
+ invalid: {
+ control: 'boolean',
+ description: 'Невалидное состояние',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: 'false' },
+ type: { summary: 'boolean' },
+ },
+ },
+ disabled: {
+ control: 'boolean',
+ description: 'Отключает взаимодействие',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: 'false' },
+ type: { summary: 'boolean' },
+ },
+ },
+ readonly: {
+ control: 'boolean',
+ description: 'Только для чтения',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: 'false' },
+ type: { summary: 'boolean' },
+ },
+ },
+ placeholder: {
+ control: 'text',
+ description: 'Подсказка при пустом поле',
+ table: {
+ category: 'Props',
+ type: { summary: 'string' },
+ },
+ },
+ hasFloatlabel: {
+ control: 'boolean',
+ description: 'Включает режим плавающей метки (FloatLabel)',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: 'false' },
+ type: { summary: 'boolean' },
+ },
+ },
+ label: {
+ control: 'text',
+ description: 'Текст плавающей метки (при `hasFloatlabel`)',
+ table: {
+ category: 'Props',
+ type: { summary: 'string' },
+ },
+ },
+ required: {
+ control: 'boolean',
+ description: 'Показывает маркер обязательного поля `*` рядом с меткой',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: 'false' },
+ type: { summary: 'boolean' },
+ },
+ },
+ fluid: {
+ control: 'boolean',
+ description: 'Растягивает поле на всю ширину контейнера',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: 'false' },
+ type: { summary: 'boolean' },
+ },
+ },
+ },
+ args: {
+ placeholder: 'Введите текст...',
+ showClear: true,
+ hasFloatlabel: false,
+ invalid: false,
+ disabled: false,
+ readonly: false,
+ fluid: false,
+ },
+};
+
+export default meta;
+
+// ── Stories ──────────────────────────────────────────────────────────────────
+
+export const Default = {
+ render: Template,
+ parameters: {
+ docs: {
+ description: {
+ story:
+ 'Базовый пример компонента. Используйте Controls для интерактивного изменения пропсов.',
+ },
+ },
+ },
+};
+
+export const Disabled = {
+ render: (args) => ({
+ components: { PBlockInputText },
+ setup() {
+ const value = ref('');
+ return { args, value };
+ },
+ template: `
+
+ `,
+ }),
+ args: {
+ placeholder: 'Введите текст...',
+ },
+ parameters: {
+ controls: { disable: true },
+ docs: {
+ description: {
+ story: 'Отключённое состояние.',
+ },
+ source: {
+ code: `
+
+
+
+ `,
+ },
+ },
+ },
};
-export const Primary = {
- render: Template.bind({}),
+export const Readonly = {
+ render: (args) => ({
+ components: { PBlockInputText },
+ setup() {
+ const value = ref('');
+ return { args, value };
+ },
+ template: `
+
+ `,
+ }),
+ args: {
+ placeholder: 'Введите текст...',
+ },
+ parameters: {
+ controls: { disable: true },
+ docs: {
+ description: {
+ story:
+ 'Режим только для чтения — поле отображает значение, но недоступно для редактирования.',
+ },
+ source: {
+ code: `
+
+
+
+ `,
+ },
+ },
+ },
+};
+
+export const Invalid = {
+ render: (args) => ({
+ components: { PBlockInputText },
+ setup() {
+ const value = ref('');
+ return { args, value };
+ },
+ template: `
+
+ `,
+ }),
+ parameters: {
+ controls: { disable: true },
+ docs: {
+ description: {
+ story: 'Невалидное состояние.',
+ },
+ source: {
+ code: `
+
+
+
+ `,
+ },
+ },
+ },
+};
+
+export const FloatLabel = {
+ render: (args) => ({
+ components: { PBlockInputText },
+ setup() {
+ const value = ref('');
+ return { args, value };
+ },
+ template: `
+
+ `,
+ }),
+ args: {
+ label: 'Имя',
+ required: true,
+ showClear: true,
+ },
+ argTypes: {
+ label: {
+ control: 'text',
+ description: 'Текст плавающей метки',
+ table: {
+ category: 'Props',
+ type: { summary: 'string' },
+ },
+ },
+ required: {
+ control: 'boolean',
+ description: 'Показывает маркер обязательного поля `*` рядом с меткой',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: 'false' },
+ type: { summary: 'boolean' },
+ },
+ },
+ showClear: {
+ control: 'boolean',
+ description: 'Показывает иконку очистки при наличии значения',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: 'true' },
+ type: { summary: 'boolean' },
+ },
+ },
+ size: { table: { disable: true } },
+ invalid: { table: { disable: true } },
+ disabled: { table: { disable: true } },
+ readonly: { table: { disable: true } },
+ placeholder: { table: { disable: true } },
+ fluid: { table: { disable: true } },
+ },
+ parameters: {
+ docs: {
+ description: {
+ story: `Интеграция с \`FloatLabel\` — плавающая метка внутри поля.
+
+Можно также использовать \`FloatLabel\` и \`PBlockInputText\` напрямую:
+
+\`\`\`vue
+
+
+
+
+\`\`\``,
+ },
+ source: {
+ code: `
+
+
+
+ `,
+ },
+ },
+ },
};
-export const WithIcons = {
- render: TemplateWithIcons.bind({}),
- name: 'withIcons',
+export const FloatLabelInvalid = {
+ name: 'FloatLabel + Invalid',
+ render: (args) => ({
+ components: { PBlockInputText },
+ setup() {
+ const value = ref('');
+ return { args, value };
+ },
+ template: `
+
+ `,
+ }),
+ args: {
+ label: 'Обязательное поле',
+ required: true,
+ showClear: true,
+ },
+ argTypes: {
+ label: {
+ control: 'text',
+ description: 'Текст плавающей метки',
+ table: {
+ category: 'Props',
+ type: { summary: 'string' },
+ },
+ },
+ required: {
+ control: 'boolean',
+ description: 'Показывает маркер обязательного поля `*` рядом с меткой',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: 'false' },
+ type: { summary: 'boolean' },
+ },
+ },
+ showClear: {
+ control: 'boolean',
+ description: 'Показывает иконку очистки при наличии значения',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: 'true' },
+ type: { summary: 'boolean' },
+ },
+ },
+ size: { table: { disable: true } },
+ invalid: { table: { disable: true } },
+ disabled: { table: { disable: true } },
+ readonly: { table: { disable: true } },
+ placeholder: { table: { disable: true } },
+ fluid: { table: { disable: true } },
+ },
+ parameters: {
+ docs: {
+ description: {
+ story:
+ 'FloatLabel с невалидным состоянием — демонстрирует стилизацию ошибки в комбинации с плавающей меткой.',
+ },
+ source: {
+ code: `
+
+
+
+ `,
+ },
+ },
+ },
};
diff --git a/src/plugins/prime/stories/Form/InputText/InputText.template.js b/src/plugins/prime/stories/Form/InputText/InputText.template.js
index e3b35ab5..b2fca786 100644
--- a/src/plugins/prime/stories/Form/InputText/InputText.template.js
+++ b/src/plugins/prime/stories/Form/InputText/InputText.template.js
@@ -1,49 +1,25 @@
-import InputText from 'primevue/inputtext';
-import { IconField, InputIcon } from 'primevue';
+import PBlockInputText from '@/primeBlocks/PBlockInputText/PBlockInputText.vue';
import { ref } from 'vue';
export const Template = (args) => ({
- components: { InputText },
+ components: { PBlockInputText },
setup() {
- return { args };
+ const value = ref('');
+ return { args, value };
},
template: `
-
-
-
- invalid
- disabled
-
-
-
-
-
-
- v-model="text input"
-
-
-
-
-`,
-});
-
-export const TemplateWithIcons = (args) => ({
- components: { InputText, IconField, InputIcon },
- setup() {
- const inputValue = ref('');
-
- const onClickDelete = () => {
- inputValue.value = '';
- };
-
- return { args, inputValue, onClickDelete };
- },
- template: `
-
-
-
-
-
-
+
`,
});
diff --git a/src/plugins/prime/theme3.0/components/css/inputtext.ts b/src/plugins/prime/theme3.0/components/css/inputtext.ts
new file mode 100644
index 00000000..36e04d15
--- /dev/null
+++ b/src/plugins/prime/theme3.0/components/css/inputtext.ts
@@ -0,0 +1,58 @@
+const css = ({ dt }: { dt: (token: string) => string }) => `
+
+/* ─── Базовые стили ─── */
+.p-inputtext {
+ border-width: ${dt('inputtext.extend.borderWidth')};
+ line-height: ${dt('fonts.lineHeight.250')};
+}
+
+/* ─── Disabled ─── */
+.p-inputtext:disabled {
+ background: ${dt('inputtext.root.disabledBackground')};
+ color: ${dt('inputtext.root.disabledColor')};
+}
+
+/* ─── Readonly ─── */
+.p-inputtext:enabled:read-only {
+ background: ${dt('inputtext.extend.readonlyBackground')};
+ color: ${dt('inputtext.root.color')};
+}
+
+/* ─── Focus ─── */
+.p-inputtext:enabled:focus {
+ box-shadow: 0 0 0 ${dt('inputtext.focusRing.width')} ${dt(
+ 'inputtext.focusRing.color'
+)};
+}
+
+/* ─── Invalid + Focus ─── */
+.p-inputtext.p-invalid:focus {
+ border-color: ${dt('inputtext.root.invalidBorderColor')};
+ box-shadow: 0 0 0 ${dt('inputtext.focusRing.width')} ${dt(
+ 'focusRing.extend.invalid'
+)};
+}
+
+/* ─── Extra Large ─── */
+.p-inputtext.p-inputtext-xlg {
+ font-size: ${dt('inputtext.extend.extXlg.fontSize')};
+ padding: ${dt('inputtext.extend.extXlg.paddingY')} ${dt(
+ 'inputtext.extend.extXlg.paddingX'
+)};
+}
+
+/* ─── IconField ─── */
+.p-iconfield[data-pc-name="iconfield"] {
+ width: fit-content;
+}
+
+.p-iconfield .p-inputicon {
+ font-size: ${dt('inputtext.extend.iconSize')};
+ width: ${dt('inputtext.extend.iconSize')};
+ height: ${dt('inputtext.extend.iconSize')};
+ cursor: pointer;
+}
+
+`;
+
+export default css;
diff --git a/src/plugins/prime/theme3.0/css.ts b/src/plugins/prime/theme3.0/css.ts
index 55362c06..0c964773 100644
--- a/src/plugins/prime/theme3.0/css.ts
+++ b/src/plugins/prime/theme3.0/css.ts
@@ -1,4 +1,3 @@
-// const css = ({ dt }: { dt: (token: string) => string }) => `
const css = () => `
.p-disabled, .p-component:disabled {
mix-blend-mode: luminosity;
diff --git a/src/plugins/prime/theme3.0/tokens.json b/src/plugins/prime/theme3.0/tokens.json
index a7d0103e..5718d930 100644
--- a/src/plugins/prime/theme3.0/tokens.json
+++ b/src/plugins/prime/theme3.0/tokens.json
@@ -2699,6 +2699,7 @@
"positionX": "{form.padding.300}",
"positionY": "{form.padding.300}",
"fontWeight": "{fonts.fontWeight.regular}",
+ "fontSize": "{fonts.fontSize.300}",
"active": {
"fontSize": "{fonts.fontSize.100}",
"fontWeight": "{fonts.fontWeight.regular}"
@@ -2709,7 +2710,7 @@
"top": "{form.padding.400}"
}
},
- "inside": {
+ "in": {
"input": {
"paddingTop": "{form.padding.700}",
"paddingBottom": "{form.padding.300}"
@@ -2920,9 +2921,9 @@
"iconSize": "{form.icon.300}",
"borderWidth": "{form.borderWidth}",
"extXlg": {
- "fontSize": "{form.fontSize}",
- "paddingX": "{form.paddingX}",
- "paddingY": "{form.paddingY}"
+ "fontSize": "{fonts.fontSize.300}",
+ "paddingX": "{form.padding.300}",
+ "paddingY": "{form.padding.600}"
}
},
"root": {
@@ -2940,19 +2941,19 @@
"placeholderColor": "{form.placeholderColor}",
"invalidPlaceholderColor": "{form.invalidPlaceholderColor}",
"shadow": "0",
- "paddingX": "{form.paddingX}",
- "paddingY": "{form.paddingY}",
+ "paddingX": "{form.padding.300}",
+ "paddingY": "{form.padding.300}",
"borderRadius": "{form.borderRadius.200}",
"transitionDuration": "{form.transitionDuration}",
"sm": {
- "fontSize": "{form.fontSize}",
- "paddingX": "{form.paddingX}",
- "paddingY": "{form.paddingY}"
+ "fontSize": "{fonts.fontSize.300}",
+ "paddingX": "{form.padding.300}",
+ "paddingY": "{form.padding.200}"
},
"lg": {
- "fontSize": "{form.fontSize}",
- "paddingX": "{form.paddingX}",
- "paddingY": "{form.paddingY}"
+ "fontSize": "{fonts.fontSize.300}",
+ "paddingX": "{form.padding.300}",
+ "paddingY": "{form.padding.400}"
},
"focusRing": {
"width": "{form.focusRing.width}",
@@ -4826,4 +4827,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/primeBlocks/PBlockInputText/PBlockInputText.vue b/src/primeBlocks/PBlockInputText/PBlockInputText.vue
new file mode 100644
index 00000000..00cc13c9
--- /dev/null
+++ b/src/primeBlocks/PBlockInputText/PBlockInputText.vue
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/primeBlocks/PBlockInputText/PBlockInputTextField.vue b/src/primeBlocks/PBlockInputText/PBlockInputTextField.vue
new file mode 100644
index 00000000..dbcf009e
--- /dev/null
+++ b/src/primeBlocks/PBlockInputText/PBlockInputTextField.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/primeBlocks/index.ts b/src/primeBlocks/index.ts
index d36572ae..76a9d2cc 100644
--- a/src/primeBlocks/index.ts
+++ b/src/primeBlocks/index.ts
@@ -1,4 +1,5 @@
import PBlockPassword from './PBlockExample/PBlockPassword.vue';
import PBlockToggleButton from './PBlockToggleButton/PBlockToggleButton.vue';
+import PBlockInputText from './PBlockInputText/PBlockInputText.vue';
-export { PBlockPassword, PBlockToggleButton };
+export { PBlockPassword, PBlockToggleButton, PBlockInputText };
diff --git a/yarn.lock b/yarn.lock
index ec05e009..4c2b9078 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1294,6 +1294,13 @@
type-fest "~2.19"
vue-component-type-helpers latest
+"@tabler/icons-vue@^3.41.1":
+ version "3.41.1"
+ resolved "https://registry.yarnpkg.com/@tabler/icons-vue/-/icons-vue-3.41.1.tgz#c7c79984d8125e112556f114754227d8a70cefca"
+ integrity sha512-OLLWlyrx6HdQpdgnZ8dO8uBGIIZwQDPgE8Gl6tUuAD7AgtfnmtFz8vb7dTEvxddM5CMpPt21ogPpBhJfEjSY+Q==
+ dependencies:
+ "@tabler/icons" "3.41.1"
+
"@tabler/icons-webfont@^3.22.0":
version "3.34.1"
resolved "https://registry.npmjs.org/@tabler/icons-webfont/-/icons-webfont-3.34.1.tgz"
@@ -1307,6 +1314,11 @@
resolved "https://registry.npmjs.org/@tabler/icons/-/icons-3.34.1.tgz"
integrity sha512-9gTnUvd7Fd/DmQgr3MKY+oJLa1RfNsQo8c/ir3TJAWghOuZXodbtbVp0QBY2DxWuuvrSZFys0HEbv1CoiI5y6A==
+"@tabler/icons@3.41.1":
+ version "3.41.1"
+ resolved "https://registry.yarnpkg.com/@tabler/icons/-/icons-3.41.1.tgz#dff9c77af287c73c2fcab4074cfa9f2069f9b7ee"
+ integrity sha512-OaRnVbRmH2nHtFeg+RmMJ/7m2oBIF9XCJAUD5gQnMrpK9f05ydj8MZrAf3NZQqOXyxGN1UBL0D5IKLLEUfr74Q==
+
"@tanstack/virtual-core@3.13.12":
version "3.13.12"
resolved "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz"