startup-ui 0.12.0 → 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.
Files changed (123) hide show
  1. package/AGENTS.md +18 -0
  2. package/CHANGELOG.md +39 -0
  3. package/CLAUDE.md +1 -0
  4. package/LICENSE +21 -0
  5. package/README.md +54 -0
  6. package/dist/defaults.css +83 -0
  7. package/dist/index.css +1 -1
  8. package/dist/startup-ui.cjs.js +33 -586
  9. package/dist/startup-ui.cjs.js.map +1 -1
  10. package/dist/startup-ui.es.js +7109 -8839
  11. package/dist/startup-ui.es.js.map +1 -1
  12. package/dist/types/components/SActionIcon.d.ts +2 -1
  13. package/dist/types/components/SActionIcon.d.ts.map +1 -1
  14. package/dist/types/components/SCanvas.d.ts +0 -1
  15. package/dist/types/components/SCanvas.d.ts.map +1 -1
  16. package/dist/types/components/SCheckbox.d.ts.map +1 -1
  17. package/dist/types/components/SCheckboxGroup.d.ts +4 -0
  18. package/dist/types/components/SCheckboxGroup.d.ts.map +1 -1
  19. package/dist/types/components/SColumnSettings.d.ts +4 -4
  20. package/dist/types/components/SColumnSettings.d.ts.map +1 -1
  21. package/dist/types/components/SConfirm/SConfirm.d.ts +2 -0
  22. package/dist/types/components/SConfirm/SConfirm.d.ts.map +1 -1
  23. package/dist/types/components/SCopyText.d.ts.map +1 -1
  24. package/dist/types/components/SDatePicker.d.ts +3 -4
  25. package/dist/types/components/SDatePicker.d.ts.map +1 -1
  26. package/dist/types/components/SDialog.d.ts.map +1 -1
  27. package/dist/types/components/SFilterGroup.d.ts +3 -3
  28. package/dist/types/components/SFilterGroup.d.ts.map +1 -1
  29. package/dist/types/components/SFooter.d.ts.map +1 -1
  30. package/dist/types/components/SForm.d.ts.map +1 -1
  31. package/dist/types/components/SFormRow.d.ts.map +1 -1
  32. package/dist/types/components/SHtmlEditor.d.ts +20 -0
  33. package/dist/types/components/SHtmlEditor.d.ts.map +1 -1
  34. package/dist/types/components/SImagePreview.d.ts.map +1 -1
  35. package/dist/types/components/SInput.d.ts +9 -2
  36. package/dist/types/components/SInput.d.ts.map +1 -1
  37. package/dist/types/components/SMenu.d.ts +39 -0
  38. package/dist/types/components/SMenu.d.ts.map +1 -0
  39. package/dist/types/components/SNote.d.ts.map +1 -1
  40. package/dist/types/components/SPagination.d.ts.map +1 -1
  41. package/dist/types/components/SProgressbar.d.ts.map +1 -1
  42. package/dist/types/components/SRadio.d.ts.map +1 -1
  43. package/dist/types/components/SRadioGroup.d.ts +4 -0
  44. package/dist/types/components/SRadioGroup.d.ts.map +1 -1
  45. package/dist/types/components/SSelect.d.ts +6 -0
  46. package/dist/types/components/SSelect.d.ts.map +1 -1
  47. package/dist/types/components/SStatus.d.ts.map +1 -1
  48. package/dist/types/components/STable.d.ts +4 -4
  49. package/dist/types/components/STable.d.ts.map +1 -1
  50. package/dist/types/components/SToggle.d.ts.map +1 -1
  51. package/dist/types/components/STooltip.d.ts +0 -1
  52. package/dist/types/components/STooltip.d.ts.map +1 -1
  53. package/dist/types/components/STree.d.ts +11 -5
  54. package/dist/types/components/STree.d.ts.map +1 -1
  55. package/dist/types/components/SUpload.d.ts +5 -4
  56. package/dist/types/components/SUpload.d.ts.map +1 -1
  57. package/dist/types/components/SVerticalMenu.d.ts.map +1 -1
  58. package/dist/types/components/htmlEditor/contentStyle.d.ts +9 -0
  59. package/dist/types/components/htmlEditor/contentStyle.d.ts.map +1 -0
  60. package/dist/types/components/icons.d.ts +24 -0
  61. package/dist/types/components/icons.d.ts.map +1 -0
  62. package/dist/types/config.d.ts +49 -0
  63. package/dist/types/config.d.ts.map +1 -0
  64. package/dist/types/global-components.d.ts +3 -6
  65. package/dist/types/global-components.d.ts.map +1 -1
  66. package/dist/types/index.d.ts +12 -2
  67. package/dist/types/index.d.ts.map +1 -1
  68. package/dist/types/locale/index.d.ts +49 -0
  69. package/dist/types/locale/index.d.ts.map +1 -0
  70. package/dist/types/locale/messages/en-US.d.ts +4 -0
  71. package/dist/types/locale/messages/en-US.d.ts.map +1 -0
  72. package/dist/types/locale/messages/en.d.ts +4 -0
  73. package/dist/types/locale/messages/en.d.ts.map +1 -0
  74. package/dist/types/locale/messages/ru.d.ts +4 -0
  75. package/dist/types/locale/messages/ru.d.ts.map +1 -0
  76. package/dist/types/locale/types.d.ts +74 -0
  77. package/dist/types/locale/types.d.ts.map +1 -0
  78. package/dist/types/plugin.d.ts +2 -1
  79. package/dist/types/plugin.d.ts.map +1 -1
  80. package/dist/types/utils/deepMerge.d.ts +9 -0
  81. package/dist/types/utils/deepMerge.d.ts.map +1 -0
  82. package/dist/types/utils/options.d.ts +25 -0
  83. package/dist/types/utils/options.d.ts.map +1 -0
  84. package/llms/components/data/sfilter.md +194 -0
  85. package/llms/components/data/spagination.md +114 -0
  86. package/llms/components/data/stable.md +638 -0
  87. package/llms/components/data/stree.md +213 -0
  88. package/llms/components/forms/scheckbox.md +139 -0
  89. package/llms/components/forms/sdatepicker.md +161 -0
  90. package/llms/components/forms/sform.md +240 -0
  91. package/llms/components/forms/shtmleditor.md +143 -0
  92. package/llms/components/forms/sinput.md +165 -0
  93. package/llms/components/forms/sradio.md +164 -0
  94. package/llms/components/forms/sselect.md +149 -0
  95. package/llms/components/forms/sswitch.md +69 -0
  96. package/llms/components/forms/supload.md +189 -0
  97. package/llms/components/interfaces/sactionbar.md +40 -0
  98. package/llms/components/interfaces/sactionicon.md +126 -0
  99. package/llms/components/interfaces/salert.md +87 -0
  100. package/llms/components/interfaces/sbutton.md +167 -0
  101. package/llms/components/interfaces/scolumnsettings.md +204 -0
  102. package/llms/components/interfaces/sconfirm.md +57 -0
  103. package/llms/components/interfaces/scopytext.md +67 -0
  104. package/llms/components/interfaces/sdashboard.md +130 -0
  105. package/llms/components/interfaces/sdialog.md +158 -0
  106. package/llms/components/interfaces/simagepreview.md +98 -0
  107. package/llms/components/interfaces/snote.md +64 -0
  108. package/llms/components/interfaces/sprogressbar.md +48 -0
  109. package/llms/components/interfaces/sstat.md +79 -0
  110. package/llms/components/interfaces/sstatus.md +76 -0
  111. package/llms/components/interfaces/stag.md +70 -0
  112. package/llms/components/interfaces/stimeline.md +47 -0
  113. package/llms/components/interfaces/stoggle.md +120 -0
  114. package/llms/components/interfaces/stooltip.md +88 -0
  115. package/llms/components/template/scanvas.md +61 -0
  116. package/llms/components/template/smenu.md +88 -0
  117. package/llms/components/template/sverticalmenu.md +113 -0
  118. package/llms/llms.txt +49 -0
  119. package/package.json +37 -4
  120. package/dist/types/components/SDropdownMenu.d.ts +0 -39
  121. package/dist/types/components/SDropdownMenu.d.ts.map +0 -1
  122. package/dist/types/components/SHorizontalMenu.d.ts +0 -33
  123. package/dist/types/components/SHorizontalMenu.d.ts.map +0 -1
@@ -0,0 +1,164 @@
1
+ # SRadioGroup > SRadio
2
+
3
+ Радио-кнопки.
4
+
5
+ <SToggleGroup>
6
+ <SToggle title="В чем отличие от аналогов?">
7
+ <p>В отличие от популярных библиотек компонентов для Vue3:</p>
8
+ <ol>
9
+ <li>Сразу идет с кликабельным стандартизированным лейблом в качестве простого атрибута. Это унифицирует код и внешний вид компонентов, упрощается поддержка и взаимозаменяемость.</li>
10
+ <li>Сразу из коробки идет кнопочный стиль, который часто используется.</li>
11
+ <li>Поддерживает три формата передачи опций в группы радио-кнопок, что удобно в зависимости от кейса:
12
+ <ol>
13
+ <li><code>&lt;SRadio /&gt;</code> — там где опции являются частью дизайна, их можно и удобно хардкодить в шаблон;</li>
14
+ <li><code>{value1: title1, value2: title2}</code> — что удобно для быстрого получения из key-value конфигов, а также из моделей — <code>User::pluck('name', 'id')</code>;</li>
15
+ <li><code>[[value1, title1], [value2, title2]]</code> — что удобно для выгрузки там, где важен порядок. Это минимизирует код в контроллерах, помогая сохранять принцип «тонкого контроллера», которого мы придерживаемся.</li>
16
+ </ol>
17
+ </li>
18
+ <li>Взаимозаменяемость формата опций с другими выбиралками из вариантов. Это позволяет легко заменять SRadioGroup на <a href="/pages/components/forms/sselect.html">SSelect</a> или <a href="/pages/components/forms/scheckbox.html">SCheckboxGroup</a>, не трогая бэкенд код.</li>
19
+ </ol>
20
+ </SToggle>
21
+ </SToggleGroup>
22
+
23
+ ## Группа радио-кнопок
24
+
25
+ ```vue
26
+ <template>
27
+ <SRadioGroup v-model="type">
28
+ <SRadio value="bug">Ошибка</SRadio>
29
+ <SRadio value="question">Вопрос</SRadio>
30
+ <SRadio value="idea">Идея</SRadio>
31
+ </SRadioGroup>
32
+ <p>Модель будет принимать значение выбранного варианта: <code>{{ type }}</code></p>
33
+ </template>
34
+ <script setup>
35
+ import { ref } from 'vue'
36
+ const type = ref('bug')
37
+ </script>
38
+ ```
39
+
40
+ ## Динамический набор значений
41
+
42
+ В предыдущем примере набор вариантов хардкодился в шаблоне, что удобно, когда набор значений относится к логическому уровню интерфейса. Но когда набор вариантов идет из базы данных или конфига, очень неудобно каждый раз формировать набор элементов через v-for, и вместо этого используем атрибут `options`.
43
+
44
+ ```vue
45
+ <template>
46
+ <SRadioGroup v-model="type" :options="options" />
47
+ </template>
48
+ <script setup>
49
+ import { ref } from 'vue'
50
+ const options = { 1: 'Ошибка', 2: 'Вопрос', 3: 'Идея' }
51
+ const type = ref(1)
52
+ </script>
53
+ ```
54
+
55
+ Где options — это объект вариантов выбора в формате <code>{value1: title1, value2: title2}</code> или массив в формате <code>[[value1, title1], [value2, title2]]</code>
56
+
57
+ ## Кнопочный стиль
58
+
59
+ Чтобы заменить стиль с кружочками на группу кнопок, добавляем атрибут `buttons`:
60
+
61
+ ```vue
62
+ <template>
63
+ <SRadioGroup v-model="type" :options="options" buttons />
64
+ </template>
65
+ <script setup>
66
+ import { ref } from 'vue'
67
+ const options = { 1: 'Ошибка', 2: 'Вопрос', 3: 'Идея' }
68
+ const type = ref(1)
69
+ </script>
70
+ ```
71
+
72
+ ## Вертикальный список радио-кнопок
73
+
74
+ Чтобы выводить группу радио-кнопок вертикальным списком, добавляем атрибут `vertical`:
75
+
76
+ ```vue
77
+ <template>
78
+ <SRadioGroup v-model="type" :options="options" vertical />
79
+ </template>
80
+ <script setup>
81
+ import { ref } from 'vue'
82
+ const options = { 1: 'Ошибка', 2: 'Вопрос', 3: 'Идея' }
83
+ const type = ref(1)
84
+ </script>
85
+ ```
86
+
87
+ ## Недоступное значение
88
+
89
+ Добавляем `disabled`-атрибут значению, которое должно быть недоступно для переключения.
90
+
91
+ ```vue
92
+ <template>
93
+ <SRadioGroup v-model="type">
94
+ <SRadio value="bug" disabled>Ошибка</SRadio>
95
+ <SRadio value="question">Вопрос</SRadio>
96
+ <SRadio value="idea">Идея</SRadio>
97
+ </SRadioGroup>
98
+ </template>
99
+ <script setup>
100
+ import { ref } from 'vue'
101
+ const type = ref('question')
102
+ </script>
103
+ ```
104
+
105
+ ## Нулевое значение (плейсхолдер)
106
+
107
+ У радио-кнопок иногда бывает «не выбранное значение», особенно в фильтрах при заданном наборе вариантов. Для этого удобно использовать синтаксис `placeholder`:
108
+
109
+ ```vue
110
+ <template>
111
+ <SRadioGroup v-model="type" placeholder="Все" :options="options" />
112
+ </template>
113
+ <script setup>
114
+ import { ref } from 'vue'
115
+ const options = { 1: 'Ошибка', 2: 'Вопрос', 3: 'Идея' }
116
+ const type = ref(null)
117
+ </script>
118
+ ```
119
+
120
+ ## Интерфейс компонента SRadioGroup
121
+
122
+ ### Свойства (Props)
123
+
124
+ | Название | Тип | По умолчанию | Описание |
125
+ |----------|-----|--------------|----------|
126
+ | v-model | `any` | `undefined` | Выбранное значение. |
127
+ | options | `Record \| Array` | `{}` | Список вариантов (объект `{value: title}` или массив `[[value, title]]`). |
128
+ | buttons | `boolean` | `false` | Использовать стиль кнопок вместо кружочков. |
129
+ | vertical | `boolean` | `false` | Расположение элементов в колонку. |
130
+ | placeholder | `string` | `undefined` | Текст для «нулевого» варианта выбора (с пустым значением). |
131
+
132
+ ### Слоты (Slots)
133
+
134
+ | Название | Описание |
135
+ |----------|----------|
136
+ | default | Содержимое группы (обычно компоненты `SRadio`). |
137
+
138
+ ### События (Events)
139
+
140
+ | Название | Параметры | Описание |
141
+ |----------|-----------|----------|
142
+ | change | `(value: any)` | Вызывается при изменении выбранного значения. |
143
+
144
+ ## Интерфейс компонента SRadio
145
+
146
+ ### Свойства (Props)
147
+
148
+ | Название | Тип | По умолчанию | Описание |
149
+ |----------|-----|--------------|----------|
150
+ | value | `string \| number \| boolean` | - | **Обязательное.** Значение элемента. |
151
+ | disabled | `boolean` | `false` | Отключает возможность выбора. |
152
+ | labelClass | `string` | `undefined` | Кастомные CSS классы для лейбла. |
153
+
154
+ ### Слоты (Slots)
155
+
156
+ | Название | Описание |
157
+ |----------|----------|
158
+ | default | Текст или HTML-содержимое лейбла радио-кнопки. |
159
+
160
+ <style lang="scss">
161
+ .s-radio {
162
+ color: var(--s-text);
163
+ }
164
+ </style>
@@ -0,0 +1,149 @@
1
+ # SSelect
2
+
3
+ Выпадающий список с вариантами выбора.
4
+
5
+ <SToggleGroup>
6
+ <SToggle title="В чем отличие от аналогов?">
7
+ <p>В отличие от популярных библиотек компонентов для Vue3:</p>
8
+ <ol>
9
+ <li>Исключает опции, которые, как правило, не используются в стартапах (флоат лейблы, размеры), но из-за которых разные программисты реализовывают компонент по-разному. Без лишних опций унифицируется код и внешний вид компонентов, упрощается поддержка и взаимозаменяемость.</li>
10
+ <li>Поддерживает два формата передачи опций, удобных для выгрузки из контроллеров Laravel:
11
+ <ol>
12
+ <li><code>{value1: title1, value2: title2}</code> — что удобно для быстрого получения из key-value конфигов, а также из моделей — <code>User::pluck('name', 'id')</code>;</li>
13
+ <li><code>[[value1, title1], [value2, title2]]</code> — что удобно для выгрузки там, где важен порядок. Это минимизирует код в контроллерах, помогая сохранять принцип «тонкого контроллера», которого мы придерживаемся.</li>
14
+ </ol>
15
+ </li>
16
+ <li>Взаимозаменяемость формата опций с другими выбиралками из вариантов. Это позволяет легко заменять SSelect на <a href="/pages/components/forms/scheckbox.html">SCheckboxGroup</a> или <a href="/pages/components/forms/sradio.html">SRadioGroup</a>, не трогая бэкенд код.</li>
17
+ </ol>
18
+ </SToggle>
19
+ </SToggleGroup>
20
+
21
+ ## Классический вариант
22
+ ```vue
23
+ <template>
24
+ <SSelect v-model="value" :options="options" placeholder="Выберите" />
25
+ </template>
26
+ <script setup>
27
+ import { ref } from 'vue'
28
+ const options = { 1: 'Иванов', 2: 'Петров', 3: 'Сидоров' }
29
+ const value = ref(null)
30
+ </script>
31
+ ```
32
+
33
+ Где options — это объект вариантов выбора в формате <code>{value1: title1, value2: title2}</code> или массив в формате <code>[[value1, title1], [value2, title2]]</code>
34
+
35
+ ## Фильтрация при вводе
36
+
37
+ ```vue
38
+ <template>
39
+ <SSelect v-model="value" :options="options" filterable placeholder="Выберите" />
40
+ </template>
41
+ <script setup>
42
+ import { ref } from 'vue'
43
+ const options = { 1: 'Иванов', 2: 'Петров', 3: 'Сидоров' }
44
+ const value = ref(null)
45
+ </script>
46
+ ```
47
+
48
+ ## Получение значений по API
49
+
50
+ Список значений можно подгружать по API — в том числе на каждый ввод. Ниже живой пример: варианты приходят с публичного тестового API [DummyJSON](https://dummyjson.com), запрос уходит в обработчике события `@filter`. Начните вводить имя (например, «jo»):
51
+
52
+ ```vue
53
+ <template>
54
+ <SSelect v-model="value" :options="options" filterable @filter="onFilter" placeholder="Начните вводить имя" />
55
+ </template>
56
+ <script setup>
57
+ import { ref, onMounted } from 'vue'
58
+
59
+ const value = ref(null)
60
+ const options = ref([])
61
+
62
+ // Подгружаем варианты с публичного тестового API (DummyJSON):
63
+ // при пустом запросе — первые 10, иначе — поиск по введённой строке
64
+ async function load(query = '') {
65
+ const url = query
66
+ ? `https://dummyjson.com/users/search?q=${encodeURIComponent(query)}&select=firstName,lastName`
67
+ : 'https://dummyjson.com/users?limit=10&select=firstName,lastName'
68
+ const { users } = await (await fetch(url)).json()
69
+ options.value = users.map(u => [u.id, `${u.firstName} ${u.lastName}`])
70
+ }
71
+
72
+ // Запрашиваем только непустую строку: при выборе значения фильтр сбрасывается в '',
73
+ // и перезагружать список не нужно — иначе выбранный вариант пропал бы из опций
74
+ const onFilter = (query) => { if (query) load(query) }
75
+
76
+ onMounted(() => load())
77
+ </script>
78
+ ```
79
+
80
+ Обработчик `@filter` получает введённую строку; ответ API мы приводим к формату опций (`[[value, title]]` или `{value: title}`) и кладём в `:options`. В реальном проекте запрос обычно стоит дебаунсить.
81
+
82
+ ## Возможность сброса значения
83
+
84
+ Иногда бывает нужно сделать возможность сбрасывать значение в невыбранное (null). Для этого используется атрибут `clearable`:
85
+
86
+ ```vue
87
+ <template>
88
+ <SSelect v-model="user" :options="options" clearable placeholder="Не выбрано" />
89
+ </template>
90
+ <script setup>
91
+ import { ref } from 'vue'
92
+ const options = { 1: 'Иванов', 2: 'Петров', 3: 'Сидоров' }
93
+ const user = ref(null)
94
+ </script>
95
+ ```
96
+
97
+ ## Виртуальный скролл
98
+
99
+ Когда доступно очень много вариантов выбора, можно применить виртуальный скролл для более быстрой загрузки, добавив атрибут `virtual`:
100
+
101
+ ```vue
102
+ <template>
103
+ <SSelect v-model="region" :options="regions" virtual placeholder="Выберите регион" />
104
+ </template>
105
+ <script setup>
106
+ import { ref } from 'vue'
107
+ // 1000 options — virtual scroll keeps it fast by rendering only the visible window
108
+ const regions = Object.fromEntries(
109
+ Array.from({ length: 1000 }, (_, i) => [i + 1, `Регион ${i + 1}`])
110
+ )
111
+ const region = ref(null)
112
+ </script>
113
+ ```
114
+
115
+ ## Интерфейс компонента
116
+
117
+ ### Свойства (Props)
118
+
119
+ | Название | Тип | По умолчанию | Описание |
120
+ |----------|-----|--------------|----------|
121
+ | v-model | any | - | Текущее выбранное значение. |
122
+ | options | Record \| Array | - | Список вариантов (объект или массив пар). |
123
+ | placeholder | string | - | Текст заглушки. |
124
+ | filterable | boolean | `false` | Включает текстовый поиск. |
125
+ | clearable | boolean | `false` | Показывает кнопку сброса значения. |
126
+ | disabled | boolean | `false` | Отключает компонент. |
127
+ | inline | boolean | `false` | Убирает границы и делает селект компактным. |
128
+ | virtual | boolean | `false` | Включает виртуальный скролл. |
129
+ | virtualScrollSize | number | `10` | Количество одновременно отображаемых элементов при виртуальном скролле. |
130
+
131
+ ### Слоты (Slots)
132
+
133
+ | Название | Параметры | Описание |
134
+ |----------|-----------|----------|
135
+ | value | `{ value: any }` | Кастомное отображение выбранного значения. |
136
+ | option | `{ option: { label: string, value: any } }` | Кастомное отображение элемента в выпадающем списке. |
137
+
138
+ ### События (Events)
139
+
140
+ | Название | Параметры | Описание |
141
+ |----------|-----------|----------|
142
+ | change | `(value: any)` | Вызывается при изменении значения. |
143
+ | filter | `(query: string)` | Вызывается при вводе текста в режиме поиска. |
144
+
145
+ <style lang="scss">
146
+ .s-select {
147
+ min-width: 200px;
148
+ }
149
+ </style>
@@ -0,0 +1,69 @@
1
+ # SSwitch
2
+
3
+ Включатель-выключатель.
4
+
5
+ <SToggle title="В чем отличие от аналогов?">
6
+ <p>В отличие от популярных библиотек компонентов для Vue3:</p>
7
+ <ol>
8
+ <li>Сразу идет с кликабельным стандартизированным лейблом в качестве простого атрибута. Это унифицирует код и внешний вид компонентов, упрощается поддержка и взаимозаменяемость.</li>
9
+ </ol>
10
+ </SToggle>
11
+
12
+ ## Базовый пример
13
+
14
+ ```vue
15
+ <template>
16
+ <SSwitch v-model="checked">Значение: {{ checked }}</SSwitch>
17
+ </template>
18
+ <script setup>
19
+ import { ref } from 'vue'
20
+ const checked = ref(false)
21
+ </script>
22
+ ```
23
+
24
+ Модель принимает значение true/false.
25
+
26
+ ## Недоступное состояние
27
+
28
+ ```vue
29
+ <template>
30
+ <SSwitch v-model="checked" disabled>Не работает</SSwitch>
31
+ </template>
32
+ <script setup>
33
+ import { ref } from 'vue'
34
+ const checked = ref(false)
35
+ </script>
36
+ ```
37
+
38
+ ## Кастомные да/нет-значения
39
+
40
+ Для включенного/отключенного состояния можно задать кастомные значения:
41
+
42
+ ```vue
43
+ <template>
44
+ <SSwitch v-model="value" true-value="yes" false-value="no">
45
+ Значение: {{ value }}
46
+ </SSwitch>
47
+ </template>
48
+ <script setup>
49
+ import { ref } from 'vue'
50
+ const value = ref('no')
51
+ </script>
52
+ ```
53
+
54
+ ## Интерфейс компонента
55
+
56
+ ### Свойства (Props)
57
+
58
+ | Название | Тип | По умолчанию | Описание |
59
+ |----------|-----|--------------|----------|
60
+ | v-model | `any` | `null` | Состояние переключателя. |
61
+ | disabled | `boolean` | `false` | Отключает возможность переключения. |
62
+ | true-value | `any` | `true` | Значение для «включенного» состояния. |
63
+ | false-value | `any` | `false` | Значение для «выключенного» состояния. |
64
+
65
+ ### Слоты (Slots)
66
+
67
+ | Название | Описание |
68
+ |----------|----------|
69
+ | default | Текст лейбла (описания) рядом с переключателем. |
@@ -0,0 +1,189 @@
1
+ # SUpload
2
+
3
+ Загрузка файла. Всегда вставляется внутри некой формы, не используется в отрыве от неё.
4
+
5
+ <SToggleGroup>
6
+ <SToggle title="В чем отличие от аналогов?">
7
+ <p>В отличие от популярных библиотек компонентов для Vue3:</p>
8
+ <ol>
9
+ <li>В других библиотеках Upload работает как поле, которое по дефолту сразу отправляет файл после выбора (что удобно, например, при импорте таблиц). На практике же гораздо чаще встречается кейс, когда файл/картинка — это атрибут некой модели, и этот файл нужно добавлять-редактировать в форме редактирования модели. SUpload в первую очередь заточен под этот, более частый кейс.</li>
10
+ <li>SUpload сразу работает в связке с SForm, что позволяет использовать его так же просто, как простые текстовые SInput, не задумываясь о том, как связывать данные с формой.</li>
11
+ </ol>
12
+ </SToggle>
13
+ </SToggleGroup>
14
+
15
+ ## Базовый пример
16
+
17
+ Если нужно вставить полем в форму. Сама отправка идёт через Inertia, поэтому базовый пример показан кодом (вживую не запускается):
18
+
19
+ ```vue
20
+ <template>
21
+ <SUpload v-model="screenshot" />
22
+ <SButton v-if="screenshot" @click="submit">Отправить</SButton>
23
+ </template>
24
+
25
+ <script setup>
26
+ import { ref } from 'vue';
27
+ import { router } from '@inertiajs/vue3';
28
+ import { SUpload, SButton, SAlert } from 'startup-ui';
29
+
30
+ const screenshot = ref(null);
31
+
32
+ function submit() {
33
+ router.post('/photos', { screenshot: screenshot.value }, {
34
+ preserveState: true,
35
+ onError: (errors) => console.error(errors),
36
+ onSuccess: () => SAlert.success('Файл отправлен'),
37
+ });
38
+ }
39
+ </script>
40
+ ```
41
+
42
+ ## Авто-отправка при выборе
43
+
44
+ Если нужна авто-отправка, то выполняем отправку формы по событию `@select`:
45
+
46
+ ```vue
47
+ <template>
48
+ <SUpload v-model="screenshot" @select="submit" />
49
+ </template>
50
+ <script setup>
51
+ import { ref } from 'vue'
52
+ import { SAlert } from 'startup-ui'
53
+ const screenshot = ref(null)
54
+ // в реальном приложении здесь router.post(...) (Inertia)
55
+ function submit() {
56
+ SAlert.success('Файл отправлен')
57
+ }
58
+ </script>
59
+ ```
60
+
61
+ ## Кастомный текст кнопки
62
+
63
+ Можно задать произвольный текст кнопки с помощью атрибута `upload-button-title`
64
+
65
+ ```vue
66
+ <template>
67
+ <SUpload v-model="screenshot" upload-button-title="Выбрать скриншот" />
68
+ </template>
69
+ <script setup>
70
+ import { ref } from 'vue'
71
+ const screenshot = ref(null)
72
+ </script>
73
+ ```
74
+
75
+ ## Кастомный шаблон
76
+
77
+ ```vue
78
+ <template>
79
+ <SUpload v-model="screenshot">
80
+ <template #header="{ choose, clear, files, isDragging }">
81
+ <div class="s-upload-area" :class="{ dragging: isDragging }">
82
+ <FontAwesomeIcon icon="cloud-arrow-up" class="s-upload-area-icon" />
83
+ <p>Перетащите файл сюда или <a href="javascript:void(0)" @click="choose">нажмите</a> для загрузки</p>
84
+ </div>
85
+ </template>
86
+ <template #preview="{ files, remove }">
87
+ <div v-if="files && files.length">
88
+ Файл выбран <SActionIcon @click="remove(files[0])" icon="trash" />
89
+ </div>
90
+ </template>
91
+ </SUpload>
92
+ </template>
93
+ <script setup>
94
+ import { ref } from 'vue'
95
+ const screenshot = ref(null)
96
+ </script>
97
+ ```
98
+
99
+ Слоты сразу пробрасывают методы и переменные, которые могут быть полезны в собственных шаблонах:
100
+
101
+ * В слоте кнопки выбора `#header`: `choose()` открывает окно выбора файла, `clear()` очищает набор выбранных файлов, `files` — список текущих выбранных файлов, `isDragging` — булево значение, равное true, когда над компонентом перемещают файл.
102
+ * В слоте предпросмотра выбранных файлов `#preview`: `files` — список текущих выбранных файлов, `remove(file)` — удаление конкретного файла из набора (передаётся элемент массива `files`).
103
+
104
+ ## Ограничения выбора
105
+
106
+ Для ограничения выбора по расширению добавляем атрибут `accept`:
107
+
108
+ ```vue
109
+ <template>
110
+ <SUpload v-model="screenshot" accept=".txt,.csv,.xlsx" />
111
+ </template>
112
+ <script setup>
113
+ import { ref } from 'vue'
114
+ const screenshot = ref(null)
115
+ </script>
116
+ ```
117
+
118
+ Для ограничения выбора по размеру файла добавляем атрибут `max-file-size`. Можно указывать человекочитаемо (`2M`, `512K`, `1.5GB` — единицы 1024-кратные, как в php.ini) или числом в байтах:
119
+
120
+ ```vue
121
+ <template>
122
+ <SUpload v-model="screenshot" max-file-size="500K" />
123
+ </template>
124
+ <script setup>
125
+ import { ref } from 'vue'
126
+ const screenshot = ref(null)
127
+ </script>
128
+ ```
129
+
130
+ ## Выбор нескольких файлов
131
+
132
+ ```vue
133
+ <template>
134
+ <SUpload v-model="screenshots" multiple />
135
+ </template>
136
+ <script setup>
137
+ import { ref } from 'vue'
138
+ const screenshots = ref([])
139
+ </script>
140
+ ```
141
+
142
+ ## Интерфейс компонента
143
+
144
+ ### Свойства (Props)
145
+
146
+ | Название | Тип | По умолчанию | Описание |
147
+ |----------|-----|--------------|----------|
148
+ | v-model | `any \| any[]` | `null` | Выбранный файл (объект File или строка) или массив файлов. |
149
+ | accept | `string` | `undefined` | Допустимые типы файлов (напр. `.jpg,.png`). |
150
+ | max-file-size | `number \| string` | `undefined` | Максимальный размер файла: в байтах (число) или человекочитаемо (`2M`, `512K`, `1.5GB`). |
151
+ | multiple | `boolean` | `false` | Позволяет выбирать несколько файлов. |
152
+ | upload-button-title | `string` | `'Выбрать файл(ы)'` | Текст на кнопке выбора (если не используется слот `header`). |
153
+
154
+ ### Слоты (Slots)
155
+
156
+ | Название | Параметры | Описание |
157
+ |----------|-----------|----------|
158
+ | header | `{ choose, clear, files, isDragging }` | Кастомная область выбора (триггер). |
159
+ | preview | `{ files, remove }` | Кастомная область предпросмотра выбранных файлов. |
160
+ | default | - | Содержимое под списком файлов. |
161
+
162
+ ### События (Events)
163
+
164
+ | Название | Параметры | Описание |
165
+ |----------|-----------|----------|
166
+ | select | `value` | Вызывается при выборе файлов. |
167
+ | clear | - | Вызывается при полной очистке списка. |
168
+
169
+ <style lang="scss">
170
+ .s-upload-area {
171
+ border: 1px dashed var(--s-border);
172
+ display: flex;
173
+ flex-direction: column;
174
+ align-items: center;
175
+ border-radius: var(--s-border-radius);
176
+ padding: var(--s-base-margin);
177
+ gap: var(--s-base-margin);
178
+ cursor: pointer;
179
+
180
+ &-icon {
181
+ font-size: 56px;
182
+ color: var(--s-primary);
183
+ }
184
+
185
+ &:hover, &.dragging {
186
+ border-color: var(--s-primary);
187
+ }
188
+ }
189
+ </style>
@@ -0,0 +1,40 @@
1
+ # SActionBar
2
+
3
+ Полоска действия (обычно, для выбранных элементов).
4
+
5
+ <SToggle title="В чем отличие от аналогов?">В популярных библиотеках прямого аналога нет.</SToggle>
6
+
7
+ ## Базовый пример
8
+
9
+ ```vue
10
+ <template>
11
+ <SCheckboxGroup v-model="users" :options="userOptions" />
12
+ <SActionBar v-if="users.length">
13
+ <SSelect v-model="massAction" :options="massActions" style="width: 250px" />
14
+ <SButton @click="applyMassAction">Применить</SButton>
15
+ </SActionBar>
16
+ </template>
17
+ <script setup>
18
+ import { ref } from 'vue'
19
+ import { SAlert } from 'startup-ui'
20
+
21
+ const userOptions = { 1: 'Иванов', 2: 'Петров', 3: 'Сидоров' }
22
+ const users = ref([])
23
+
24
+ const massActions = { sendBonus: 'Отправить бонус', greet: 'Поприветствовать', delete: 'Удалить' }
25
+ const massAction = ref('sendBonus')
26
+
27
+ function applyMassAction() {
28
+ users.value = []
29
+ SAlert.success('Действие выполнено')
30
+ }
31
+ </script>
32
+ ```
33
+
34
+ ## Интерфейс компонента
35
+
36
+ ### Слоты (Slots)
37
+
38
+ | Название | Описание |
39
+ |----------|----------|
40
+ | default | Интерактивные элементы, кнопки или формы для отображения в панели действий. Контент использует flexbox, элементы располагаются в ряд. |