From 7e2c6303bc2699f203e58b806eb61d2cc5331d89 Mon Sep 17 00:00:00 2001 From: SerraMorec Date: Mon, 26 Oct 2026 09:43:36 +0300 Subject: [PATCH 1/2] feat(sdds-acore/uikit-compose): Avatar styles have been translated to StatefulValue --- .../docs/components/AvatarUsage.md | 24 ++ .../fixtures/samples/avatar/AvatarSamples.kt | 52 +++ .../avatar/group/AvatarGroupSamples.kt | 2 +- .../.openspec.yaml | 2 + .../design.md | 101 +++++ .../proposal.md | 36 ++ .../specs/avatar-motion/spec.md | 57 +++ .../tasks.md | 33 ++ .../.openspec.yaml | 2 + .../design.md | 59 +++ .../proposal.md | 34 ++ .../specs/avatar-style/spec.md | 76 ++++ .../tasks.md | 40 ++ .../api/android/uikit-compose.api | 38 ++ .../uikit-compose/api/jvm/uikit-compose.api | 38 ++ sdds-core/uikit-compose/api/uikit-compose.api | 38 ++ .../kotlin/com/sdds/compose/uikit/Avatar.kt | 208 +++++++--- .../com/sdds/compose/uikit/AvatarGroup.kt | 21 +- .../sdds/compose/uikit/AvatarGroupStyle.kt | 129 ++++++- .../com/sdds/compose/uikit/AvatarStyle.kt | 359 +++++++++++++----- .../components/avatar/AvatarGroupMotion.kt | 23 ++ .../avatar/AvatarGroupMotionStyle.kt | 8 + .../motion/components/avatar/AvatarMotion.kt | 22 ++ .../components/avatar/AvatarMotionStyle.kt | 135 +++++++ 24 files changed, 1387 insertions(+), 150 deletions(-) create mode 100644 openspec/changes/archive/2026-10-15-avatar-motion-integration/.openspec.yaml create mode 100644 openspec/changes/archive/2026-10-15-avatar-motion-integration/design.md create mode 100644 openspec/changes/archive/2026-10-15-avatar-motion-integration/proposal.md create mode 100644 openspec/changes/archive/2026-10-15-avatar-motion-integration/specs/avatar-motion/spec.md create mode 100644 openspec/changes/archive/2026-10-15-avatar-motion-integration/tasks.md create mode 100644 openspec/changes/archive/2026-10-15-avatar-style-stateful-value/.openspec.yaml create mode 100644 openspec/changes/archive/2026-10-15-avatar-style-stateful-value/design.md create mode 100644 openspec/changes/archive/2026-10-15-avatar-style-stateful-value/proposal.md create mode 100644 openspec/changes/archive/2026-10-15-avatar-style-stateful-value/specs/avatar-style/spec.md create mode 100644 openspec/changes/archive/2026-10-15-avatar-style-stateful-value/tasks.md create mode 100644 sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarGroupMotion.kt create mode 100644 sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarGroupMotionStyle.kt create mode 100644 sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarMotion.kt create mode 100644 sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarMotionStyle.kt diff --git a/build-system/docs-template/compose-template/docs/components/AvatarUsage.md b/build-system/docs-template/compose-template/docs/components/AvatarUsage.md index cb8f394331..246aa85c38 100644 --- a/build-system/docs-template/compose-template/docs/components/AvatarUsage.md +++ b/build-system/docs-template/compose-template/docs/components/AvatarUsage.md @@ -65,3 +65,27 @@ AsyncImage( model = "https://cdn.example.com/test.png", ) ``` + +## Состояния и Motion API + +В `Avatar` и `Modifier.avatar(...)` можно передать `motion: Motion`. +`rememberAvatarMotion()` использует `LocalAvatarMotionStyle`, если стиль переходов не указан явно. +`StatefulValue` задаёт значения для состояний, а `AvatarMotionStyle` — переходы фона, текста, иконки действия и фона иконки действия. +По умолчанию все переходы используют `noMotion()`: значения меняются без анимации. + +```kotlin +// @sample: com/sdds/compose/uikit/fixtures/samples/avatar/Avatar_Motion.kt +``` + +Для передачи взаимодействий используйте `motion.context.interactionSource` также в `clickable`. +Статус `AvatarStatus` публикуется в `motion.context.semanticStateSource` автоматически; +индикатор статуса использует общий с аватаром `MotionContext`. +Форма и размеры переключаются по состояниям без интерполяции. +В `AvatarGroup` у каждого аватара собственный контекст: смена статуса одного элемента не анимирует соседей. + +### Переход со старых свойств + +Существующие вызовы с `interactionSource` сохраняются (перегрузка помечена deprecated) и делегируют motion-реализации. +Для чтения текущих значений используйте stateful-свойства `shapes`, `textStyles`, `colors.textBrushes`, +`colors.backgroundBrushes`, `colors.actionBrushes`, `colors.actionScrimBrushes` +и размерные свойства с суффиксом `Values`, например `dimensionValues.widthValues`. diff --git a/integration-core/uikit-compose-fixtures/src/androidMain/kotlin/com/sdds/compose/uikit/fixtures/samples/avatar/AvatarSamples.kt b/integration-core/uikit-compose-fixtures/src/androidMain/kotlin/com/sdds/compose/uikit/fixtures/samples/avatar/AvatarSamples.kt index 7acdb46d1c..10118cae5b 100644 --- a/integration-core/uikit-compose-fixtures/src/androidMain/kotlin/com/sdds/compose/uikit/fixtures/samples/avatar/AvatarSamples.kt +++ b/integration-core/uikit-compose-fixtures/src/androidMain/kotlin/com/sdds/compose/uikit/fixtures/samples/avatar/AvatarSamples.kt @@ -1,7 +1,13 @@ package com.sdds.compose.uikit.fixtures.samples.avatar +import androidx.compose.animation.core.tween +import androidx.compose.foundation.clickable import androidx.compose.foundation.shape.CircleShape import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush @@ -17,8 +23,13 @@ import com.sdds.compose.uikit.Counter import com.sdds.compose.uikit.CounterStyle import com.sdds.compose.uikit.IndicatorStyle import com.sdds.compose.uikit.badgeBuilder +import com.sdds.compose.uikit.graphics.brush.asStatefulBrush import com.sdds.compose.uikit.interactions.asInteractive import com.sdds.compose.uikit.interactions.asStatefulValue +import com.sdds.compose.uikit.motion.components.avatar.AvatarMotionStyle +import com.sdds.compose.uikit.motion.components.avatar.rememberAvatarMotion +import com.sdds.compose.uikit.motion.finite +import com.sdds.compose.uikit.motion.transition import com.sdds.docs.DocSample import com.sdds.docs.composableCodeSnippet @@ -94,3 +105,44 @@ fun Avatar_Extra() { ) } } + +@Composable +@DocSample(needScreenshot = false) +fun Avatar_Motion() { + composableCodeSnippet { + var status by remember { mutableStateOf(AvatarStatus.None) } + val activeState = setOf(AvatarStatus.Active) + val style = AvatarStyle.builder() + .shape(CircleShape) + .colors { + backgroundColor( + Color.LightGray.asStatefulBrush( + activeState to Color(0xFF4CAF50), + ), + ) + } + .dimensions { + width(88.0.dp) + height(88.0.dp) + } + .style() + val motion = rememberAvatarMotion( + style = AvatarMotionStyle.builder() + .backgroundColor(transition { segment { } changesWith { finite(tween(200)) } }) + .style(), + ) + Avatar( + modifier = Modifier.clickable( + interactionSource = motion.context.interactionSource, + indication = null, + ) { + status = if (status == AvatarStatus.None) AvatarStatus.Active else AvatarStatus.None + }, + style = style, + status = status, + placeholder = AvatarPlaceholder.Name("Michael Scott"), + motion = motion, + content = { }, + ) + } +} diff --git a/integration-core/uikit-compose-fixtures/src/androidMain/kotlin/com/sdds/compose/uikit/fixtures/samples/avatar/group/AvatarGroupSamples.kt b/integration-core/uikit-compose-fixtures/src/androidMain/kotlin/com/sdds/compose/uikit/fixtures/samples/avatar/group/AvatarGroupSamples.kt index 4f0b6687b9..2e04141da8 100644 --- a/integration-core/uikit-compose-fixtures/src/androidMain/kotlin/com/sdds/compose/uikit/fixtures/samples/avatar/group/AvatarGroupSamples.kt +++ b/integration-core/uikit-compose-fixtures/src/androidMain/kotlin/com/sdds/compose/uikit/fixtures/samples/avatar/group/AvatarGroupSamples.kt @@ -40,7 +40,7 @@ fun AvatarGroup_Style() { composableCodeSnippet { AvatarGroupStyle.builder() .avatarStyle(placeholder(AvatarStyle.builder().style(), "/** Стиль компонента */")) - .dimensions { + .dimensionValues { itemSpacing(2.0.dp) itemOffset(16.0.dp) } diff --git a/openspec/changes/archive/2026-10-15-avatar-motion-integration/.openspec.yaml b/openspec/changes/archive/2026-10-15-avatar-motion-integration/.openspec.yaml new file mode 100644 index 0000000000..49ea85b3f2 --- /dev/null +++ b/openspec/changes/archive/2026-10-15-avatar-motion-integration/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-14 diff --git a/openspec/changes/archive/2026-10-15-avatar-motion-integration/design.md b/openspec/changes/archive/2026-10-15-avatar-motion-integration/design.md new file mode 100644 index 0000000000..0aaa684c6c --- /dev/null +++ b/openspec/changes/archive/2026-10-15-avatar-motion-integration/design.md @@ -0,0 +1,101 @@ +## Context + +`Avatar` — Compose-компонент в `sdds-core/uikit-compose`. После миграции на `StatefulValue` (`avatar-style-stateful-value`) все визуальные значения стиля уже stateful: + +- `AvatarStyle.shapes: StatefulValue`, `textStyles: StatefulValue` +- `AvatarColors.backgroundBrushes/textBrushes: StatefulValue?`, `actionBrushes/actionScrimBrushes: StatefulValue` +- `AvatarDimensionValues.widthValues/heightValues/statusOffsetXValues/statusOffsetYValues/statusCutoutPaddingValues: StatefulValue` + +Но резолвинг в `Avatar.kt` идёт мгновенно через `getValue(interactionSource, stateSet)` / `getValueAsState(interactionSource, stateSet)`, где `stateSet = setOf(status)`. Анимации отсутствуют. + +`AvatarMotionStyle`/`AvatarMotion` добавлены заказчиком и **не изменяются**: свойства `backgroundColor`, `actionColor`, `actionScrimColor`, `textColor`: `MotionProperty`; `textStyle`: `MotionProperty`; по умолчанию все `noMotion()`. CompositionLocal — `LocalAvatarMotionStyle`, фабрика — `rememberAvatarMotion(style, motionContext)`. + +Эталон интеграции motion в этом же модуле: `Counter.kt`, `Switch.kt`/`internal/checkable/switch/BaseSwitch.kt`, `Chip.kt`, `motion/components/indicator/IndicatorMotion.kt` (+ `Indicator.kt`), `internal/segment/SegmentStyleValues.kt`. Хелперы резолва: `motion/BrushMotion.kt` → `StatefulValue.getBrushAsState(context, motionProperty, defaultValue)`; `motion/TextStyleMotion.kt` → `StatefulValue.getTextStyleAsState(context, motionProperty, defaultValue)`; `motion/StatefulValueMotion.kt` → `getDpAsState` и др. + +## Goals / Non-Goals + +**Goals:** +- Предоставить `motion: Motion` **новыми перегрузками** функций `Avatar` и `Modifier.avatar`; существующие сигнатуры не изменяются (binary-совместимость, additive API). +- Анимировать цвета кистей (`backgroundColor`, `textColor`, `actionColor`, `actionScrimColor`) и стиль текста через `getBrushAsState`/`getTextStyleAsState`. +- Перевести `AvatarStatus` в `motion.context.semanticStateSource`, чтобы статусно-зависимые значения резолвились и анимировались. +- Дать индикатору статуса общий с аватаром `MotionContext` (сейчас у него изолированный). +- Сохранить обратную совместимость: при `noMotion()` (дефолт) поведение и пиксели идентичны текущим. + +**Non-Goals:** +- Изменение `AvatarMotionStyle`/`AvatarMotion` (уже готовы). +- Motion для формы и размеров (shape/width/height/offsets/cutout) — резолв через `motion.context` без интерполяции, по образцу `BaseSwitch` (`getValueAsState(motion.context)`). +- Motion для `AvatarGroupStyle`/`AvatarGroupDimensions` (обычные `Dp`, не stateful) и отдельных `AvatarGroupMotionStyle`. +- Изменения в `tokens/*`, `plugin_theme_builder`, View-стеке. + +## Decisions + +**1. Сигнатуры: `motion` последним параметром; перегрузка — только где мешает trailing-лямбда** + +Правило: параметр `motion: Motion` с дефолтом добавляется **последним параметром** каждой публичной функции — это source-совместимо. Исключение: функции, у которых последний параметр — лямбда `content` (нельзя вставить параметр после обязательной trailing-лямбды, сохранив идиоматический порядок «параметры, затем лямбда»): для них создаётся **новая перегрузка** с `motion` перед `content`, а существующая функция **делегированно** вызывает её, конструируя motion из `interactionSource`. Это ровно схема `Counter.kt`: count-перегрузки получили `motion` в конец, контентная — новая перегрузка + делегирование старой. + +| Функция | Последний параметр | Решение | +|---|---|---| +| `Avatar(imageBitmap, …, filterQuality)` | `filterQuality: FilterQuality` | `motion = rememberAvatarMotion()` в конец, прокинуть вниз | +| `Avatar(imageVector, …, contentScale)` | `contentScale: ContentScale` | `motion` в конец, прокинуть вниз | +| `Avatar(modifier, painter, …, contentScale)` | `contentScale: ContentScale` | `motion` в конец, прокинуть вниз | +| `Avatar(…, extra, interactionSource, content: @Composable BoxScope.() -> Unit)` | лямбда `content` | **новая перегрузка** `Avatar(…, extra, motion: Motion = rememberAvatarMotion(), content)` — основная реализация; старая сигнатура без изменений, делегирует с `motion = rememberAvatarMotion(motionContext = rememberMotionContext(interactionSource as? MutableInteractionSource ?: MutableInteractionSource()))` (cast — приём `TabBarItem`), помечается `@Deprecated("Use Avatar with motion parameter")` как контентная перегрузка `Counter` | +| `Modifier.avatar(interactionSource, …, statusCutoutPadding): Modifier` | `statusCutoutPadding: Dp` | `motion: Motion = rememberAvatarMotion(motionContext = rememberMotionContext(cast(interactionSource)))` в конец — **без новой перегрузки**, реализация здесь | +| `Modifier.avatar(style, …)` (`@Deprecated`) | — | не трогаем (делегает предыдущей) | + +Смысл: `motion.context.interactionSource` — единственный источник взаимодействий внутри реализации (`Indicator.kt`); делегирующая контентная перегрузка связывает переданный `interactionSource` через контекст motion. + +Альтернативы и почему отклонены: +- *Перегрузки для каждой функции* — лишний API-surface, расходится с практикой `Counter`/`Chip` (там, где можно, motion добавлялся в сигнатуру). +- *Менять существующую контентную сигнатуру (заменить `interactionSource` на `motion` или вставить `motion` перед `content`)* — ломает вызовы с позиционными аргументами/`content` по позиции. + +**2. Единый источник состояний — `motion.context`** + +После входа в реализацию: `val interactionSource = motion.context.interactionSource` (как делает `Indicator.kt`), и **все** резолвы используют `motion.context`. `stateSet` из `setOf(status)` убирается из резолвинга — статус публикуется в семантический источник: + +```kotlin +SideEffect { + motion.context.semanticStateSource.setExclusive( + status, AvatarStatus.Inactive, AvatarStatus.Active, AvatarStatus.None, + ) +} +``` + +Это уже применялось к изолированному `indicatorMotion`; теперь к общему контексту. `setExclusive` (а не `replace`) сохраняет интерактивные состояния и исключает взаимно исключающие статусы. + +Публичный `interactionSource` сохраняет силу, пока `motion` не передан явно: дефолт `motion` привязывает контекст к `interactionSource` (см. таблицу Decision 1). Если вызывающий передал и `interactionSource`, и `motion` — действует `motion.context.interactionSource` (параметр игнорируется) — задокументировать в KDoc, как в `Indicator`. + +**3. Разделение анимируемых и мгновенных значений** + +| Значение | Резолвинг | +|---|---| +| `colors.backgroundBrushes` | `getBrushAsState(motion.context, motion.style.backgroundColor)` | +| `colors.textBrushes` | `getBrushAsState(motion.context, motion.style.textColor)` | +| `colors.actionBrushes` | `getBrushAsState(motion.context, motion.style.actionColor)` | +| `colors.actionScrimBrushes` | `getBrushAsState(motion.context, motion.style.actionScrimColor)` | +| `style.textStyles` | `getTextStyleAsState(motion.context, motion.style.textStyle)` | +| `shapes`, `*Values` (Dp) | `getValueAsState(motion.context)` — без интерполяции | +| `textColors`, `backgroundColors` (`InteractiveColor`) | legacy-путь, мгновенно через `motion.context.interactionSource` | + +`getBrushAsState`/`getTextStyleAsState` **не принимают nullable** `StatefulValue` → null-проверка обязательна до вызова (текущая логика `textBrushes`/`backgroundBrushes` + fallback на `InteractiveColor` сохраняется полностью). + +**4. Общий контекст для индикатора** + +`Modifier.indicator(motion = rememberIndicatorMotion(motionContext = motion.context), ...)` — по образцу `SegmentItem.kt` (`motion = rememberCounterMotion(motionContext = motion.context)`). Исправляет дефект: взаимодействия аватара теперь доходят до индикатора. Стиль индикатора остаётся `style.statusStyle ?: LocalIndicatorStyle.current`. + +Риск: индикатор начнёт реагировать на Pressed/Hovered аватара. Принято осознанно — это и есть цель интеграции; если в дизайн-системе у `statusStyle` есть hover-состояния, они станут живыми. Скриншотные тесты это проверят (goldens не должны измениться, т.к. скриншоты снимаются без взаимодействий). + +**5. Подпись `Modifier.action` остаётся brush-колбэком** + +Текущая private-функция принимает `color: Brush, scrimColor: Brush` (резолв до вызова). Меняем на `color: () -> Brush, scrimColor: () -> Brush`, читающие `State.value` внутри `drawWithContent` — анимация не вызывает рекомпозицию поддерева (паттерн `Counter`: `brushProducer = { backgroundColor.value }`). + +**6. Nullability значения по умолчанию** + +`getBrushAsState(context, motionProperty, defaultValue)` — `defaultValue` оставляем `null`: `StatefulValue.getDefaultValue()` уже содержит корректный дефолт стиля. Для `backgroundBrushes == null` используем прежний путь `backgroundColors` → `internal.background(brush, color)`, ничего не анимируем. + +## Risks / Trade-offs + +- [Двойственность `backgroundBrushes` (nullable) и `backgroundColors`] → сохранена текущая логика приоритета; анимируется только brush-путь. A/B-поведение legacy `InteractiveColor`-пути не меняется. +- [`SideEffect` с `setExclusive` выполняется после первого кадра] → на первой композиции статус может быть не применён в анимационном снапшоте. Mitigation: семантическое состояние инициализируется синхронно до резолва там, где это важно (Switch использует `rememberSwitchSemanticStateSource(active)` — `remember {}` с первичным значением). Применим тот же приём: `remember { MutableSemanticStateSource().apply { setExclusive(status, ...) } }` + `SideEffect` для обновлений. +- [`Modifier.avatar` на `composed {}` + motion] → `composed` устаревший путь; оставляем как есть (non-goal), motion передаётся в лямбду `composed` из внешней Composable-области, что корректно. +- [BCV] → изменение/добавление сигнатур требует `apiDump`; локально задача заблокирована сетью — проверить в CI. +- [Скриншотные goldens] → при `noMotion()` значения резолвятся так же (`motionProperty.isEmpty` → `rememberUpdatedState(getValue(...))` внутри `getBrushAsState`), пиксельных отличий быть не должно; обязательный прогон тестов. diff --git a/openspec/changes/archive/2026-10-15-avatar-motion-integration/proposal.md b/openspec/changes/archive/2026-10-15-avatar-motion-integration/proposal.md new file mode 100644 index 0000000000..6792cb6066 --- /dev/null +++ b/openspec/changes/archive/2026-10-15-avatar-motion-integration/proposal.md @@ -0,0 +1,36 @@ +## Why + +Стиль `Avatar` переведён на `StatefulValue` (change `avatar-style-stateful-value`), но значения резолвятся мгновенно (`getValue`/`getValueAsState(interactionSource, stateSet)`) — при смене состояния (нажатие, фокус, `AvatarStatus`) значения переключаются без анимации. В модуле уже есть инфраструктура motion (`Motion`/`MotionContext`, `getBrushAsState`, `getTextStyleAsState`), а эталонные компоненты (`Counter`, `Switch`, `Chip`, `SegmentItem`) поддерживают анимации через параметр `motion`. Пользователем уже добавлены `AvatarMotion` (`rememberAvatarMotion`) и `AvatarMotionStyle` (свойства `backgroundColor`, `actionColor`, `actionScrimColor`, `textColor`: `MotionProperty`, `textStyle`: `MotionProperty`, по умолчанию `noMotion()`); требуется внедрить motion в функции компонента. + +Также существует текущий дефект: `Modifier.avatar(...)` создаёт `rememberIndicatorMotion()` с изолированным контекстом (приватные `MutableInteractionSource`/`MutableSemanticStateSource`), поэтому нажатия/ховеры аватара не влияют на индикатор статуса. + +## What Changes + +- **`motion: Motion` добавляется последним параметром** в функции, где это обратно совместимо (последний параметр — не лямбда): `Avatar(imageBitmap, …)`, `Avatar(imageVector, …)`, `Avatar(painter, …)`, `Modifier.avatar(interactionSource, …)`. Внутри реализаций источником взаимодействий становится `motion.context.interactionSource` (как в `Indicator`/`Switch`). +- **Только для `Avatar(…, interactionSource, content)`** (последний параметр — лямбда `content`) добавление параметра ломает совместимость: создаётся **новая перегрузка** `Avatar(…, extra, motion: Motion, content)` (основная реализация), а существующая функция остаётся с прежней сигнатурой и делегирует ей, конструируя motion из переданного `interactionSource` (`@Deprecated("Use Avatar with motion parameter")` по образцу контентной перегрузки `Counter`). Это же разграничение реализовано в `Counter.kt`. +- **Анимируемые значения** (при заданном motion-свойстве): `colors.backgroundBrushes` → `motion.style.backgroundColor`, `colors.textBrushes` → `motion.style.textColor`, `colors.actionBrushes` → `motion.style.actionColor`, `colors.actionScrimBrushes` → `motion.style.actionScrimBrushes` (все через `getBrushAsState`), `style.textStyles` → `motion.style.textStyle` (через `getTextStyleAsState`). +- **Мгновенные значения** (motion не вводится, как в `BaseSwitch`/`Indicator`): `shapes`, `widthValues`/`heightValues`, `statusOffsetXValues`/`statusOffsetYValues`, `statusCutoutPaddingValues` — резолвятся через `getValueAsState(motion.context)` без интерполяции. +- **Статус** — `AvatarStatus` публикуется в `motion.context.semanticStateSource` через `setExclusive(status, Inactive, Active, None)` (SideEffect), вместо локального `stateSet` для анимируемых путей; статусно-зависимые `StatefulValue` начинают резолвиться и анимироваться через контекст. +- **Внутренний индикатор** — `rememberIndicatorMotion(motionContext = motion.context)`: общий контекст с аватаром; исправляет дефект изоляции interaction source. +- **Legacy-пути `InteractiveColor`** (`colors.textColors`, `colors.backgroundColors`, используемые когда brush-свойства `null`) остаются мгновенными — поведение без изменений. +- **`AvatarMotionStyle`/`AvatarMotion` не изменяются** (уже добавлены); **`AvatarGroup` не изменяется** — стиль анимации достигает элементов группы через `LocalAvatarMotionStyle`, каждый `Avatar` сохраняет собственный контекст. +- **Docs/фикстуры** — пример `Avatar_Motion` в `uikit-compose-fixtures`, секция «Состояния и Motion API» в `AvatarUsage.md`. + +## Capabilities + +### New Capabilities + +- `avatar-motion`: motion-API компонента `Avatar` — параметры `motion` функций, резолв/анимация stateful-значений через `MotionContext`, публикация статуса в семантический источник, поведение совместимости. + +### Modified Capabilities + +- `avatar-style`: не меняется (API стиля остаётся прежним; change только использует его). + +## Impact + +- `sdds-core/uikit-compose` — `Avatar.kt` (параметры и резолв), импорты `motion.*`; `AvatarStyle.kt`/`AvatarGroup.kt` без изменений. +- Публичный API: **source-compatible** — `motion` добавляется последним параметром с дефолтом там, где это безопасно; для контентной перегрузки — только новая перегрузка + делегирование. Существующие вызовы компилируются без изменений. JVM-сигнатуры функций с добавленным параметром меняются → обновить BCV-бейзлайны `apiDump` (`api/uikit-compose.api`, `api/android/`, `api/jvm/`) — локально `apiDump` заблокирован сетью (SSL), выполнить в CI. +- Дефолты: `AvatarMotionStyle` по умолчанию `noMotion()` → скриншотные тесты и поведение существующих вызовов не меняются. +- `tokens/*`, `integration-core/sandbox-compose` (сгенерированный код) — не затрагиваются, регенерация не требуется. +- `integration-core/uikit-compose-fixtures` — новый doc-sample; `build-system/docs-template` — документация. +- Валидация: компиляция `:sdds-core:uikit-compose`, detekt/spotless, компиляция токенов, скриншотные тесты Avatar, (опционально) целевой motion-тест по образцу `SelectItemMotionTest` в `tokens/sdds.serv.compose`. diff --git a/openspec/changes/archive/2026-10-15-avatar-motion-integration/specs/avatar-motion/spec.md b/openspec/changes/archive/2026-10-15-avatar-motion-integration/specs/avatar-motion/spec.md new file mode 100644 index 0000000000..676429cf59 --- /dev/null +++ b/openspec/changes/archive/2026-10-15-avatar-motion-integration/specs/avatar-motion/spec.md @@ -0,0 +1,57 @@ +## ADDED Requirements + +### Requirement: Avatar functions SHALL accept Motion compatibly +`sdds-core/uikit-compose` SHALL предоставить функции `Avatar(...)` и `Modifier.avatar(...)`, принимающие `motion: Motion`. Параметр `motion` с дефолтным значением SHALL добавляться **последним параметром** функций, у которых последний параметр не является лямбдой (`Avatar(imageBitmap/…)`, `Avatar(imageVector/…)`, `Avatar(painter/…)`, `Modifier.avatar(interactionSource/…)`). Для функции `Avatar(…, interactionSource, content: @Composable BoxScope.() -> Unit)` (последний параметр — лямбда) SHALL быть создана **новая перегрузка** с `motion` перед `content`, а существующая сигнатура SHALL сохраниться без изменений и делегировать новой, конструируя motion из переданного `interactionSource`. + +#### Scenario: Существующие вызовы компилируются без изменений +- **WHEN** существующий код вызывает `Avatar(style = s, status = st) { ... }`, `Modifier.avatar(interactionSource, style)` или convenience-перегрузку с позиционными/именованными аргументами +- **THEN** код MUST компилироваться без изменений и MUST рендериться идентично предыдущей версии + +#### Scenario: Делегирование контентной перегрузки +- **WHEN** вызывается `Avatar(..., interactionSource = src) { ... }` +- **THEN** реализация MUST использовать motion, чей контекст связан с `src`, и поведение MUST совпадать с предыдущей версией + +#### Scenario: Перегрузка с motion +- **WHEN** вызывается `Avatar(style = s, motion = rememberAvatarMotion()) { ... }` +- **THEN** компонент MUST использовать `motion.context.interactionSource` как источник взаимодействий + +### Requirement: Animated values SHALL resolve through MotionContext +В motion-реализации `Modifier.avatar` SHALL анимироваться: `colors.backgroundBrushes` через `getBrushAsState(motion.context, motion.style.backgroundColor)`, `colors.textBrushes` через `motion.style.textColor`, `colors.actionBrushes` через `motion.style.actionColor`, `colors.actionScrimBrushes` через `motion.style.actionScrimColor`, `style.textStyles` через `getTextStyleAsState(motion.context, motion.style.textStyle)`. `shapes` и все `*Values: StatefulValue` SHALL резолвиться через `motion.context` без интерполяции. Nullability `backgroundBrushes`/`textBrushes` SHALL сохраняться: при `null` используется существующий legacy-путь `backgroundColors`/`textColors` (`InteractiveColor`, мгновенный резолв). + +#### Scenario: noMotion по умолчанию не анимирует +- **WHEN** используется `AvatarMotionStyle.builder().style()` (все свойства `noMotion()`) и меняется состояние +- **THEN** значения MUST переключаться мгновенно, без анимационных кадров между состояниями + +#### Scenario: transition анимирует смену статуса +- **WHEN** motion-стиль содержит `backgroundColor = transition { segment { } changesWith { finite(tween(200)) } }`, а `backgroundBrushes` заданы разными `SolidColor` для `AvatarStatus.None`/`AvatarStatus.Active` +- **AND** компонент отрисуется, затем `status` изменится с `None` на `Active` +- **THEN** кисть фона MUST проходить промежуточные значения между цветами во время `tween` + +#### Scenario: Legacy InteractiveColor путь без изменений +- **WHEN** `backgroundBrushes == null` и задан `backgroundColors: InteractiveColor` +- **THEN** фон MUST резолвиться мгновенно по состояниям, поведение MUST совпадать с предыдущей версией + +### Requirement: AvatarStatus SHALL publish to MotionContext semantic state source +Реализация `Avatar` SHALL инициализировать `motion.context.semanticStateSource` текущим статусом до первого резолва (`remember` с первичным `setExclusive(status, AvatarStatus.Inactive, AvatarStatus.Active, AvatarStatus.None)`) и SHALL обновлять его через `SideEffect` при изменении `status`. Локальный `stateSet = setOf(status)` SHALL больше не участвовать в резолвинге значений. + +#### Scenario: Статусно-зависимые значения резолвятся через контекст +- **WHEN** `backgroundBrushes` содержит значение для состояния `AvatarStatus.Active`, компонент отображается со `status = Active` +- **THEN** `getBrushAsState` MUST вернуть значение для `AvatarStatus.Active` без явной передачи stateSet + +### Requirement: Indicator inside Avatar SHALL share Avatar MotionContext +`Modifier.indicator` внутри аватара SHALL вызываться с `rememberIndicatorMotion(motionContext = motion.context)`, сохраняя стиль `style.statusStyle ?: LocalIndicatorStyle.current`. + +#### Scenario: Взаимодействия аватара достигают индикатора +- **WHEN** аватар находится в нажатом состоянии (Pressed через interaction source аватара) и у индикатора заданы значения для `InteractiveState.Pressed` +- **THEN** индикатор MUST применить нажатое значение (ранее — приватный источник индикатора не получал взаимодействия) + +#### Scenario: Скриншоты без взаимодействий не меняются +- **WHEN** выполняются существующие скриншотные тесты Avatar c дефолтными стилями и без взаимодействий +- **THEN** отрендеренные артефакты MUST остаться идентичными предыдущей версии + +### Requirement: AvatarGroup SHALL inherit motion style via CompositionLocal +`AvatarGroup` SHALL не требовать изменений: элементы группы получают стиль анимации через `LocalAvatarMotionStyle`, каждый `Avatar` обязан иметь собственный `MotionContext` (смена статуса одного элемента не должна анимировать соседей). + +#### Scenario: Независимость контекстов элементов группы +- **WHEN** в `AvatarGroup` у одного аватара меняется `status` +- **THEN** анимация MUST произойти только у этого аватара diff --git a/openspec/changes/archive/2026-10-15-avatar-motion-integration/tasks.md b/openspec/changes/archive/2026-10-15-avatar-motion-integration/tasks.md new file mode 100644 index 0000000000..93aa5b3277 --- /dev/null +++ b/openspec/changes/archive/2026-10-15-avatar-motion-integration/tasks.md @@ -0,0 +1,33 @@ +## 1. sdds-core/uikit-compose — Modifier.avatar: motion-параметр и резолв + +- [x] 1.1 В существующую `@Composable fun Modifier.avatar(interactionSource, style, status, action, actionEnabled, placeholder, statusCutoutEnabled, statusCutoutPadding)` добавить в конец `motion: Motion = rememberAvatarMotion(motionContext = rememberMotionContext(interactionSource as? MutableInteractionSource ?: MutableInteractionSource()))` (каст — приём `TabBarItem`); внутри `val interactionSource = motion.context.interactionSource`; семантический источник инициализировать `remember { MutableSemanticStateSource().apply { setExclusive(status, Inactive, Active, None) } }` до резолва + `SideEffect` для обновлений статуса +- [x] 1.2 Перевести резолв анимируемых значений: `colors.backgroundBrushes?.getBrushAsState(motion.context, motion.style.backgroundColor)`, `colors.textBrushes?.getBrushAsState(motion.context, motion.style.textColor)`, `colors.actionBrushes.getBrushAsState(motion.context, motion.style.actionColor)`, `colors.actionScrimBrushes.getBrushAsState(motion.context, motion.style.actionScrimColor)`, `style.textStyles.getTextStyleAsState(motion.context, motion.style.textStyle)`; null-проверки brush-свойств и legacy-пути `textColors`/`backgroundColors` (через `colorForInteraction(motion.context.interactionSource, ...)`) сохранить +- [x] 1.3 Мгновенные значения (`shapes`, `widthValues`, `heightValues`, `statusOffsetXValues`, `statusOffsetYValues`) резолвить через `getValueAsState(motion.context).value` / `getValue(motion.context.interactionSource, stateSet)` вместо исходного источника +- [x] 1.4 Индикатор: `rememberIndicatorMotion(motionContext = motion.context)` вместо изолированного `rememberIndicatorMotion()`; убрать старый `SideEffect` c `setExclusive` (публикация статуса идёт в общий контекст по 1.1) +- [x] 1.5 Приватный `Modifier.action` перевести на `color: () -> Brush`, `scrimColor: () -> Brush` (чтение `State.value` внутри `drawWithContent`), tint — из `SolidColor` (текущая логика) +- [x] 1.6 Новая motion-перегрузка для `Modifier.avatar` НЕ создаётся (параметр добавлен в существующую по 1.1); `@Deprecated fun Modifier.avatar(style, ...)` остаётся без изменений; KDoc обновить `@param motion` + +## 2. sdds-core/uikit-compose — Avatar composable: motion-перегрузки и параметры + +- [x] 2.1 Добавить **новую** перегрузку `Avatar(modifier, style, status, action, actionEnabled, placeholder, extra, motion: Motion, content)` — основная реализация (существующая контентная сигнатура заканчивается лямбдой, добавлять motion в неё нельзя); `motion` **без дефолта** — иначе вызовы без `motion`/`interactionSource` дают overload ambiguity (тот же приём — обязательный `motion` в `Modifier.indicator`); `width`/`height`/`statusCutoutPadding` резолвить из `motion.context` (без `stateSet`) +- [x] 2.2 Существующая `Avatar(modifier, style, status, action, actionEnabled, placeholder, extra, interactionSource, content)`: сигнатура не меняется; `@Deprecated("Use Avatar with motion parameter")`; делегирует 2.1 с `motion = rememberAvatarMotion(motionContext = rememberMotionContext())` +- [x] 2.3 Convenience-перегрузки `Avatar(painter, ...)`, `Avatar(imageVector, ...)`, `Avatar(imageBitmap, ...)`: добавить `motion: Motion = rememberAvatarMotion(...)` последним параметром (после `contentScale`/`filterQuality` — лямбды нет, безопасно), прокинуть в 2.1/2.2, дополнить KDoc `@param motion` +- [x] 2.4 Импорты: `getBrushAsState`, `getTextStyleAsState`, `MutableSemanticStateSource`, `Motion` (убрать неиспользуемые) + +## 3. Метаданные и публичный API + +- [ ] 3.1 Обновить BCV-бейзлайны (`api/uikit-compose.api`, `api/android/uikit-compose.api`, `api/jvm/uikit-compose.api`) — новые motion-перегрузки, удалённых members нет; локальный `apiDump` заблокирован сетью (SSL), обновить вручную по формату BCV и перепроверить `apiCheck` в CI + +## 4. Примеры и документация — integration-core, build-system + +- [ ] 4.1 В `integration-core/uikit-compose-fixtures/.../samples/avatar/AvatarSamples.kt` добавить `Avatar_Motion` (`@DocSnippet(needScreenshot = false)`) по образцу `SegmentItem_Motion`: переключаемый `status`/клик через `motion.context.interactionSource`, style с статусно-зависимым `backgroundBrushes`, `rememberAvatarMotion(style = AvatarMotionStyle.builder().backgroundColor(transition { segment { } changesWith { finite(tween(200)) } }).style())` +- [ ] 4.2 В `build-system/docs-template/compose-template/docs/components/AvatarUsage.md` добавить секцию «Состояния и Motion API» по образцу `SegmentUsage.md` (передача motion, `LocalAvatarMotionStyle`, `noMotion()` по умолчанию, `motion.context.interactionSource` в `clickable`, статус через семантический источник); в `docs/graphics/MotionAPI.md` добавить `getTextStyleAsState` в каталог резолверов + +## 5. Валидация — sdds-core, tokens + +- [ ] 5.1 `./gradlew :sdds-core:uikit-compose:compileDebugKotlin` — нет ошибок +- [ ] 5.2 `./gradlew :sdds-core:uikit-compose:detekt` и `./gradlew :sdds-core:uikit-compose:spotlessApply` +- [ ] 5.3 `./gradlew :integration-core:uikit-compose-fixtures:compileDebugKotlin` (новый sample) и компиляция 1–2 токеновых систем (`:tokens:plasma.giga.compose:compileDebugKotlin`, `:tokens:sdds-sbcom-compose:compileDebugKotlin`) +- [ ] 5.4 Скриншотные тесты: `./gradlew :tokens:plasma.giga.compose:testDebugUnitTest --tests "*ComposeAvatarScreenshotTest*"` и `:tokens:sdds-sbcom-compose:...` — goldens без изменений (`noMotion()` по умолчанию) +- [ ] 5.5 Целевой motion-тест по образцу `tokens/sdds.serv.compose/src/androidUnitTest/kotlin/com/sdds/serv/SelectItemMotionTest.kt`: `AvatarMotionTest` — при `transition`-стиле с `tween` смена `status` анимирует `backgroundBrushes` (прогресс между SolidColor), при `noMotion()` — мгновенное переключение; добавить только если тестовая инфраструктура `sdds.serv.compose` позволяет собрать Avatar-стили (иначе — задокументировать пропуск) +- [ ] 5.6 `./gradlew :sdds-core:uikit-compose:apiCheck` в CI (локально заблокирован) diff --git a/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/.openspec.yaml b/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/.openspec.yaml new file mode 100644 index 0000000000..29382a2c80 --- /dev/null +++ b/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-09-29 diff --git a/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/design.md b/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/design.md new file mode 100644 index 0000000000..057c595b25 --- /dev/null +++ b/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/design.md @@ -0,0 +1,59 @@ +## Context + +`AvatarStyle` — стиль Compose-компонента `Avatar` в `sdds-core/uikit-compose`. Всё семейство стилей (`AvatarStyle`, `AvatarStyleBuilder`, `AvatarColors`, `AvatarColorsBuilder`, `AvatarDimensionValues`, `AvatarDimensionsBuilder`, `DefaultAvatarStyle`, `DefaultAvatarColors`, `DefaultAvatarDimensionValues`) находится в одном файле `sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarStyle.kt`. + +Состояние на момент изменения: + +- `AvatarColors` уже частично мигрирован: есть `textBrushes: StatefulValue?` + `textColors: InteractiveColor?` и `backgroundBrushes: StatefulValue?` + `backgroundColors: InteractiveColor?`, deprecated-compat свойства (`textColor`, `backgroundColor`) вычисляются через `getDefaultValue()`. Не мигрированы только `actionColor: Color` и `actionScrimColor: Color`. +- `AvatarStyle.shape: Shape` и `AvatarStyle.textStyle: TextStyle` — простые значения. +- `AvatarDimensionValues` — пять простых `Dp`-свойств (`statusOffsetX`, `statusOffsetY`, `width`, `height`, `statusCutoutPadding`). +- Свойство `AvatarStyle.dimensions: AvatarDimensions` уже помечено `@Deprecated("Use dimensionValues")` и не участвует в миграции. + +Эталонные реализации миграции в этом же модуле: `IndicatorStyle.kt` (shape→`shapes`, `width`/`widthValues`), `SwitchStyle.kt` (`toggleTrackWidth` + `toggleTrackWidthValues`), `PopoverStyle.kt`, `ChipStyle.kt`. `StatefulValue` определён в этом же репозитории: `com.sdds.compose.uikit.interactions.StatefulValue` (методы `getValue(stateSet)`, `getDefaultValue()`, `isStateful()`), конструируется через `T.asStatefulValue(vararg valueStates)`. + +## Goals / Non-Goals + +**Goals:** +- `AvatarStyle.shape`/`textStyle` → `shapes: StatefulValue`/`textStyles: StatefulValue`. +- `AvatarColors.actionColor`/`actionScrimColor` → `actionBrushes`/`actionScrimBrushes: StatefulValue`. +- Каждое свойство `AvatarDimensionValues` → `*Values: StatefulValue` (суффикс `Values` — паттерн `SwitchDimensionValues`/`IndicatorDimensions`). +- Полная обратная совместимость: старые свойства и builder-перегрузки сохраняются, помечаются `@Deprecated` с `ReplaceWith` на новое property. +- Реализации (`Default*`) хранят только stateful-значения; deprecated-свойства вычисляются из них. +- Дефолтные значения не меняются (`RoundedCornerShape(50)`, `TextStyle.Default`, `Color.White`, `AvatarDefaults.DefaultScrimColor`, `0.dp`, `3.dp`). + +**Non-Goals:** +- Повторная миграция уже stateful-частей `AvatarColors` (`textBrushes`/`textColors`/`backgroundBrushes`/`backgroundColors`) — не трогаем. +- Motion-анимации переходов между состояниями значений Avatar. +- Изменения в `tokens/*`, `plugin_theme_builder`, View-стеке (`uikit`), а также миграция других компонентов. +- Удаление `AvatarDimensions` (deprecated data class) — остаётся как есть. + +## Decisions + +**Naming: `shapes`/`textStyles`, `actionBrushes`/`actionScrimBrushes`, `*Values`** +Для shape/TextStyle используется множественное число (`shapes`, `textStyles`) — как `IndicatorStyle.shapes`, `AiHeaderStyle.titleStyles`. Для action-цветов — суффикс `Brushes` по образцу уже мигрированных `textBrushes`/`backgroundBrushes` в этом же `AvatarColors`. Для размеров — суффикс `Values` (`widthValues`, `heightValues`, `statusOffsetXValues`, `statusOffsetYValues`, `statusCutoutPaddingValues`) — как `SwitchDimensionValues.toggleTrackWidthValues` и `IndicatorDimensions.widthValues`. Альтернатива (единый суффикс `Values` для всего) отклонена: противоречит устоявшимся в модуле паттернам для shape/кистей. + +**Хранение в `Default*` — только stateful** +Основные constructor-properties `DefaultAvatarStyle`, `DefaultAvatarColors`, `DefaultAvatarDimensionValues` — только `StatefulValue`. Deprecated-свойства вычисляются как `get()`: +- `override val shape: Shape get() = shapes.getDefaultValue()` (по образцу `DefaultIndicatorStyle`). +- `override val width: Dp get() = widthValues.getDefaultValue()` (по образцу `DefaultSwitchDimensionValues`). +- Для цветов: `override val actionColor: Color get() = (actionBrushes.getDefaultValue() as? SolidColor)?.value ?: Color.White` — по образцу `TabBarStyle.kt:372` / `SegmentStyleValues.legacyColor()` (извлечение `Color` из `SolidColor`). Fallback равен существующему дефолту (`Color.White` для action, `AvatarDefaults.DefaultScrimColor` для scrim), что сохраняет прежнее поведение при non-solid brush. + +**Builder: abstract StatefulValue-перегрузка + default Dp/Color-перегрузка** +В `AvatarDimensionsBuilder` абстрактным остаётся `fun width(width: StatefulValue)`, а `fun width(width: Dp)` становится default-методом `width(width.asStatefulValue())` — точно как `IndicatorDimensionsBuilder.height`. Аналогично в `AvatarStyleBuilder` для `shape`/`textStyle`. В `AvatarColorsBuilder` для `actionColor`/`actionScrimColor` — четыре перегрузки: `Color` → `asStatefulBrush()` (из `graphics/brush/BrushUtils.kt`), `InteractiveColor` → `asStatefulBrush()` (из `interactions/InteractiveColor.kt`), `Brush` → `asStatefulValue()`, `StatefulValue` — каноническая абстрактная. Существующие вызовы в сгенерированных стилях (`actionColor(Color)`, `width(88.0.dp)`) продолжают компилироваться без изменений. + +**Сигнатура `Modifier.action` в `Avatar.kt`** +Приватный `Modifier.action(painter, color: () -> Color, scrimColor: () -> Color, size)` меняет типы лямбд на `() -> Brush`: scrim рисуется через `drawRect(brush = ...)`, tint иконки — `ColorFilter.tint((color() as? SolidColor)?.value ?: Color.Unspecified)`. Это internal-функция, публичный API не затрагивается. + +**Резолв в компоненте `Avatar.kt`** +stateSet уже формируется из `AvatarStatus` (`remember(status) { setOf(status) }`) и используется для `textBrushes`/`backgroundBrushes`. Те же `interactionSource` + stateSet применяются к новым значениям: `shapes`, `textStyles`, `widthValues`/`heightValues`/`statusOffsetXValues`/`statusOffsetYValues`/`statusCutoutPaddingValues`, `actionBrushes`/`actionScrimBrushes`. В `Avatar` Composable (нужны до входа в `Box`) — через `getValueAsState(...).value`; внутри `Modifier.avatar(...)` (composed-область) — через недерёвянный `getValue(interactionSource, stateSet)` по образцу существующего `backgroundBrush`. + +**Defaults сохраняются 1:1** +`shapes = shapes ?: RoundedCornerShape(50).asStatefulValue()`, `textStyles = textStyles ?: TextStyle.Default.asStatefulValue()`, `actionBrushes = actionBrushes ?: Color.White.asStatefulBrush()`, `actionScrimBrushes = ... ?: AvatarDefaults.DefaultScrimColor.asStatefulBrush()`, все размеры `?: 0.dp.asStatefulValue()`, `statusCutoutPadding ?: 3.dp.asStatefulValue()`. + +## Risks / Trade-offs + +- [Двойной API surface] → Mitigation: deprecated-свойства — тонкие геттеры над stateful-значениями, логика одна; `@Deprecated(..., ReplaceWith(...))` направляет на новый API. +- [Non-solid brush в `actionColor`] → из `LinearGradient` нельзя извлечь `Color`; fallback — прежний дефолт (`Color.White`/`DefaultScrimColor`). Acceptable: legacy-API по определению про одиночный `Color`; документировано в KDoc deprecated-свойства. +- [Перехват `shape` в `.clip()` вне recomposition-трека] → `getValue` в composed-области перекомпозируется при изменении состояния interaction source (тот же механизм, что у работающего `backgroundBrush`). +- [BCV-бейзлайны] → любое изменение сигнатур ломает `apiCheck`; обязательно `./gradlew :sdds-core:uikit-compose:apiDump` и коммит трёх `.api` файлов. +- [Сгенерированный код `tokens/*`] → не регенерируется сейчас; default-перегрузки `Dp`/`Color` сохраняют компилируемость. При будущей регенерации Theme Builder сам начнёт выбирать `StatefulValue`-перегрузки (поведение проверено существующими тестами `PropGroupTreeTest`). diff --git a/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/proposal.md b/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/proposal.md new file mode 100644 index 0000000000..ddfdbd9948 --- /dev/null +++ b/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/proposal.md @@ -0,0 +1,34 @@ +## Why + +`AvatarStyle` частично не поддерживает stateful-значения: форма (`shape`) и стиль текста (`textStyle`) хранятся как простые `Shape`/`TextStyle`, цвета иконки действия (`actionColor`, `actionScrimColor`) — как простые `Color`, а все размеры `AvatarDimensionValues` — как простые `Dp`. Это не позволяет задавать значения, зависящие от интерактивных состояний и статуса (`AvatarStatus`), в то время как другие компоненты уже переведены на `StatefulValue` (эталонные реализации — `IndicatorStyle`, `SwitchStyle`, уже мигрированная часть `AvatarColors`). + +`AvatarColors` уже частично мигрирован (`textBrushes`/`textColors`, `backgroundBrushes`/`backgroundColors`) — эта часть API повторно не меняется. + +## What Changes + +- **`AvatarStyle`** — добавлены `shapes: StatefulValue` и `textStyles: StatefulValue`; свойства `shape` и `textStyle` сохранены как `@Deprecated` compat API (вычисляются из новых через `getDefaultValue()`). +- **`AvatarStyleBuilder`** — методы `shape(Shape)` и `textStyle(TextStyle)` становятся default-перегрузками, делегирующими новым абстрактным `shape(StatefulValue)` / `textStyle(StatefulValue)`. +- **`AvatarColors`** — добавлены `actionBrushes: StatefulValue` и `actionScrimBrushes: StatefulValue`; свойства `actionColor`/`actionScrimColor` сохранены как `@Deprecated` compat API. Уже мигрированные `textBrushes`/`backgroundBrushes` и т.п. не изменяются. +- **`AvatarColorsBuilder`** — для `actionColor` и `actionScrimColor` добавлены перегрузки `Color`, `InteractiveColor`, `Brush`, `StatefulValue` с конвертацией через существующие расширения проекта (`asStatefulBrush()`, `asStatefulValue()`). +- **`AvatarDimensionValues`** — каждое dimension-значение получает `StatefulValue`-свойство с суффиксом `Values` (`widthValues`, `heightValues`, `statusOffsetXValues`, `statusOffsetYValues`, `statusCutoutPaddingValues`); существующие `Dp`-свойства сохранены как `@Deprecated` compat API. +- **`AvatarDimensionsBuilder`** — для каждого параметра добавлена пара перегрузок `fun x(Dp)` (default-реализация через `asStatefulValue()`) и `fun x(StatefulValue)` (абстрактная). +- **`DefaultAvatarStyle` / `DefaultAvatarColors` / `DefaultAvatarDimensionValues`** — хранят только stateful-значения; deprecated-свойства вычисляются из них (`getDefaultValue()`). Существующие значения по умолчанию не меняются. +- **`Avatar.kt`** — резолв `shapes`, `textStyles`, размеров и action-цветов перенесён на `getValue`/`getValueAsState` с учётом `interactionSource` и состояния `AvatarStatus`. + +## Capabilities + +### New Capabilities + +- `avatar-style`: контракт stateful-API стиля `Avatar` (интерфейсы `AvatarStyle`, `AvatarColors`, `AvatarDimensionValues`, их билдеры и compat-поведение deprecated-API). + +### Modified Capabilities + +*(нет — `avatar-style` ранее не был описан в `openspec/specs`)* + +## Impact + +- `sdds-core/uikit-compose` — изменены публичные интерфейсы и реализации в `AvatarStyle.kt` (единственный файл со всем семейством стилей Avatar); обновлён потребитель `Avatar.kt`. +- Публичный API: **не breaking** — все существующие вызовы (`shape(Shape)`, `width(Dp)`, `actionColor(Color)`, чтение `style.shape`, `dimensionValues.width` и т.д.) продолжают компилироваться и работать; изменённые свойства помечены `@Deprecated` с `ReplaceWith`. Нужно обновить бейзлайны binary-compatibility-validator: `api/uikit-compose.api`, `api/android/uikit-compose.api`, `api/jvm/uikit-compose.api` задачей `apiDump`. +- `tokens/*` и `integration-core/*` — сгенерированный Theme Builder код (например, `tokens/plasma.giga.compose/.../avatar/AvatarStyles.kt`, `integration-core/sandbox-compose/.../avatar/AvatarStyles.kt`) вызывает `Dp`/`Color`/`StatefulValue`-перегрузки билдеров — они сохраняются, регенерация не требуется. Код плагина `plugin_theme_builder` не меняется (маппинг перегрузок рефлексивный). +- Docs generation: KDoc новых свойств подхватывается docs-ksp автоматически; скриншоты документации Avatar генерируются из существующих samples без изменений. +- Валидация: компиляция `:sdds-core:uikit-compose`, `apiCheck`, detekt/spotless, компиляция затронутых `tokens/*`/`integration-core/*`, скриншотные тесты Avatar (`ComposeAvatarScreenshotTest`). diff --git a/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/specs/avatar-style/spec.md b/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/specs/avatar-style/spec.md new file mode 100644 index 0000000000..15f592b785 --- /dev/null +++ b/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/specs/avatar-style/spec.md @@ -0,0 +1,76 @@ +## ADDED Requirements + +### Requirement: AvatarStyle SHALL expose stateful shape and textStyle +`sdds-core/uikit-compose` SHALL содержать в `interface AvatarStyle` свойства `shapes: StatefulValue` и `textStyles: StatefulValue`. Существующие свойства `shape: Shape` и `textStyle: TextStyle` SHALL сохраняться как compatibility API, помеченные `@Deprecated` с `ReplaceWith`, указывающим на соответствующее новое свойство, и SHALL вычисляться из новых значений через `getDefaultValue()`. + +#### Scenario: Дефолтный стиль через builder +- **WHEN** вызывается `AvatarStyle.builder().style()` без явных `shape`/`textStyle` +- **THEN** `shapes.getDefaultValue()` MUST равняться `RoundedCornerShape(50)` +- **AND** `textStyles.getDefaultValue()` MUST равняться `TextStyle.Default` + +#### Scenario: Deprecated-свойства остаются рабочими +- **WHEN** из полученного `AvatarStyle` читаются `shape` и `textStyle` +- **THEN** значения MUST совпадать с `shapes.getDefaultValue()` и `textStyles.getDefaultValue()` соответственно + +#### Scenario: StatefulValue через builder +- **WHEN** вызывается `shape(statefulValue)` с разными формами для состояний +- **THEN** `Avatar.kt` MUST резолвить форму через `interactionSource` и stateSet статуса и применять соответствующую форму при изменении состояния + +### Requirement: AvatarStyleBuilder SHALL provide Shape/TextStyle and StatefulValue overloads +`interface AvatarStyleBuilder` SHALL предоставлять абстрактные методы `shape(StatefulValue)` и `textStyle(StatefulValue)`, а также default-перегрузки `shape(Shape)` и `textStyle(TextStyle)`, делегирующие каноническим через `asStatefulValue()`. + +#### Scenario: Обратная совместимость shape(Shape) +- **WHEN** существующий код вызывает `builder.shape(RoundedCornerShape(8.dp))` +- **THEN** код MUST компилироваться без изменений +- **AND** `style().shapes.getDefaultValue()` MUST равняться `RoundedCornerShape(8.dp)` + +### Requirement: AvatarColors SHALL store action colors as StatefulValue +`interface AvatarColors` SHALL содержать `actionBrushes: StatefulValue` и `actionScrimBrushes: StatefulValue`. Свойства `actionColor: Color` и `actionScrimColor: Color` SHALL сохраняться как `@Deprecated` compatibility API (с `ReplaceWith`) и SHALL вычисляться как `SolidColor`-компонента дефолтного значения кисти с fallback на прежние значения по умолчанию (`Color.White` и `AvatarDefaults.DefaultScrimColor`). Уже stateful-свойства (`textBrushes`, `textColors`, `backgroundBrushes`, `backgroundColors`) SHALL оставаться без изменений. + +#### Scenario: Значения по умолчанию сохранены +- **WHEN** вызывается `AvatarColors.builder().build()` без параметров +- **THEN** `actionBrushes.getDefaultValue()` MUST быть эквивалентна `SolidColor(Color.White)` +- **AND** `actionScrimBrushes.getDefaultValue()` MUST быть эквивалентна `SolidColor(AvatarDefaults.DefaultScrimColor)` +- **AND** deprecated-свойства `actionColor`/`actionScrimColor` MUST возвращать те же цвета, что и до миграции + +#### Scenario: Обратная совместимость actionColor(Color) +- **WHEN** существующий код вызывает `colorsBuilder.actionColor(Color.Red)` +- **THEN** код MUST компилироваться без изменений +- **AND** `build().actionBrushes.getDefaultValue()` MUST быть эквивалентна `SolidColor(Color.Red)` + +### Requirement: AvatarColorsBuilder SHALL provide Color, InteractiveColor, Brush and StatefulValue overloads +`interface AvatarColorsBuilder` SHALL предоставлять для `actionColor` и `actionScrimColor` по четыре перегрузки: `Color`, `InteractiveColor`, `Brush`, `StatefulValue`. Перегрузки `Color` и `InteractiveColor` MUST конвертироваться через существующие расширения `asStatefulBrush()`, перегрузка `Brush` — через `asStatefulValue()`, и все MUST делегировать к `StatefulValue`-перегрузке. + +#### Scenario: InteractiveColor конвертируется в StatefulValue +- **WHEN** вызывается `actionColor(interactiveColor)` где `interactiveColor: InteractiveColor` +- **THEN** результирующий `actionBrushes` MUST равняться `interactiveColor.asStatefulBrush()` +- **AND** `actionBrushes.isStateful()` MUST возвращать `true` для интерактивного цвета с состояниями + +### Requirement: AvatarDimensionValues SHALL expose StatefulValue per dimension +`interface AvatarDimensionValues` SHALL содержать `statusOffsetXValues`, `statusOffsetYValues`, `widthValues`, `heightValues`, `statusCutoutPaddingValues` типа `StatefulValue`. Существующие `Dp`-свойства SHALL сохраняться как `@Deprecated` compatibility API (с `ReplaceWith` на соответствующее `*Values`) и SHALL вычисляться через `getDefaultValue()`. Реализация `DefaultAvatarDimensionValues` SHALL хранить в constructor только stateful-значения. Значения по умолчанию SHALL сохраниться: `0.dp` для всех, кроме `statusCutoutPadding = 3.dp`. + +#### Scenario: Дефолты builder не изменились +- **WHEN** вызывается `AvatarDimensionsBuilder.builder().build()` без параметров +- **THEN** `widthValues.getDefaultValue()` и `heightValues.getDefaultValue()` MUST равняться `0.dp` +- **AND** `statusCutoutPaddingValues.getDefaultValue()` MUST равняться `3.dp` +- **AND** deprecated-свойство `width` MUST равняться `widthValues.getDefaultValue()` + +#### Scenario: Обратная совместимость width(Dp) +- **WHEN** существующий код (в т.ч. сгенерированный Theme Builder) вызывает `dimensions { width(88.0.dp) }` +- **THEN** код MUST компилироваться без изменений +- **AND** `build().widthValues.getDefaultValue()` MUST равняться `88.0.dp` + +#### Scenario: Разные размеры по состояниям +- **WHEN** задан `widthValues` с разными `Dp` для состояний из `AvatarStatus`/`InteractiveState` +- **THEN** компонент `Avatar` MUST резолвить актуальный размер через `interactionSource` и stateSet и применять его в `requiredSize`/размере фона + +### Requirement: Existing Avatar API SHALL remain source-compatible +Миграция SHALL сохранять все существующие public API `AvatarStyle`, `AvatarColors`, `AvatarDimensions*` без удаления. Компоненты-потребители, передающие `AvatarStyle` (`AvatarGroupStyle`, `Cell`, `ListItem`, `NavigationDrawerItem`), и сгенерированный код `tokens/*`/`integration-core/sandbox-compose` MUST компилироваться без правок. + +#### Scenario: Сгенерированные стили токенов компилируются +- **WHEN** собирается модуль `tokens/*`, содержащий сгенерированный `AvatarStyles.kt` (вызовы `dimensions { statusOffsetX(1.0.dp); width(88.0.dp) }`, `colors { textColor(...); backgroundColor(...) }`) +- **THEN** компиляция MUST проходить без изменений в сгенерированном коде + +#### Scenario: Скриншотное поведение не изменилось +- **WHEN** выполняются существующие скриншотные тесты Avatar (`ComposeAvatarScreenshotTest`) с дефолтными стилями +- **THEN** отрендеренные артефакты MUST остаться идентичными предыдущей версии diff --git a/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/tasks.md b/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/tasks.md new file mode 100644 index 0000000000..441019714d --- /dev/null +++ b/openspec/changes/archive/2026-10-15-avatar-style-stateful-value/tasks.md @@ -0,0 +1,40 @@ +## 1. sdds-core/uikit-compose — AvatarStyle.kt: AvatarStyle и AvatarStyleBuilder + +- [x] 1.1 В интерфейсе `AvatarStyle` добавить `val shapes: StatefulValue` и `val textStyles: StatefulValue` с KDoc; существующие `shape`/`textStyle` пометить `@Deprecated("Use shapes", ReplaceWith("shapes"))` / `@Deprecated("Use textStyles", ReplaceWith("textStyles"))` +- [x] 1.2 В `AvatarStyleBuilder` добавить абстрактные `fun shape(shape: StatefulValue): AvatarStyleBuilder` и `fun textStyle(textStyle: StatefulValue): AvatarStyleBuilder`; существующие `shape(Shape)`/`textStyle(TextStyle)` сделать default-реализациями с делегированием через `shape.asStatefulValue()` / `textStyle.asStatefulValue()` +- [x] 1.3 В `DefaultAvatarStyle` заменить constructor-поля `shape: Shape`/`textStyle: TextStyle` на `shapes: StatefulValue`/`textStyles: StatefulValue`; deprecated-свойства реализовать как `get() = shapes.getDefaultValue()` / `get() = textStyles.getDefaultValue()` (с `@Suppress("OVERRIDE_DEPRECATION")` по месту, как для `dimensions`) +- [x] 1.4 В `DefaultAvatarStyle.Builder` заменить типы полей на `StatefulValue?`/`StatefulValue?`, обновить override-методы и дефолты в `style()`: `shapes ?: RoundedCornerShape(50).asStatefulValue()`, `textStyles ?: TextStyle.Default.asStatefulValue()` + +## 2. sdds-core/uikit-compose — AvatarStyle.kt: AvatarColors + +- [x] 2.1 В интерфейсе `AvatarColors` добавить `val actionBrushes: StatefulValue` и `val actionScrimBrushes: StatefulValue` с KDoc; `actionColor`/`actionScrimColor` пометить `@Deprecated("Use actionBrushes", ReplaceWith("actionBrushes"))` / `@Deprecated("Use actionScrimBrushes", ReplaceWith("actionScrimBrushes"))`. Уже мигрированные `textBrushes`/`textColors`/`backgroundBrushes`/`backgroundColors` не изменять +- [x] 2.2 В `AvatarColorsBuilder` для `actionColor` добавить default-перегрузки `Color` → `actionColor(color.asStatefulBrush())`, `InteractiveColor` → `actionColor(color.asStatefulBrush())`, `Brush` → `actionColor(brush.asStatefulValue())` и абстрактную `actionColor(actionColor: StatefulValue)`; то же для `actionScrimColor`. Проверить импорты: `graphics.brush.asStatefulBrush` (Color) и `interactions.asStatefulBrush` (InteractiveColor) +- [x] 2.3 В `DefaultAvatarColors` заменить constructor-поля `actionColor`/`actionScrimColor: Color` на `actionBrushes`/`actionScrimBrushes: StatefulValue`; deprecated-свойства вычислять как `(actionBrushes.getDefaultValue() as? SolidColor)?.value ?: Color.White` и `(actionScrimBrushes.getDefaultValue() as? SolidColor)?.value ?: AvatarDefaults.DefaultScrimColor` +- [x] 2.4 В `DefaultAvatarColors.Builder` заменить поля на `StatefulValue?`, обновить override-методы и дефолты в `build()`: `actionBrushes ?: Color.White.asStatefulBrush()`, `actionScrimBrushes ?: AvatarDefaults.DefaultScrimColor.asStatefulBrush()` + +## 3. sdds-core/uikit-compose — AvatarStyle.kt: AvatarDimensionValues + +- [x] 3.1 В `AvatarDimensionValues` добавить `statusOffsetXValues`, `statusOffsetYValues`, `widthValues`, `heightValues`, `statusCutoutPaddingValues`: `StatefulValue` с KDoc; существующие `Dp`-свойства пометить `@Deprecated("Use Values", ReplaceWith("Values"))` +- [x] 3.2 В `AvatarDimensionsBuilder` для каждого из пяти параметров добавить абстрактную перегрузку `fun x(x: StatefulValue)` и default-перегрузку `fun x(x: Dp) = x(x.asStatefulValue())` +- [x] 3.3 В `DefaultAvatarDimensionValues` оставить в constructor только `StatefulValue`-поля; deprecated-свойства реализовать как `get() = xValues.getDefaultValue()` +- [x] 3.4 В `DefaultAvatarDimensionValues.Builder` заменить поля на `StatefulValue` с дефолтами `0.dp.asStatefulValue()` (statusOffsetX/Y, width, height) и `3.dp.asStatefulValue()` (statusCutoutPadding); значения по умолчанию в `build()` сохранить без изменений + +## 4. sdds-core/uikit-compose — Avatar.kt: резолв stateful-значений + +- [x] 4.1 В Composable `Avatar`: сформировать `stateSet` из `status` до `Box` и резолвить `widthValues`/`heightValues` через `getValueAsState(interactionSource, stateSet)` для `requiredSize(...)`, `statusCutoutPaddingValues` — для параметра `statusCutoutPadding` модификатора `avatar` +- [x] 4.2 В `Modifier.avatar(interactionSource, ...)`: резолвить через `getValue(interactionSource, stateSet)` значения `statusOffsetXValues`/`statusOffsetYValues` (в `Modifier.indicator`), `widthValues`/`heightValues` (sizeModifier), `shapes` (`.clip(...)`), `textStyles` (placeholder), `actionBrushes`/`actionScrimBrushes` (actionModifier) +- [x] 4.3 Приватный `Modifier.action` перевести на `color: Brush`, `scrimColor: Brush`: scrim через `drawRect(brush = ...)`, tint иконки через `ColorFilter.tint` из `SolidColor` (без tint для non-solid brush). Дополнительно: `AvatarGroup.kt` переведён с deprecated `avatarStyle.shape` на `avatarStyle.shapes.getDefaultValue()` + +## 5. Публичный API (бейзлайны) + +- [x] 5.1 Обновить бейзлайны binary-compatibility-validator (`api/uikit-compose.api`, `api/android/uikit-compose.api`, `api/jvm/uikit-compose.api`) — добавлены новые members, удалённых нет. ПРИМЕЧАНИЕ: `apiDump`/`apiCheck` не запускаются локально (SSL-блокировка загрузки `kotlin-metadata-jvm:2.1.10`), бейзлайны обновлены вручную по формату BCV; перепроверить `apiCheck` в CI + +## 6. Валидация + +- [x] 6.1 Проверить все usages `AvatarStyle`/`AvatarColors`/`AvatarDimensionValues` (`Avatar.kt`, `AvatarGroup.kt`, `AvatarGroupStyle.kt`, `CellStyle.kt`, `ListItemStyle.kt`, `NavigationDrawerItem.kt`) — совместимость сохранена; `AvatarGroup.kt` переведён на новый API, остальные только передают стиль +- [x] 6.2 `./gradlew :sdds-core:uikit-compose:compileDebugKotlin` — нет ошибок компиляции +- [ ] 6.3 `./gradlew :sdds-core:uikit-compose:apiCheck` — ЗАБЛОКИРОВАНО: задача не может resolвить `org.jetbrains.kotlin:kotlin-metadata-jvm:2.1.10` (SSL handshake к dl.google.com/repo.maven.apache.org). Выполнить в CI +- [x] 6.4 `./gradlew :sdds-core:uikit-compose:detekt` и `./gradlew :sdds-core:uikit-compose:spotlessApply` — нет новых нарушений, форматирование применено +- [x] 6.5 `./gradlew :integration-core:sandbox-compose:compileDebugKotlin`, `./gradlew :tokens:plasma.giga.compose:compileDebugKotlin`, `./gradlew :tokens:sdds-sbcom-compose:compileDebugKotlin` — сгенерированный код компилируется без изменений (default/Dp-перегрузки работают) +- [x] 6.6 `./gradlew :tokens:plasma.giga.compose:testDebugUnitTest --tests "*ComposeAvatarScreenshotTest*"` — 22 теста, 0 failures; goldens в `screenshots-compose/` не изменились (дефолты сохранены) +- [x] 6.7 Deprecated-API компилируется (только warnings); KDoc новых свойств/builder-перегрузок добавлен diff --git a/sdds-core/uikit-compose/api/android/uikit-compose.api b/sdds-core/uikit-compose/api/android/uikit-compose.api index e4fbaee3be..13b60b9b43 100644 --- a/sdds-core/uikit-compose/api/android/uikit-compose.api +++ b/sdds-core/uikit-compose/api/android/uikit-compose.api @@ -235,7 +235,9 @@ public final class com/sdds/compose/uikit/AutocompleteStyleKt { public abstract interface class com/sdds/compose/uikit/AvatarColors { public static final field Companion Lcom/sdds/compose/uikit/AvatarColors$Companion; + public abstract fun getActionBrushes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getActionColor-0d7_KjU ()J + public abstract fun getActionScrimBrushes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getActionScrimColor-0d7_KjU ()J public abstract fun getBackgroundAlpha ()F public abstract fun getBackgroundBrushes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; @@ -257,7 +259,13 @@ public final class com/sdds/compose/uikit/AvatarColors$DefaultImpls { } public abstract interface class com/sdds/compose/uikit/AvatarColorsBuilder { + public abstract fun actionColor (Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionColor (Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionColor (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun actionColor-8_81llA (J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionScrimColor (Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionScrimColor (Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionScrimColor (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun actionScrimColor-8_81llA (J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun activeStatusColor-8_81llA (J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun backgroundAlpha (F)Lcom/sdds/compose/uikit/AvatarColorsBuilder; @@ -275,6 +283,12 @@ public abstract interface class com/sdds/compose/uikit/AvatarColorsBuilder { } public final class com/sdds/compose/uikit/AvatarColorsBuilder$DefaultImpls { + public static fun actionColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionColor-8_81llA (Lcom/sdds/compose/uikit/AvatarColorsBuilder;J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionScrimColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionScrimColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionScrimColor-8_81llA (Lcom/sdds/compose/uikit/AvatarColorsBuilder;J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public static fun backgroundAlpha (Lcom/sdds/compose/uikit/AvatarColorsBuilder;F)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public static fun backgroundColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public static fun backgroundColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; @@ -286,10 +300,15 @@ public final class com/sdds/compose/uikit/AvatarColorsBuilder$DefaultImpls { public abstract interface class com/sdds/compose/uikit/AvatarDimensionValues { public abstract fun getHeight-D9Ej5fM ()F + public abstract fun getHeightValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusCutoutPadding-D9Ej5fM ()F + public abstract fun getStatusCutoutPaddingValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusOffsetX-D9Ej5fM ()F + public abstract fun getStatusOffsetXValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusOffsetY-D9Ej5fM ()F + public abstract fun getStatusOffsetYValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getWidth-D9Ej5fM ()F + public abstract fun getWidthValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; } public final class com/sdds/compose/uikit/AvatarDimensions { @@ -314,10 +333,15 @@ public final class com/sdds/compose/uikit/AvatarDimensions { public abstract interface class com/sdds/compose/uikit/AvatarDimensionsBuilder { public static final field Companion Lcom/sdds/compose/uikit/AvatarDimensionsBuilder$Companion; public abstract fun build ()Lcom/sdds/compose/uikit/AvatarDimensionValues; + public abstract fun height (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun height-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun statusCutoutPadding (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun statusCutoutPadding-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun statusOffsetX (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun statusOffsetX-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun statusOffsetY (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun statusOffsetY-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun width (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun width-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; } @@ -325,6 +349,14 @@ public final class com/sdds/compose/uikit/AvatarDimensionsBuilder$Companion { public final fun builder ()Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; } +public final class com/sdds/compose/uikit/AvatarDimensionsBuilder$DefaultImpls { + public static fun height-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun statusCutoutPadding-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun statusOffsetX-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun statusOffsetY-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun width-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; +} + public final class com/sdds/compose/uikit/AvatarGroupDimensions { public static final field $stable I public synthetic fun (FFILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -441,9 +473,11 @@ public abstract interface class com/sdds/compose/uikit/AvatarStyle : com/sdds/co public abstract fun getDimensions ()Lcom/sdds/compose/uikit/AvatarDimensions; public abstract fun getIconBadgeStyle ()Lcom/sdds/compose/uikit/BadgeStyle; public abstract fun getShape ()Landroidx/compose/ui/graphics/Shape; + public abstract fun getShapes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusCutoutEnabled ()Z public abstract fun getStatusStyle ()Lcom/sdds/compose/uikit/IndicatorStyle; public abstract fun getTextStyle ()Landroidx/compose/ui/text/TextStyle; + public abstract fun getTextStyles ()Lcom/sdds/compose/uikit/interactions/StatefulValue; } public final class com/sdds/compose/uikit/AvatarStyle$Companion { @@ -459,13 +493,17 @@ public abstract interface class com/sdds/compose/uikit/AvatarStyleBuilder : com/ public abstract fun dimensions (Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun iconBadgeStyle (Lcom/sdds/compose/uikit/BadgeStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun shape (Landroidx/compose/ui/graphics/Shape;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; + public abstract fun shape (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun statusCutoutEnabled (Z)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun statusStyle (Lcom/sdds/compose/uikit/IndicatorStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun textStyle (Landroidx/compose/ui/text/TextStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; + public abstract fun textStyle (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; } public final class com/sdds/compose/uikit/AvatarStyleBuilder$DefaultImpls { public static fun getReceiver (Lcom/sdds/compose/uikit/AvatarStyleBuilder;)Ljava/lang/Object; + public static fun shape (Lcom/sdds/compose/uikit/AvatarStyleBuilder;Landroidx/compose/ui/graphics/Shape;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; + public static fun textStyle (Lcom/sdds/compose/uikit/AvatarStyleBuilder;Landroidx/compose/ui/text/TextStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; } public final class com/sdds/compose/uikit/AvatarStyleKt { diff --git a/sdds-core/uikit-compose/api/jvm/uikit-compose.api b/sdds-core/uikit-compose/api/jvm/uikit-compose.api index 61f4778494..6f8c4c9a4c 100644 --- a/sdds-core/uikit-compose/api/jvm/uikit-compose.api +++ b/sdds-core/uikit-compose/api/jvm/uikit-compose.api @@ -235,7 +235,9 @@ public final class com/sdds/compose/uikit/AutocompleteStyleKt { public abstract interface class com/sdds/compose/uikit/AvatarColors { public static final field Companion Lcom/sdds/compose/uikit/AvatarColors$Companion; + public abstract fun getActionBrushes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getActionColor-0d7_KjU ()J + public abstract fun getActionScrimBrushes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getActionScrimColor-0d7_KjU ()J public abstract fun getBackgroundAlpha ()F public abstract fun getBackgroundBrushes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; @@ -257,7 +259,13 @@ public final class com/sdds/compose/uikit/AvatarColors$DefaultImpls { } public abstract interface class com/sdds/compose/uikit/AvatarColorsBuilder { + public abstract fun actionColor (Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionColor (Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionColor (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun actionColor-8_81llA (J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionScrimColor (Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionScrimColor (Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionScrimColor (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun actionScrimColor-8_81llA (J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun activeStatusColor-8_81llA (J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun backgroundAlpha (F)Lcom/sdds/compose/uikit/AvatarColorsBuilder; @@ -275,6 +283,12 @@ public abstract interface class com/sdds/compose/uikit/AvatarColorsBuilder { } public final class com/sdds/compose/uikit/AvatarColorsBuilder$DefaultImpls { + public static fun actionColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionColor-8_81llA (Lcom/sdds/compose/uikit/AvatarColorsBuilder;J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionScrimColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionScrimColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionScrimColor-8_81llA (Lcom/sdds/compose/uikit/AvatarColorsBuilder;J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public static fun backgroundAlpha (Lcom/sdds/compose/uikit/AvatarColorsBuilder;F)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public static fun backgroundColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public static fun backgroundColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; @@ -286,10 +300,15 @@ public final class com/sdds/compose/uikit/AvatarColorsBuilder$DefaultImpls { public abstract interface class com/sdds/compose/uikit/AvatarDimensionValues { public abstract fun getHeight-D9Ej5fM ()F + public abstract fun getHeightValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusCutoutPadding-D9Ej5fM ()F + public abstract fun getStatusCutoutPaddingValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusOffsetX-D9Ej5fM ()F + public abstract fun getStatusOffsetXValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusOffsetY-D9Ej5fM ()F + public abstract fun getStatusOffsetYValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getWidth-D9Ej5fM ()F + public abstract fun getWidthValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; } public final class com/sdds/compose/uikit/AvatarDimensions { @@ -314,10 +333,15 @@ public final class com/sdds/compose/uikit/AvatarDimensions { public abstract interface class com/sdds/compose/uikit/AvatarDimensionsBuilder { public static final field Companion Lcom/sdds/compose/uikit/AvatarDimensionsBuilder$Companion; public abstract fun build ()Lcom/sdds/compose/uikit/AvatarDimensionValues; + public abstract fun height (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun height-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun statusCutoutPadding (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun statusCutoutPadding-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun statusOffsetX (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun statusOffsetX-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun statusOffsetY (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun statusOffsetY-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun width (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun width-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; } @@ -325,6 +349,14 @@ public final class com/sdds/compose/uikit/AvatarDimensionsBuilder$Companion { public final fun builder ()Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; } +public final class com/sdds/compose/uikit/AvatarDimensionsBuilder$DefaultImpls { + public static fun height-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun statusCutoutPadding-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun statusOffsetX-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun statusOffsetY-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun width-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; +} + public final class com/sdds/compose/uikit/AvatarGroupDimensions { public static final field $stable I public synthetic fun (FFILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -441,9 +473,11 @@ public abstract interface class com/sdds/compose/uikit/AvatarStyle : com/sdds/co public abstract fun getDimensions ()Lcom/sdds/compose/uikit/AvatarDimensions; public abstract fun getIconBadgeStyle ()Lcom/sdds/compose/uikit/BadgeStyle; public abstract fun getShape ()Landroidx/compose/ui/graphics/Shape; + public abstract fun getShapes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusCutoutEnabled ()Z public abstract fun getStatusStyle ()Lcom/sdds/compose/uikit/IndicatorStyle; public abstract fun getTextStyle ()Landroidx/compose/ui/text/TextStyle; + public abstract fun getTextStyles ()Lcom/sdds/compose/uikit/interactions/StatefulValue; } public final class com/sdds/compose/uikit/AvatarStyle$Companion { @@ -459,13 +493,17 @@ public abstract interface class com/sdds/compose/uikit/AvatarStyleBuilder : com/ public abstract fun dimensions (Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun iconBadgeStyle (Lcom/sdds/compose/uikit/BadgeStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun shape (Landroidx/compose/ui/graphics/Shape;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; + public abstract fun shape (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun statusCutoutEnabled (Z)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun statusStyle (Lcom/sdds/compose/uikit/IndicatorStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun textStyle (Landroidx/compose/ui/text/TextStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; + public abstract fun textStyle (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; } public final class com/sdds/compose/uikit/AvatarStyleBuilder$DefaultImpls { public static fun getReceiver (Lcom/sdds/compose/uikit/AvatarStyleBuilder;)Ljava/lang/Object; + public static fun shape (Lcom/sdds/compose/uikit/AvatarStyleBuilder;Landroidx/compose/ui/graphics/Shape;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; + public static fun textStyle (Lcom/sdds/compose/uikit/AvatarStyleBuilder;Landroidx/compose/ui/text/TextStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; } public final class com/sdds/compose/uikit/AvatarStyleKt { diff --git a/sdds-core/uikit-compose/api/uikit-compose.api b/sdds-core/uikit-compose/api/uikit-compose.api index be741741cd..089a444a26 100644 --- a/sdds-core/uikit-compose/api/uikit-compose.api +++ b/sdds-core/uikit-compose/api/uikit-compose.api @@ -203,7 +203,9 @@ public final class com/sdds/compose/uikit/AutocompleteStyleKt { public abstract interface class com/sdds/compose/uikit/AvatarColors { public static final field Companion Lcom/sdds/compose/uikit/AvatarColors$Companion; + public abstract fun getActionBrushes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getActionColor-0d7_KjU ()J + public abstract fun getActionScrimBrushes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getActionScrimColor-0d7_KjU ()J public abstract fun getBackgroundAlpha ()F public abstract fun getBackgroundBrushes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; @@ -225,7 +227,13 @@ public final class com/sdds/compose/uikit/AvatarColors$DefaultImpls { } public abstract interface class com/sdds/compose/uikit/AvatarColorsBuilder { + public abstract fun actionColor (Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionColor (Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionColor (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun actionColor-8_81llA (J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionScrimColor (Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionScrimColor (Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public abstract fun actionScrimColor (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun actionScrimColor-8_81llA (J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun activeStatusColor-8_81llA (J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public abstract fun backgroundAlpha (F)Lcom/sdds/compose/uikit/AvatarColorsBuilder; @@ -243,6 +251,12 @@ public abstract interface class com/sdds/compose/uikit/AvatarColorsBuilder { } public final class com/sdds/compose/uikit/AvatarColorsBuilder$DefaultImpls { + public static fun actionColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionColor-8_81llA (Lcom/sdds/compose/uikit/AvatarColorsBuilder;J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionScrimColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionScrimColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; + public static fun actionScrimColor-8_81llA (Lcom/sdds/compose/uikit/AvatarColorsBuilder;J)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public static fun backgroundAlpha (Lcom/sdds/compose/uikit/AvatarColorsBuilder;F)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public static fun backgroundColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Landroidx/compose/ui/graphics/Brush;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; public static fun backgroundColor (Lcom/sdds/compose/uikit/AvatarColorsBuilder;Lcom/sdds/compose/uikit/interactions/InteractiveColor;)Lcom/sdds/compose/uikit/AvatarColorsBuilder; @@ -254,10 +268,15 @@ public final class com/sdds/compose/uikit/AvatarColorsBuilder$DefaultImpls { public abstract interface class com/sdds/compose/uikit/AvatarDimensionValues { public abstract fun getHeight-D9Ej5fM ()F + public abstract fun getHeightValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusCutoutPadding-D9Ej5fM ()F + public abstract fun getStatusCutoutPaddingValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusOffsetX-D9Ej5fM ()F + public abstract fun getStatusOffsetXValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusOffsetY-D9Ej5fM ()F + public abstract fun getStatusOffsetYValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getWidth-D9Ej5fM ()F + public abstract fun getWidthValues ()Lcom/sdds/compose/uikit/interactions/StatefulValue; } public final class com/sdds/compose/uikit/AvatarDimensions { @@ -282,10 +301,15 @@ public final class com/sdds/compose/uikit/AvatarDimensions { public abstract interface class com/sdds/compose/uikit/AvatarDimensionsBuilder { public static final field Companion Lcom/sdds/compose/uikit/AvatarDimensionsBuilder$Companion; public abstract fun build ()Lcom/sdds/compose/uikit/AvatarDimensionValues; + public abstract fun height (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun height-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun statusCutoutPadding (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun statusCutoutPadding-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun statusOffsetX (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun statusOffsetX-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun statusOffsetY (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun statusOffsetY-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public abstract fun width (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; public abstract fun width-0680j_4 (F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; } @@ -293,6 +317,14 @@ public final class com/sdds/compose/uikit/AvatarDimensionsBuilder$Companion { public final fun builder ()Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; } +public final class com/sdds/compose/uikit/AvatarDimensionsBuilder$DefaultImpls { + public static fun height-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun statusCutoutPadding-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun statusOffsetX-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun statusOffsetY-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; + public static fun width-0680j_4 (Lcom/sdds/compose/uikit/AvatarDimensionsBuilder;F)Lcom/sdds/compose/uikit/AvatarDimensionsBuilder; +} + public final class com/sdds/compose/uikit/AvatarGroupDimensions { public static final field $stable I public synthetic fun (FFILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -409,9 +441,11 @@ public abstract interface class com/sdds/compose/uikit/AvatarStyle : com/sdds/co public abstract fun getDimensions ()Lcom/sdds/compose/uikit/AvatarDimensions; public abstract fun getIconBadgeStyle ()Lcom/sdds/compose/uikit/BadgeStyle; public abstract fun getShape ()Landroidx/compose/ui/graphics/Shape; + public abstract fun getShapes ()Lcom/sdds/compose/uikit/interactions/StatefulValue; public abstract fun getStatusCutoutEnabled ()Z public abstract fun getStatusStyle ()Lcom/sdds/compose/uikit/IndicatorStyle; public abstract fun getTextStyle ()Landroidx/compose/ui/text/TextStyle; + public abstract fun getTextStyles ()Lcom/sdds/compose/uikit/interactions/StatefulValue; } public final class com/sdds/compose/uikit/AvatarStyle$Companion { @@ -427,13 +461,17 @@ public abstract interface class com/sdds/compose/uikit/AvatarStyleBuilder : com/ public abstract fun dimensions (Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun iconBadgeStyle (Lcom/sdds/compose/uikit/BadgeStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun shape (Landroidx/compose/ui/graphics/Shape;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; + public abstract fun shape (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun statusCutoutEnabled (Z)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun statusStyle (Lcom/sdds/compose/uikit/IndicatorStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; public abstract fun textStyle (Landroidx/compose/ui/text/TextStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; + public abstract fun textStyle (Lcom/sdds/compose/uikit/interactions/StatefulValue;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; } public final class com/sdds/compose/uikit/AvatarStyleBuilder$DefaultImpls { public static fun getReceiver (Lcom/sdds/compose/uikit/AvatarStyleBuilder;)Ljava/lang/Object; + public static fun shape (Lcom/sdds/compose/uikit/AvatarStyleBuilder;Landroidx/compose/ui/graphics/Shape;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; + public static fun textStyle (Lcom/sdds/compose/uikit/AvatarStyleBuilder;Landroidx/compose/ui/text/TextStyle;)Lcom/sdds/compose/uikit/AvatarStyleBuilder; } public final class com/sdds/compose/uikit/AvatarStyleKt { diff --git a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/Avatar.kt b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/Avatar.kt index 97067244e1..ad403172e5 100644 --- a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/Avatar.kt +++ b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/Avatar.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.NonRestartableComposable import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -19,13 +20,15 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Rect import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.center +import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.Paint import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.Painter @@ -42,14 +45,23 @@ import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.sdds.api.info.compose.ApiStateSet +import com.sdds.compose.uikit.graphics.brush.BrushProducer import com.sdds.compose.uikit.graphics.cutout.ProvideCutoutState import com.sdds.compose.uikit.graphics.cutout.cutoutTarget import com.sdds.compose.uikit.graphics.cutout.rememberCutoutState +import com.sdds.compose.uikit.interactions.MutableSemanticStateSource import com.sdds.compose.uikit.interactions.ValueState import com.sdds.compose.uikit.interactions.getValue import com.sdds.compose.uikit.interactions.getValueAsState +import com.sdds.compose.uikit.interactions.setExclusiveEnum import com.sdds.compose.uikit.internal.common.background +import com.sdds.compose.uikit.motion.Motion +import com.sdds.compose.uikit.motion.components.avatar.AvatarMotionStyle +import com.sdds.compose.uikit.motion.components.avatar.rememberAvatarMotion import com.sdds.compose.uikit.motion.components.indicator.rememberIndicatorMotion +import com.sdds.compose.uikit.motion.getBrushAsState +import com.sdds.compose.uikit.motion.getTextStyleAsState +import com.sdds.compose.uikit.motion.rememberMotionContext /** * Компонент [Avatar] предназначен для отрисовки пользовательских аватаров. @@ -65,6 +77,7 @@ import com.sdds.compose.uikit.motion.components.indicator.rememberIndicatorMotio * @param contentDescription описание контента для accessibility * @param contentScale тип масштабирования * @param filterQuality фильтр качества + * @param motion объект анимаций */ @Composable @NonRestartableComposable @@ -79,6 +92,11 @@ fun Avatar( contentDescription: String? = null, contentScale: ContentScale = ContentScale.None, filterQuality: FilterQuality = FilterQuality.Low, + motion: Motion = rememberAvatarMotion( + motionContext = rememberMotionContext( + semanticStateSource = rememberAvatarSemanticStateSource(status), + ), + ), ) { val bitmapPainter = remember(imageBitmap) { BitmapPainter(imageBitmap, filterQuality = filterQuality) } @@ -92,6 +110,7 @@ fun Avatar( placeholder = placeholder, contentDescription = contentDescription, contentScale = contentScale, + motion = motion, ) } @@ -108,6 +127,7 @@ fun Avatar( * @param placeholder текст-заглушка * @param contentDescription описание контента для accessibility * @param contentScale тип масштабирования + * @param motion объект анимаций */ @Composable @NonRestartableComposable @@ -121,6 +141,11 @@ fun Avatar( placeholder: AvatarPlaceholder? = null, contentDescription: String? = null, contentScale: ContentScale = ContentScale.None, + motion: Motion = rememberAvatarMotion( + motionContext = rememberMotionContext( + semanticStateSource = rememberAvatarSemanticStateSource(status), + ), + ), ) { Avatar( modifier = modifier, @@ -132,6 +157,7 @@ fun Avatar( placeholder = placeholder, contentDescription = contentDescription, contentScale = contentScale, + motion = motion, ) } @@ -148,6 +174,7 @@ fun Avatar( * @param placeholder текст-заглушка * @param contentDescription описание контента для accessibility * @param contentScale тип масштабирования + * @param motion объект анимаций */ @Composable fun Avatar( @@ -160,6 +187,11 @@ fun Avatar( placeholder: AvatarPlaceholder? = null, contentDescription: String? = null, contentScale: ContentScale = ContentScale.None, + motion: Motion = rememberAvatarMotion( + motionContext = rememberMotionContext( + semanticStateSource = rememberAvatarSemanticStateSource(status), + ), + ), ) { Avatar( modifier = modifier, @@ -168,16 +200,18 @@ fun Avatar( action = action, actionEnabled = actionEnabled, placeholder = placeholder, - ) { - if (painter != null) { - Image( - modifier = Modifier.matchParentSize(), - painter = painter, - contentDescription = contentDescription, - contentScale = contentScale, - ) - } - } + motion = motion, + content = { + if (painter != null) { + Image( + modifier = Modifier.matchParentSize(), + painter = painter, + contentDescription = contentDescription, + contentScale = contentScale, + ) + } + }, + ) } /** @@ -206,22 +240,74 @@ fun Avatar( extra: (@Composable BoxScope.() -> Unit)? = null, interactionSource: InteractionSource = remember { MutableInteractionSource() }, content: @Composable BoxScope.() -> Unit, +) { + Avatar( + modifier = modifier, + style = style, + status = status, + action = action, + actionEnabled = actionEnabled, + placeholder = placeholder, + extra = extra, + motion = rememberAvatarMotion( + motionContext = rememberMotionContext( + semanticStateSource = rememberAvatarSemanticStateSource(status), + interactionSource = interactionSource as? MutableInteractionSource + ?: remember { MutableInteractionSource() }, + ), + ), + content = content, + ) +} + +/** + * Компонент [Avatar] предназначен для отрисовки пользовательских аватаров. + * Умеет отображать статус, текст-заглушку, а также иконку действия. + * + * @param modifier модификатор + * @param style стиль компонента + * @param status статус [AvatarStatus] + * @param action иконка действия [Painter] + * @param actionEnabled включена ли иконка действия + * @param placeholder текст-заглушка + * @param extra обвесы аватара + * @param motion объект анимаций. Взаимодействия берутся из [Motion.context] + * @param content контент аватара (изображеине) + */ +@Composable +fun Avatar( + motion: Motion, + content: @Composable BoxScope.() -> Unit, + modifier: Modifier = Modifier, + style: AvatarStyle = LocalAvatarStyle.current, + status: AvatarStatus = AvatarStatus.None, + action: Painter? = null, + actionEnabled: Boolean = LocalAvatarGroupActionEnabled.current, + placeholder: AvatarPlaceholder? = null, + extra: (@Composable BoxScope.() -> Unit)? = null, ) { val cutoutState = rememberCutoutState() - Box(modifier.requiredSize(style.dimensionValues.width, style.dimensionValues.height)) { + val context = motion.context + val dimensions = style.dimensionValues + val width by dimensions.widthValues.getValueAsState(context) + val height by dimensions.heightValues.getValueAsState(context) + Box(modifier.requiredSize(width, height)) { ProvideCutoutState(cutoutState) { Box( modifier = Modifier .cutoutTarget(enabled = style.statusCutoutEnabled) .avatar( - interactionSource = interactionSource, + interactionSource = context.interactionSource, style = style, status = status, action = action, actionEnabled = actionEnabled, placeholder = placeholder, statusCutoutEnabled = style.statusCutoutEnabled, - statusCutoutPadding = style.dimensionValues.statusCutoutPadding, + statusCutoutPadding = dimensions.statusCutoutPaddingValues + .getValueAsState(context) + .value, + motion = motion, ), contentAlignment = Alignment.Center, propagateMinConstraints = false, @@ -298,8 +384,9 @@ fun Modifier.avatar( * @param placeholder текст-заглушка * @param statusCutoutEnabled включен ли вырез под индикатор статуса * @param statusCutoutPadding внутренний отступ выреза индикатора статуса + * @param motion объект анимаций. Взаимодействия берутся из [Motion.context] */ -@Suppress("ComposableModifierFactory") +@Suppress("ComposableModifierFactory", "LongParameterList", "DEPRECATION") @Composable fun Modifier.avatar( interactionSource: InteractionSource, @@ -310,26 +397,32 @@ fun Modifier.avatar( placeholder: AvatarPlaceholder? = null, statusCutoutEnabled: Boolean = style.statusCutoutEnabled, statusCutoutPadding: Dp = 3.dp, + motion: Motion = rememberAvatarMotion( + motionContext = rememberMotionContext( + semanticStateSource = rememberAvatarSemanticStateSource(status), + interactionSource = interactionSource as? MutableInteractionSource + ?: remember { MutableInteractionSource() }, + ), + ), ): Modifier = composed { + val context = motion.context + val motionInteractionSource = context.interactionSource val dimensions = style.dimensionValues val colors = style.colors - val stateSet = remember(status) { setOf(status) } + val stateSet: Set = remember(status) { setOf(status) } + + SideEffect { + context.semanticStateSource.setExclusiveEnum(status) + } + val statusModifier = if (status.isEnabled && LocalAvatarGroupStatusEnabled.current) { - val indicatorMotion = rememberIndicatorMotion() - SideEffect { - indicatorMotion.context.semanticStateSource.setExclusive( - status, - AvatarStatus.Inactive, - AvatarStatus.Active, - AvatarStatus.None, - ) - } + val indicatorMotion = rememberIndicatorMotion(motionContext = context) Modifier.indicator( motion = indicatorMotion, alignment = Alignment.BottomEnd, style = style.statusStyle ?: LocalIndicatorStyle.current, - horizontalPadding = dimensions.statusOffsetX, - verticalPadding = dimensions.statusOffsetY, + horizontalPadding = dimensions.statusOffsetXValues.getValue(motionInteractionSource, stateSet), + verticalPadding = dimensions.statusOffsetYValues.getValue(motionInteractionSource, stateSet), stateSet = stateSet, cutoutEnabled = statusCutoutEnabled, cutoutPadding = statusCutoutPadding, @@ -339,12 +432,15 @@ fun Modifier.avatar( } val placeholderModifier = if (placeholder != null) { - val textBrushes = colors.textBrushes?.getValueAsState(interactionSource, stateSet) + val textBrushes = colors.textBrushes?.getBrushAsState(context, motion.style.textColor) val textColors = colors.textColors?.takeIf { textBrushes == null } - ?.colorForInteractionAsState(interactionSource, stateSet) + ?.colorForInteractionAsState(motionInteractionSource, stateSet) + val textStyle = style.textStyles + .getTextStyleAsState(context, motion.style.textStyle) + .value Modifier.placeholder( text = placeholder.minimize, - style = style.textStyle, + style = textStyle, textMeasurer = rememberTextMeasurer(), textBrushProvider = { textBrushes?.value }, textColorProvider = { textColors?.value }, @@ -355,10 +451,13 @@ fun Modifier.avatar( val actionModifier = if (action != null && actionEnabled && LocalAvatarGroupActionEnabled.current) { + val actionBrush = colors.actionBrushes.getBrushAsState(context, motion.style.actionColor) + val actionScrimBrush = colors.actionScrimBrushes + .getBrushAsState(context, motion.style.actionScrimColor) Modifier.action( painter = action, - color = { colors.actionColor }, - scrimColor = { colors.actionScrimColor }, + color = { actionBrush.value }, + scrimColor = { actionScrimBrush.value }, size = action.intrinsicSize, ) } else { @@ -366,15 +465,17 @@ fun Modifier.avatar( } val sizeModifier = Modifier - .width(dimensions.width) - .height(dimensions.height) + .width(dimensions.widthValues.getValue(motionInteractionSource, stateSet)) + .height(dimensions.heightValues.getValue(motionInteractionSource, stateSet)) - val backgroundBrush = style.colors.backgroundBrushes?.getValue(interactionSource, stateSet) - val backgroundColors = style.colors.backgroundColors?.takeIf { backgroundBrush == null } - ?.colorForInteraction(interactionSource, stateSet) + val backgroundBrush = colors.backgroundBrushes + ?.getBrushAsState(context, motion.style.backgroundColor) + ?.value + val backgroundColors = colors.backgroundColors?.takeIf { backgroundBrush == null } + ?.colorForInteraction(motionInteractionSource, stateSet) statusModifier - .clip(style.shape) + .clip(style.shapes.getValue(motionInteractionSource, stateSet)) .background(backgroundBrush, backgroundColors) .then(this) .then(sizeModifier) @@ -488,25 +589,44 @@ private fun Modifier.placeholder( private fun Modifier.action( painter: Painter, - color: () -> Color, - scrimColor: () -> Color, + color: BrushProducer, + scrimColor: BrushProducer, size: Size, ): Modifier = this .drawWithContent { drawContent() - drawRect(scrimColor()) + drawRect(brush = scrimColor()) val topLeft = this.size.center - size.center translate(topLeft.x, topLeft.y) { - with(painter) { - draw( + drawContext.canvas.saveLayer( + Rect(Offset.Zero, size), + Paint(), + ) + try { + with(painter) { + draw(size = size) + } + drawRect( + brush = color(), size = size, - colorFilter = ColorFilter.tint(color()), + blendMode = BlendMode.SrcIn, ) + } finally { + drawContext.canvas.restore() } } } +@Composable +private fun rememberAvatarSemanticStateSource(status: AvatarStatus): MutableSemanticStateSource { + return remember { + MutableSemanticStateSource().apply { + setExclusiveEnum(status) + } + } +} + private val AvatarPlaceholder.minimize get() = when (this) { is AvatarPlaceholder.Name -> diff --git a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarGroup.kt b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarGroup.kt index 5b5ecb80aa..0567213c40 100644 --- a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarGroup.kt +++ b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarGroup.kt @@ -4,7 +4,9 @@ import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.NonRestartableComposable +import androidx.compose.runtime.State import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -18,8 +20,10 @@ import androidx.compose.ui.layout.MeasurePolicy import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp +import com.sdds.compose.uikit.interactions.getValueAsState import com.sdds.compose.uikit.internal.AvatarDefaults import com.sdds.compose.uikit.internal.drawWithLayer +import com.sdds.compose.uikit.motion.components.avatar.rememberAvatarGroupMotion import kotlin.math.max import kotlin.math.min @@ -69,9 +73,11 @@ fun AvatarGroup( threshold: Int = AvatarDefaults.DefaultThreshold, content: AvatarGroupScope.() -> Unit, ) { - val dimensions = style.dimensions - val policy = remember(dimensions) { - avatarGroupMeasurePolicy(dimensions.itemOffset, dimensions.itemSpacing) + val motion = rememberAvatarGroupMotion() + val itemOffset by style.dimensionValues.itemOffsetValues.getValueAsState(motion.context) + val itemSpacing by style.dimensionValues.itemSpacingValues.getValueAsState(motion.context) + val policy = remember(itemOffset, itemSpacing) { + avatarGroupMeasurePolicy(itemOffset, itemSpacing) } val scope = remember(threshold) { AvatarGroupScopeImpl(threshold) } CompositionLocalProvider( @@ -83,11 +89,12 @@ fun AvatarGroup( modifier = modifier, content = { scope.content() + val shape = style.avatarStyle.shapes.getValueAsState(motion.context) scope.avatars.forEachIndexed { index, avatar -> AvatarContainer( index = index, - offset = dimensions.itemOffset.floatPx, - shape = style.avatarStyle.shape, + offset = itemOffset.floatPx, + shape = shape, clipOut = index < scope.avatars.size - 1 || scope.counter != null, content = avatar.content, ) @@ -135,7 +142,7 @@ internal val LocalAvatarGroupActionEnabled = compositionLocalOf { true } private fun AvatarContainer( index: Int, offset: Float, - shape: Shape, + shape: State, clipOut: Boolean, content: (@Composable AvatarGroupItemScope.(Int) -> Unit), ) { @@ -146,7 +153,7 @@ private fun AvatarContainer( if (clipOut) { withTransform({ translate(left = offset) }) { drawOutline( - outline = shape.createOutline(size, layoutDirection, density), + outline = shape.value.createOutline(size, layoutDirection, density), color = Color.Transparent, blendMode = BlendMode.Clear, ) diff --git a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarGroupStyle.kt b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarGroupStyle.kt index 8854837520..e46693dee4 100644 --- a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarGroupStyle.kt +++ b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarGroupStyle.kt @@ -6,6 +6,8 @@ import androidx.compose.runtime.Stable import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.unit.Dp import com.sdds.api.info.compose.ApiInfo +import com.sdds.compose.uikit.interactions.StatefulValue +import com.sdds.compose.uikit.interactions.asStatefulValue import com.sdds.compose.uikit.internal.AvatarDefaults import com.sdds.compose.uikit.style.Style import com.sdds.compose.uikit.style.StyleBuilder @@ -31,8 +33,15 @@ interface AvatarGroupStyle : Style { * Размеры и отступы компонента * @see AvatarGroupDimensions */ + @Deprecated("Use dimensionValues", ReplaceWith("dimensionValues")) val dimensions: AvatarGroupDimensions + /** + * Размеры и отступы компонента + * @see AvatarGroupDimensionValues + */ + val dimensionValues: AvatarGroupDimensionValues + companion object { /** @@ -66,6 +75,7 @@ interface AvatarGroupStyleBuilder : StyleBuilder { * @see AvatarGroupStyle.dimensions * @see AvatarGroupDimensions */ + @Deprecated("Use dimensionValues(AvatarGroupDimensionValuesBuilder)", ReplaceWith("dimensionValues { }")) fun dimensions(dimensions: AvatarGroupDimensions): AvatarGroupStyleBuilder /** @@ -73,8 +83,17 @@ interface AvatarGroupStyleBuilder : StyleBuilder { * @see AvatarGroupStyle.dimensions * @see AvatarGroupDimensions */ + @Deprecated("Use dimensionValues(AvatarGroupDimensionValuesBuilder)", ReplaceWith("dimensionValues { }")) @Composable fun dimensions(builder: @Composable AvatarGroupDimensionsBuilder.() -> Unit): AvatarGroupStyleBuilder + + /** + * Устанавливает размеры и отступы компонента [dimensions] + * @see AvatarGroupStyle.dimensions + * @see AvatarGroupDimensionValues + */ + @Composable + fun dimensionValues(builder: @Composable AvatarGroupDimensionValuesBuilder.() -> Unit): AvatarGroupStyleBuilder } /** @@ -82,12 +101,40 @@ interface AvatarGroupStyleBuilder : StyleBuilder { * @property itemOffset смещение дочерних [Avatar] относительно друг друга * @property itemSpacing расстояние между дочерними [Avatar] (влияет на толщину выреза [Avatar]) */ +@Deprecated("Use dimensionValues", ReplaceWith("dimensionValues")) @Immutable data class AvatarGroupDimensions( val itemOffset: Dp = AvatarDefaults.DefaultItemOffset, val itemSpacing: Dp = AvatarDefaults.DefaultItemSpacing, ) +/** + * Размеры и отступы компонента [AvatarGroup] + * @property itemOffsetValues смещение дочерних [Avatar] относительно друг друга + * @property itemSpacingValues расстояние между дочерними [Avatar] (влияет на толщину выреза [Avatar]) + */ +@Immutable +interface AvatarGroupDimensionValues { + + /** + * Смещения дочерних [Avatar] относительно друг друга + */ + val itemOffsetValues: StatefulValue + + /** + * Расстояния между дочерними [Avatar] (влияет на толщину выреза [Avatar]) + */ + val itemSpacingValues: StatefulValue + + companion object { + + /** + * Возвращает [AvatarGroupDimensionsBuilder] + */ + fun builder(): AvatarGroupDimensionValuesBuilder = DefaultAvatarGroupDimensionValues.Builder() + } +} + /** * Билдер для [AvatarGroupDimensions] */ @@ -109,45 +156,123 @@ interface AvatarGroupDimensionsBuilder { fun build(): AvatarGroupDimensions } +/** + * Билдер для [AvatarGroupDimensionValues] + */ +interface AvatarGroupDimensionValuesBuilder { + + /** + * Устанавливает смещение дочерних [Avatar] относительно друг друга + */ + fun itemOffset(itemOffset: Dp): AvatarGroupDimensionValuesBuilder = + itemOffset(itemOffset.asStatefulValue()) + + /** + * Устанавливает смещения дочерних [Avatar] относительно друг друга + */ + fun itemOffset(itemOffset: StatefulValue): AvatarGroupDimensionValuesBuilder + + /** + * Устанавливает расстояние между дочерними [Avatar] (влияет на толщину выреза [Avatar]) + */ + fun itemSpacing(itemSpacing: Dp): AvatarGroupDimensionValuesBuilder = + itemSpacing(itemSpacing.asStatefulValue()) + + /** + * Устанавливает расстояния между дочерними [Avatar] (влияет на толщину выреза [Avatar]) + */ + fun itemSpacing(itemSpacing: StatefulValue): AvatarGroupDimensionValuesBuilder + + /** + * Собирает [AvatarGroupDimensionValues] + */ + fun build(): AvatarGroupDimensionValues +} + @Immutable private data class DefaultAvatarGroupStyle( override val avatarStyle: AvatarStyle, + @Deprecated("Use dimensionValues(AvatarGroupDimensionValuesBuilder)", ReplaceWith("dimensionValues { }")) override val dimensions: AvatarGroupDimensions, + override val dimensionValues: AvatarGroupDimensionValues, ) : AvatarGroupStyle { class Builder(override val receiver: Any?) : AvatarGroupStyleBuilder { private var avatarStyle: AvatarStyle? = null + private var dimensionsBuilder: DefaultAvatarGroupDimensionsBuilder = DefaultAvatarGroupDimensionsBuilder() + private var dimensionValuesBuilder: AvatarGroupDimensionValuesBuilder = + AvatarGroupDimensionValues.builder() override fun avatarStyle(avatarStyle: AvatarStyle) = apply { this.avatarStyle = avatarStyle } + @Deprecated("Use dimensionValues(AvatarGroupDimensionValuesBuilder)", ReplaceWith("dimensionValues { }")) override fun dimensions(dimensions: AvatarGroupDimensions) = apply { - dimensionsBuilder.apply { + this.dimensionsBuilder.apply { + itemOffset(dimensions.itemOffset) + itemSpacing(dimensions.itemSpacing) + } + this.dimensionValuesBuilder.apply { itemOffset(dimensions.itemOffset) itemSpacing(dimensions.itemSpacing) } } + @Deprecated("Use dimensionValues(AvatarGroupDimensionValuesBuilder)", ReplaceWith("dimensionValues { }")) @Composable override fun dimensions( builder: @Composable AvatarGroupDimensionsBuilder.() -> Unit, ): AvatarGroupStyleBuilder = apply { dimensionsBuilder.builder() + val dimensions = dimensionsBuilder.build() + dimensionValuesBuilder + .itemOffset(dimensions.itemOffset) + .itemSpacing(dimensions.itemSpacing) + } + + @Composable + override fun dimensionValues( + builder: @Composable AvatarGroupDimensionValuesBuilder.() -> Unit, + ): AvatarGroupStyleBuilder = apply { + this.dimensionValuesBuilder.builder() } override fun style(): AvatarGroupStyle = DefaultAvatarGroupStyle( avatarStyle = avatarStyle ?: AvatarStyle.builder().style(), dimensions = dimensionsBuilder.build(), + dimensionValues = dimensionValuesBuilder.build(), ) } } -private class DefaultAvatarGroupDimensionsBuilder : AvatarGroupDimensionsBuilder { +@Immutable +private data class DefaultAvatarGroupDimensionValues( + override val itemOffsetValues: StatefulValue, + override val itemSpacingValues: StatefulValue, +) : AvatarGroupDimensionValues { + class Builder : AvatarGroupDimensionValuesBuilder { + + private var itemOffsetValues: StatefulValue = + AvatarDefaults.DefaultItemOffset.asStatefulValue() + private var itemSpacingValues: StatefulValue = + AvatarDefaults.DefaultItemSpacing.asStatefulValue() + + override fun itemOffset(itemOffset: StatefulValue): AvatarGroupDimensionValuesBuilder = + apply { this.itemOffsetValues = itemOffset } + + override fun itemSpacing(itemSpacing: StatefulValue): AvatarGroupDimensionValuesBuilder = + apply { this.itemSpacingValues = itemSpacing } + override fun build(): AvatarGroupDimensionValues = + DefaultAvatarGroupDimensionValues(itemOffsetValues, itemSpacingValues) + } +} + +private class DefaultAvatarGroupDimensionsBuilder : AvatarGroupDimensionsBuilder { private var itemOffset: Dp = AvatarDefaults.DefaultItemOffset private var itemSpacing: Dp = AvatarDefaults.DefaultItemSpacing diff --git a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarStyle.kt b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarStyle.kt index fb998e4465..3570321040 100644 --- a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarStyle.kt +++ b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/AvatarStyle.kt @@ -19,9 +19,11 @@ import androidx.compose.ui.unit.dp import com.sdds.api.info.compose.ApiInfo import com.sdds.api.info.compose.ApiName import com.sdds.compose.uikit.graphics.asLayered +import com.sdds.compose.uikit.graphics.brush.asStatefulBrush import com.sdds.compose.uikit.interactions.InteractiveColor import com.sdds.compose.uikit.interactions.StatefulValue import com.sdds.compose.uikit.interactions.asInteractive +import com.sdds.compose.uikit.interactions.asStatefulBrush import com.sdds.compose.uikit.interactions.asStatefulValue import com.sdds.compose.uikit.internal.AvatarDefaults import com.sdds.compose.uikit.style.Style @@ -40,8 +42,14 @@ interface AvatarStyle : Style { /** * Форма компонента */ + @Deprecated("Use shapes", ReplaceWith("shapes")) val shape: Shape + /** + * Формы компонента + */ + val shapes: StatefulValue + /** * Цвета компонента */ @@ -56,8 +64,14 @@ interface AvatarStyle : Style { /** * Стиль текста */ + @Deprecated("Use textStyles", ReplaceWith("textStyles")) val textStyle: TextStyle + /** + * Стили текста + */ + val textStyles: StatefulValue + /** * Стиль бейджа */ @@ -105,9 +119,16 @@ interface AvatarStyleBuilder : StyleBuilder { /** * Устанавливает форму [shape] компоненту - * @see AvatarStyle.shape + * @see AvatarStyle.shapes */ - fun shape(shape: Shape): AvatarStyleBuilder + fun shape(shape: Shape): AvatarStyleBuilder = + shape(shape.asStatefulValue()) + + /** + * Устанавливает формы [shape] компоненту + * @see AvatarStyle.shapes + */ + fun shape(shape: StatefulValue): AvatarStyleBuilder /** * Устанавливает цвета компонента при помощи [builder] @@ -132,9 +153,16 @@ interface AvatarStyleBuilder : StyleBuilder { /** * Устанавливает стиль текста [textStyle] - * @see AvatarStyle.textStyle + * @see AvatarStyle.textStyles + */ + fun textStyle(textStyle: TextStyle): AvatarStyleBuilder = + textStyle(textStyle.asStatefulValue()) + + /** + * Устанавливает стили текста [textStyle] + * @see AvatarStyle.textStyles */ - fun textStyle(textStyle: TextStyle): AvatarStyleBuilder + fun textStyle(textStyle: StatefulValue): AvatarStyleBuilder /** * Устанавливает стиль индикатора статуса [statusStyle] @@ -170,17 +198,29 @@ interface AvatarColors { /** * Цвет иконки действия */ + @Deprecated("Use actionBrushes", ReplaceWith("actionBrushes")) val actionColor: Color + /** + * Кисти иконки действия + */ + val actionBrushes: StatefulValue + /** * Цвет фона иконки действия */ + @Deprecated("Use actionScrimBrushes", ReplaceWith("actionScrimBrushes")) val actionScrimColor: Color + /** + * Кисти фона иконки действия + */ + val actionScrimBrushes: StatefulValue + /** * Цвет текста */ - @Deprecated("Use textColors or textBrushes") + @Deprecated("Use textBrushes", ReplaceWith("textBrushes")) val textColor: Brush /** @@ -191,6 +231,7 @@ interface AvatarColors { /** * Цвета текста */ + @Deprecated("Use textBrushes", ReplaceWith("textBrushes")) val textColors: InteractiveColor? /** @@ -208,6 +249,7 @@ interface AvatarColors { /** * Цвета фона. Используются, если [backgroundBrushes] == `null` */ + @Deprecated("Use backgroundBrushes", ReplaceWith("backgroundBrushes")) val backgroundColors: InteractiveColor? /** @@ -255,28 +297,71 @@ interface AvatarColorsBuilder { /** * Устанавливает цвет [actionColor] иконки действия - * @see AvatarColors.actionColor + * @see AvatarColors.actionBrushes + */ + fun actionColor(actionColor: Color): AvatarColorsBuilder = + actionColor(actionColor.asStatefulBrush()) + + /** + * Устанавливает цвет (или градиент) [actionColor] иконки действия + * @see AvatarColors.actionBrushes + */ + fun actionColor(actionColor: Brush): AvatarColorsBuilder = + actionColor(actionColor.asStatefulValue()) + + /** + * Устанавливает цвета [actionColor] иконки действия + * @see AvatarColors.actionBrushes + */ + fun actionColor(actionColor: InteractiveColor): AvatarColorsBuilder = + actionColor(actionColor.asStatefulBrush()) + + /** + * Устанавливает кисти [actionColor] иконки действия + * @see AvatarColors.actionBrushes */ - fun actionColor(actionColor: Color): AvatarColorsBuilder + fun actionColor(actionColor: StatefulValue): AvatarColorsBuilder /** * Устанавливает цвет фона [actionScrimColor] иконки действия - * @see AvatarColors.actionScrimColor + * @see AvatarColors.actionScrimBrushes + */ + fun actionScrimColor(actionScrimColor: Color): AvatarColorsBuilder = + actionScrimColor(actionScrimColor.asStatefulBrush()) + + /** + * Устанавливает цвет (или градиент) фона [actionScrimColor] иконки действия + * @see AvatarColors.actionScrimBrushes + */ + fun actionScrimColor(actionScrimColor: Brush): AvatarColorsBuilder = + actionScrimColor(actionScrimColor.asStatefulValue()) + + /** + * Устанавливает цвета фона [actionScrimColor] иконки действия + * @see AvatarColors.actionScrimBrushes */ - fun actionScrimColor(actionScrimColor: Color): AvatarColorsBuilder + fun actionScrimColor(actionScrimColor: InteractiveColor): AvatarColorsBuilder = + actionScrimColor(actionScrimColor.asStatefulBrush()) + + /** + * Устанавливает кисти фона [actionScrimColor] иконки действия + * @see AvatarColors.actionScrimBrushes + */ + fun actionScrimColor(actionScrimColor: StatefulValue): AvatarColorsBuilder /** * Устанавливает цвет текста [textColor] * @see AvatarColors.textColor */ fun textColor(textColor: Color): AvatarColorsBuilder = - textColor(SolidColor(textColor)) + textColor(textColor.asStatefulBrush()) /** * Устанавливает цвет (или градиент) текста [textColor] * @see AvatarColors.textColor */ - fun textColor(textColor: Brush): AvatarColorsBuilder + fun textColor(textColor: Brush): AvatarColorsBuilder = + textColor(textColor.asStatefulValue()) /** * Устанавливает цвет (или градиент) текста [textColor] @@ -288,21 +373,24 @@ interface AvatarColorsBuilder { * Устанавливает цвет (или градиент) текста [textColor] * @see AvatarColors.textColor */ - fun textColor(textColor: InteractiveColor): AvatarColorsBuilder + fun textColor(textColor: InteractiveColor): AvatarColorsBuilder = + textColor(textColor.asStatefulBrush()) /** * Устанавливает цвет фона [backgroundColor] * @see AvatarColors.backgroundColor */ @ApiName(name = "background") - fun backgroundColor(backgroundColor: Color): AvatarColorsBuilder = this + fun backgroundColor(backgroundColor: Color): AvatarColorsBuilder = + backgroundColor(backgroundColor.asStatefulBrush()) /** * Устанавливает цвет фона [backgroundColor] * @see AvatarColors.backgroundColor */ @ApiName(name = "background") - fun backgroundColor(backgroundColor: Brush): AvatarColorsBuilder = this + fun backgroundColor(backgroundColor: Brush): AvatarColorsBuilder = + backgroundColor(backgroundColor.asStatefulValue()) /** * Устанавливает цвет фона [backgroundColor] @@ -316,14 +404,15 @@ interface AvatarColorsBuilder { * @see AvatarColors.backgroundColor */ @ApiName(name = "background") - fun backgroundColor(backgroundColor: InteractiveColor): AvatarColorsBuilder = this + fun backgroundColor(backgroundColor: InteractiveColor): AvatarColorsBuilder = + backgroundColor(backgroundColor.asStatefulBrush()) /** * Устанавливает цвет фона [backgroundColor] * @see AvatarColors.backgroundColor */ @ApiName(name = "background") - fun backgroundColor(backgroundColor: StatefulValue): AvatarColorsBuilder = this + fun backgroundColor(backgroundColor: StatefulValue): AvatarColorsBuilder /** * Устанавливает прозрачность фона [alpha] @@ -350,27 +439,57 @@ interface AvatarDimensionValues { /** * Смещение статуса по горизонтали */ + @Deprecated("Use statusOffsetXValues", ReplaceWith("statusOffsetXValues")) val statusOffsetX: Dp + /** + * Смещения статуса по горизонтали + */ + val statusOffsetXValues: StatefulValue + /** * Смещение статуса по вертикали */ + @Deprecated("Use statusOffsetYValues", ReplaceWith("statusOffsetYValues")) val statusOffsetY: Dp + /** + * Смещения статуса по вертикали + */ + val statusOffsetYValues: StatefulValue + /** * Ширина аватара */ + @Deprecated("Use widthValues", ReplaceWith("widthValues")) val width: Dp + /** + * Ширины аватара + */ + val widthValues: StatefulValue + /** * Высота аватара */ + @Deprecated("Use heightValues", ReplaceWith("heightValues")) val height: Dp + /** + * Высоты аватара + */ + val heightValues: StatefulValue + /** * Внутренний отступ выреза индикатора статуса */ + @Deprecated("Use statusCutoutPaddingValues", ReplaceWith("statusCutoutPaddingValues")) val statusCutoutPadding: Dp + + /** + * Внутренние отступы выреза индикатора статуса + */ + val statusCutoutPaddingValues: StatefulValue } /** @@ -381,27 +500,57 @@ interface AvatarDimensionsBuilder { /** * Устанавливает смещение статуса по горизонтали [statusOffsetX] */ - fun statusOffsetX(statusOffsetX: Dp): AvatarDimensionsBuilder + fun statusOffsetX(statusOffsetX: Dp): AvatarDimensionsBuilder = + statusOffsetX(statusOffsetX.asStatefulValue()) + + /** + * Устанавливает смещения статуса по горизонтали [statusOffsetX] + */ + fun statusOffsetX(statusOffsetX: StatefulValue): AvatarDimensionsBuilder /** * Устанавливает смещение статуса по вертикали [statusOffsetY] */ - fun statusOffsetY(statusOffsetY: Dp): AvatarDimensionsBuilder + fun statusOffsetY(statusOffsetY: Dp): AvatarDimensionsBuilder = + statusOffsetY(statusOffsetY.asStatefulValue()) + + /** + * Устанавливает смещения статуса по вертикали [statusOffsetY] + */ + fun statusOffsetY(statusOffsetY: StatefulValue): AvatarDimensionsBuilder /** * Устанавливает ширину аватара [width] */ - fun width(width: Dp): AvatarDimensionsBuilder + fun width(width: Dp): AvatarDimensionsBuilder = + width(width.asStatefulValue()) + + /** + * Устанавливает ширины аватара [width] + */ + fun width(width: StatefulValue): AvatarDimensionsBuilder /** * Устанавливает высоту аватара [height] */ - fun height(height: Dp): AvatarDimensionsBuilder + fun height(height: Dp): AvatarDimensionsBuilder = + height(height.asStatefulValue()) + + /** + * Устанавливает высоты аватара [height] + */ + fun height(height: StatefulValue): AvatarDimensionsBuilder /** * Устанавливает Внутренний отступ выреза индикатора статуса [statusCutoutPadding] */ - fun statusCutoutPadding(statusCutoutPadding: Dp): AvatarDimensionsBuilder + fun statusCutoutPadding(statusCutoutPadding: Dp): AvatarDimensionsBuilder = + statusCutoutPadding(statusCutoutPadding.asStatefulValue()) + + /** + * Устанавливает Внутренние отступы выреза индикатора статуса [statusCutoutPadding] + */ + fun statusCutoutPadding(statusCutoutPadding: StatefulValue): AvatarDimensionsBuilder /** * Возвращает [AvatarDimensionValues] @@ -435,9 +584,9 @@ data class AvatarDimensions( @Immutable private data class DefaultAvatarStyle( - override val shape: Shape, + override val shapes: StatefulValue, override val colors: AvatarColors, - override val textStyle: TextStyle, + override val textStyles: StatefulValue, override val badgeStyle: BadgeStyle?, override val counterStyle: CounterStyle?, override val statusStyle: IndicatorStyle?, @@ -449,19 +598,27 @@ private data class DefaultAvatarStyle( @Suppress("OVERRIDE_DEPRECATION") override val dimensions: AvatarDimensions = AvatarDimensions() + @Deprecated("Use shapes", ReplaceWith("shapes")) + override val shape: Shape + get() = shapes.getDefaultValue() + + @Deprecated("Use textStyles", ReplaceWith("textStyles")) + override val textStyle: TextStyle + get() = textStyles.getDefaultValue() + class Builder(override val receiver: Any?) : AvatarStyleBuilder { - private var shape: Shape? = null + private var shapes: StatefulValue? = null private var colorsBuilder: AvatarColorsBuilder = AvatarColors.builder() private var dimensionsBuilder: AvatarDimensionsBuilder = AvatarDimensionsBuilder.builder() - private var textStyle: TextStyle? = null + private var textStyles: StatefulValue? = null private var badgeStyle: BadgeStyle? = null private var iconBadgeStyle: BadgeStyle? = null private var counterStyle: CounterStyle? = null private var statusStyle: IndicatorStyle? = null private var statusCutoutEnabled: Boolean? = null - override fun shape(shape: Shape) = apply { - this.shape = shape + override fun shape(shape: StatefulValue) = apply { + this.shapes = shape } @Composable @@ -486,8 +643,8 @@ private data class DefaultAvatarStyle( this.dimensionsBuilder.builder() } - override fun textStyle(textStyle: TextStyle) = apply { - this.textStyle = textStyle + override fun textStyle(textStyle: StatefulValue) = apply { + this.textStyles = textStyle } override fun statusStyle(statusStyle: IndicatorStyle): AvatarStyleBuilder = apply { @@ -512,9 +669,9 @@ private data class DefaultAvatarStyle( override fun style(): AvatarStyle = DefaultAvatarStyle( - shape = shape ?: RoundedCornerShape(50), + shapes = shapes ?: RoundedCornerShape(50).asStatefulValue(), colors = colorsBuilder.build(), - textStyle = textStyle ?: TextStyle.Default, + textStyles = textStyles ?: TextStyle.Default.asStatefulValue(), badgeStyle = badgeStyle, counterStyle = counterStyle, statusStyle = statusStyle, @@ -529,23 +686,35 @@ private data class DefaultAvatarStyle( private data class DefaultAvatarColors( private val activeStatusColor: Color, private val inactiveStatusColor: Color, - override val actionColor: Color, - override val actionScrimColor: Color, + override val actionBrushes: StatefulValue, + override val actionScrimBrushes: StatefulValue, override val textBrushes: StatefulValue?, - override val textColors: InteractiveColor?, override val backgroundBrushes: StatefulValue?, - override val backgroundColors: InteractiveColor?, ) : AvatarColors { - @Deprecated("Use background: StatefulValue") + @Deprecated("Use actionBrushes", ReplaceWith("actionBrushes")) + override val actionColor: Color + get() = (actionBrushes.getDefaultValue() as? SolidColor)?.value ?: Color.White + + @Deprecated("Use actionScrimBrushes", ReplaceWith("actionScrimBrushes")) + override val actionScrimColor: Color + get() = (actionScrimBrushes.getDefaultValue() as? SolidColor)?.value ?: AvatarDefaults.DefaultScrimColor + + @Deprecated("Use backgroundBrushes", ReplaceWith("backgroundBrushes")) override val backgroundColor: List = emptyList() + @Deprecated("Use backgroundBrushes", ReplaceWith("backgroundBrushes")) + override val backgroundColors: InteractiveColor = Color.Transparent.asInteractive() + @Deprecated("The property backgroundAlpha is unused") override val backgroundAlpha: Float = 0.2f - @Deprecated("Use textColors or textBrushes") + @Deprecated("Use textBrushes", ReplaceWith("textBrushes")) override val textColor: Brush = textBrushes?.getDefaultValue() ?: AvatarDefaults.defaultBrush + @Deprecated("Use textBrushes", ReplaceWith("textBrushes")) + override val textColors: InteractiveColor = Color.Transparent.asInteractive() + @Deprecated("The results of this function is unused") @Composable override fun statusColor(status: AvatarStatus): State { @@ -561,25 +730,12 @@ private data class DefaultAvatarColors( private var activeStatusColor: Color? = null private var inactiveStatusColor: Color? = null - private var actionColor: Color? = null - private var actionScrimColor: Color? = null - private var textBrush: StatefulValue? = null - private var textColor: InteractiveColor? = null - private var backgroundBrushes: StatefulValue? = null - private var backgroundColors: InteractiveColor? = null - private var backgroundAlpha: Float? = null - - override fun backgroundColor(backgroundColor: Color): AvatarColorsBuilder = apply { - backgroundColor(backgroundColor.asInteractive()) - } - - override fun backgroundColor(backgroundColor: InteractiveColor): AvatarColorsBuilder = apply { - this.backgroundColors = backgroundColor - } + private var actionBrushes: StatefulValue? = null + private var actionScrimBrushes: StatefulValue? = null + private var textBrush: StatefulValue? = AvatarDefaults.defaultBrush.asStatefulValue() + private var backgroundBrushes: StatefulValue? = AvatarDefaults.defaultBrush.asStatefulValue() - override fun backgroundColor(backgroundColor: Brush): AvatarColorsBuilder = apply { - backgroundColor(backgroundColor.asStatefulValue()) - } + private var backgroundAlpha: Float? = null override fun backgroundColor(backgroundColor: List): AvatarColorsBuilder = backgroundColor(backgroundColor.filterIsInstance().asLayered()) @@ -601,82 +757,93 @@ private data class DefaultAvatarColors( this.inactiveStatusColor = inactiveStatusColor } - override fun actionColor(actionColor: Color) = apply { - this.actionColor = actionColor + override fun actionColor(actionColor: StatefulValue): AvatarColorsBuilder = apply { + this.actionBrushes = actionColor } - override fun actionScrimColor(actionScrimColor: Color) = apply { - this.actionScrimColor = actionScrimColor + override fun actionScrimColor(actionScrimColor: StatefulValue): AvatarColorsBuilder = apply { + this.actionScrimBrushes = actionScrimColor } - override fun textColor(textColor: Brush) = - textColor(textColor.asStatefulValue()) - override fun textColor(textColor: StatefulValue): AvatarColorsBuilder = apply { this.textBrush = textColor } - override fun textColor(textColor: InteractiveColor): AvatarColorsBuilder = apply { - this.textColor = textColor - } - override fun build(): AvatarColors = DefaultAvatarColors( activeStatusColor = activeStatusColor ?: Color.Green, inactiveStatusColor = inactiveStatusColor ?: Color.DarkGray, - actionColor = actionColor ?: Color.White, - actionScrimColor = actionScrimColor ?: AvatarDefaults.DefaultScrimColor, + actionBrushes = actionBrushes ?: Color.White.asStatefulBrush(), + actionScrimBrushes = actionScrimBrushes ?: AvatarDefaults.DefaultScrimColor.asStatefulBrush(), textBrushes = textBrush, - textColors = textColor, backgroundBrushes = backgroundBrushes, - backgroundColors = backgroundColors, ) } } @Immutable private data class DefaultAvatarDimensionValues( - override val statusOffsetX: Dp = 0.dp, - override val statusOffsetY: Dp = 0.dp, - override val width: Dp = 0.dp, - override val height: Dp = 0.dp, - override val statusCutoutPadding: Dp, + override val statusOffsetXValues: StatefulValue, + override val statusOffsetYValues: StatefulValue, + override val widthValues: StatefulValue, + override val heightValues: StatefulValue, + override val statusCutoutPaddingValues: StatefulValue, ) : AvatarDimensionValues { + @Deprecated("Use statusOffsetXValues", ReplaceWith("statusOffsetXValues")) + override val statusOffsetX: Dp + get() = statusOffsetXValues.getDefaultValue() + + @Deprecated("Use statusOffsetYValues", ReplaceWith("statusOffsetYValues")) + override val statusOffsetY: Dp + get() = statusOffsetYValues.getDefaultValue() + + @Deprecated("Use widthValues", ReplaceWith("widthValues")) + override val width: Dp + get() = widthValues.getDefaultValue() + + @Deprecated("Use heightValues", ReplaceWith("heightValues")) + override val height: Dp + get() = heightValues.getDefaultValue() + + @Deprecated("Use statusCutoutPaddingValues", ReplaceWith("statusCutoutPaddingValues")) + override val statusCutoutPadding: Dp + get() = statusCutoutPaddingValues.getDefaultValue() + class Builder : AvatarDimensionsBuilder { - private var statusOffsetX: Dp = 0.dp - private var statusOffsetY: Dp = 0.dp - private var width: Dp = 0.dp - private var height: Dp = 0.dp - private var statusCutoutPadding: Dp = 3.dp - - override fun statusOffsetX(statusOffsetX: Dp): AvatarDimensionsBuilder = apply { - this.statusOffsetX = statusOffsetX + private var statusOffsetXValues: StatefulValue = 0.dp.asStatefulValue() + private var statusOffsetYValues: StatefulValue = 0.dp.asStatefulValue() + private var widthValues: StatefulValue = 0.dp.asStatefulValue() + private var heightValues: StatefulValue = 0.dp.asStatefulValue() + private var statusCutoutPaddingValues: StatefulValue = 3.dp.asStatefulValue() + + override fun statusOffsetX(statusOffsetX: StatefulValue): AvatarDimensionsBuilder = apply { + this.statusOffsetXValues = statusOffsetX } - override fun statusOffsetY(statusOffsetY: Dp): AvatarDimensionsBuilder = apply { - this.statusOffsetY = statusOffsetY + override fun statusOffsetY(statusOffsetY: StatefulValue): AvatarDimensionsBuilder = apply { + this.statusOffsetYValues = statusOffsetY } - override fun width(width: Dp): AvatarDimensionsBuilder = apply { - this.width = width + override fun width(width: StatefulValue): AvatarDimensionsBuilder = apply { + this.widthValues = width } - override fun height(height: Dp): AvatarDimensionsBuilder = apply { - this.height = height + override fun height(height: StatefulValue): AvatarDimensionsBuilder = apply { + this.heightValues = height } - override fun statusCutoutPadding(statusCutoutPadding: Dp) = apply { - this.statusCutoutPadding = statusCutoutPadding + override fun statusCutoutPadding(statusCutoutPadding: StatefulValue): AvatarDimensionsBuilder = apply { + this.statusCutoutPaddingValues = statusCutoutPadding } override fun build(): AvatarDimensionValues { return DefaultAvatarDimensionValues( - statusOffsetX, - statusOffsetY, - width, - height, - statusCutoutPadding, + statusOffsetXValues, + statusOffsetYValues, + widthValues, + heightValues, + statusCutoutPaddingValues, ) } } diff --git a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarGroupMotion.kt b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarGroupMotion.kt new file mode 100644 index 0000000000..3b78b71abe --- /dev/null +++ b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarGroupMotion.kt @@ -0,0 +1,23 @@ +package com.sdds.compose.uikit.motion.components.avatar + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.NonRestartableComposable +import com.sdds.compose.uikit.motion.Motion +import com.sdds.compose.uikit.motion.MotionContext +import com.sdds.compose.uikit.motion.rememberMotion +import com.sdds.compose.uikit.motion.rememberMotionContext + +/** + * Создает [Motion] для [AvatarGroup] + * @param motionContext контекст анимации + * @param style стиль анимации [AvatarGroup] + */ +@Composable +@NonRestartableComposable +fun rememberAvatarGroupMotion( + motionContext: MotionContext = rememberMotionContext(), +): Motion { + return rememberMotion(AvatarGroupMotionStylePlaceholder, motionContext) +} + +private object AvatarGroupMotionStylePlaceholder : AvatarGroupMotionStyle diff --git a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarGroupMotionStyle.kt b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarGroupMotionStyle.kt new file mode 100644 index 0000000000..eeee6de5ad --- /dev/null +++ b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarGroupMotionStyle.kt @@ -0,0 +1,8 @@ +package com.sdds.compose.uikit.motion.components.avatar + +import com.sdds.compose.uikit.motion.MotionStyle + +/** + * Стиль анимаций [AvatarGroup] + */ +interface AvatarGroupMotionStyle : MotionStyle diff --git a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarMotion.kt b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarMotion.kt new file mode 100644 index 0000000000..f16d6eaa39 --- /dev/null +++ b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarMotion.kt @@ -0,0 +1,22 @@ +package com.sdds.compose.uikit.motion.components.avatar + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.NonRestartableComposable +import com.sdds.compose.uikit.motion.Motion +import com.sdds.compose.uikit.motion.MotionContext +import com.sdds.compose.uikit.motion.rememberMotion +import com.sdds.compose.uikit.motion.rememberMotionContext + +/** + * Создает [Motion] для [Avatar] + * @param motionContext контекст анимации + * @param style стиль анимации [Avatar] + */ +@Composable +@NonRestartableComposable +fun rememberAvatarMotion( + style: AvatarMotionStyle = LocalAvatarMotionStyle.current, + motionContext: MotionContext = rememberMotionContext(), +): Motion { + return rememberMotion(style, motionContext) +} diff --git a/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarMotionStyle.kt b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarMotionStyle.kt new file mode 100644 index 0000000000..fb27487bf8 --- /dev/null +++ b/sdds-core/uikit-compose/src/commonMain/kotlin/com/sdds/compose/uikit/motion/components/avatar/AvatarMotionStyle.kt @@ -0,0 +1,135 @@ +package com.sdds.compose.uikit.motion.components.avatar +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.text.TextStyle +import com.sdds.compose.uikit.motion.MotionProperty +import com.sdds.compose.uikit.motion.MotionStyle +import com.sdds.compose.uikit.motion.MotionStyleBuilder +import com.sdds.compose.uikit.motion.noMotion + +/** + * CompositionLocal для стиля [AvatarMotionStyle] + */ +val LocalAvatarMotionStyle = compositionLocalOf { AvatarMotionStyle.builder().style() } + +/** + * Стиль анимаций [Avatar] + */ +@Stable +interface AvatarMotionStyle : MotionStyle { + + /** + * Анимация изменения кисти фона + */ + val backgroundColor: MotionProperty + + /** + * Анимация изменения кисти иконки действия + */ + val actionColor: MotionProperty + + /** + * Анимация изменения кисти фона иконки действия + */ + val actionScrimColor: MotionProperty + + /** + * Анимация изменения кисти текста + */ + val textColor: MotionProperty + + /** + * Анимация изменения стиля текста + */ + val textStyle: MotionProperty + + companion object { + + /** + * Возвращает [AvatarMotionStyleBuilder] для построения стиля анимаций + */ + fun builder(): AvatarMotionStyleBuilder = AvatarMotionStyleImpl.Builder() + } +} + +/** + * Builder стиля анимации [Avatar] + */ +@Stable +interface AvatarMotionStyleBuilder : MotionStyleBuilder { + + /** + * Устанавливает анимацию изменения кисти фона + */ + fun backgroundColor(color: MotionProperty): AvatarMotionStyleBuilder + + /** + * Устанавливает анимацию изменения кисти иконки действия + */ + fun actionColor(color: MotionProperty): AvatarMotionStyleBuilder + + /** + * Устанавливает анимацию изменения кисти фона иконки действия + */ + fun actionScrimColor(color: MotionProperty): AvatarMotionStyleBuilder + + /** + * Устанавливает анимацию изменения кисти текста + */ + fun textColor(color: MotionProperty): AvatarMotionStyleBuilder + + /** + * Устанавливает анимацию изменения стиля текста + */ + fun textStyle(style: MotionProperty): AvatarMotionStyleBuilder +} + +@Immutable +private class AvatarMotionStyleImpl( + override val backgroundColor: MotionProperty, + override val actionColor: MotionProperty, + override val actionScrimColor: MotionProperty, + override val textColor: MotionProperty, + override val textStyle: MotionProperty, +) : AvatarMotionStyle { + + class Builder : AvatarMotionStyleBuilder { + private var backgroundColor: MotionProperty? = null + private var actionColor: MotionProperty? = null + private var actionScrimColor: MotionProperty? = null + private var textColor: MotionProperty? = null + private var textStyle: MotionProperty? = null + + override fun backgroundColor(color: MotionProperty) = apply { + this.backgroundColor = color + } + + override fun actionColor(color: MotionProperty) = apply { + this.actionColor = color + } + + override fun actionScrimColor(color: MotionProperty) = apply { + this.actionScrimColor = color + } + + override fun textColor(color: MotionProperty) = apply { + this.textColor = color + } + + override fun textStyle(style: MotionProperty) = apply { + this.textStyle = style + } + + override fun style(): AvatarMotionStyle { + return AvatarMotionStyleImpl( + backgroundColor = backgroundColor ?: noMotion(), + actionColor = actionColor ?: noMotion(), + actionScrimColor = actionScrimColor ?: noMotion(), + textColor = textColor ?: noMotion(), + textStyle = textStyle ?: noMotion(), + ) + } + } +} From 24824b58743bbd45d15fb0598360293a54e22978 Mon Sep 17 00:00:00 2001 From: SerraMorec Date: Mon, 26 Oct 2026 09:46:37 +0300 Subject: [PATCH 2/2] feat(sdds-acore/uikit-compose): Avatar styles were rebuilt --- .../docs-template/compose-template/docs/graphics/MotionAPI.md | 1 + .../compose/sandbox/styles/avatargroup/AvatarGroupStyles.kt | 2 +- .../star/designsystem/styles/avatargroup/AvatarGroupStyles.kt | 2 +- .../sdkit/star/designsystem/styles/tooltip/TooltipStyles.kt | 3 +-- .../src/main/theme-builder-res/values/style-dimens.xml | 2 ++ tokens/plasma.giga.compose/gradle.properties | 2 +- .../sdds/plasma/giga/styles/avatargroup/AvatarGroupStyles.kt | 2 +- tokens/plasma.homeds.compose/gradle.properties | 2 +- .../sdds/plasma/homeds/styles/avatargroup/AvatarGroupStyles.kt | 2 +- tokens/plasma.sd.service.compose/gradle.properties | 2 +- .../plasma/sd/service/styles/avatargroup/AvatarGroupStyles.kt | 2 +- .../com/sdds/plasma/sd/service/styles/tooltip/TooltipStyles.kt | 3 +-- .../com/sdds/finai/styles/avatargroup/AvatarGroupStyles.kt | 2 +- tokens/sdds-sbcom-compose/gradle.properties | 2 +- .../com/sdds/sbcom/styles/avatargroup/AvatarGroupStyles.kt | 2 +- .../main/kotlin/com/sdds/sbcom/styles/tooltip/TooltipStyles.kt | 3 +-- tokens/sdds.serv.compose/gradle.properties | 2 +- .../com/sdds/serv/styles/avatargroup/AvatarGroupStyles.kt | 2 +- .../kotlin/com/sdds/serv/styles/tooltip/TooltipStyles.kt | 3 +-- 19 files changed, 20 insertions(+), 21 deletions(-) diff --git a/build-system/docs-template/compose-template/docs/graphics/MotionAPI.md b/build-system/docs-template/compose-template/docs/graphics/MotionAPI.md index cf74160f53..fba58c05da 100644 --- a/build-system/docs-template/compose-template/docs/graphics/MotionAPI.md +++ b/build-system/docs-template/compose-template/docs/graphics/MotionAPI.md @@ -288,6 +288,7 @@ semanticStateSource.setExclusive( - `getColorAsState` - `getBrushAsState` +- `getTextStyleAsState` - `getDpAsState` - `getFloatAsState` - `getIntAsState` diff --git a/integration-core/sandbox-compose/src/commonMain/kotlin/com/sdds/compose/sandbox/styles/avatargroup/AvatarGroupStyles.kt b/integration-core/sandbox-compose/src/commonMain/kotlin/com/sdds/compose/sandbox/styles/avatargroup/AvatarGroupStyles.kt index 1ede9884c4..a784b96140 100644 --- a/integration-core/sandbox-compose/src/commonMain/kotlin/com/sdds/compose/sandbox/styles/avatargroup/AvatarGroupStyles.kt +++ b/integration-core/sandbox-compose/src/commonMain/kotlin/com/sdds/compose/sandbox/styles/avatargroup/AvatarGroupStyles.kt @@ -43,7 +43,7 @@ public val AvatarGroup.S: WrapperAvatarGroupS get() = AvatarGroupStyle.builder(this) .invariantProps .avatarStyle(Avatar.S.style()) - .dimensions { + .dimensionValues { itemOffset(16.0.dp) itemSpacing(2.0.dp) } diff --git a/tokens/plasma-stards-compose/src/main/kotlin/com/sdkit/star/designsystem/styles/avatargroup/AvatarGroupStyles.kt b/tokens/plasma-stards-compose/src/main/kotlin/com/sdkit/star/designsystem/styles/avatargroup/AvatarGroupStyles.kt index 26fb2bcc1d..0c73fc42cd 100644 --- a/tokens/plasma-stards-compose/src/main/kotlin/com/sdkit/star/designsystem/styles/avatargroup/AvatarGroupStyles.kt +++ b/tokens/plasma-stards-compose/src/main/kotlin/com/sdkit/star/designsystem/styles/avatargroup/AvatarGroupStyles.kt @@ -44,7 +44,7 @@ public val AvatarGroup.S: WrapperAvatarGroupS get() = AvatarGroupStyle.builder(this) .invariantProps .avatarStyle(Avatar.S.style()) - .dimensions { + .dimensionValues { itemOffset(dimensionResource(R.dimen.sdkit_cmp_avatar_group_item_offset_s)) itemSpacing(dimensionResource(R.dimen.sdkit_cmp_avatar_group_item_spacing_s)) } diff --git a/tokens/plasma-stards-compose/src/main/kotlin/com/sdkit/star/designsystem/styles/tooltip/TooltipStyles.kt b/tokens/plasma-stards-compose/src/main/kotlin/com/sdkit/star/designsystem/styles/tooltip/TooltipStyles.kt index 06a87f48c1..039988b9eb 100644 --- a/tokens/plasma-stards-compose/src/main/kotlin/com/sdkit/star/designsystem/styles/tooltip/TooltipStyles.kt +++ b/tokens/plasma-stards-compose/src/main/kotlin/com/sdkit/star/designsystem/styles/tooltip/TooltipStyles.kt @@ -13,7 +13,6 @@ import androidx.compose.ui.res.dimensionResource import com.sdds.compose.uikit.TooltipStyle import com.sdds.compose.uikit.TooltipStyleBuilder import com.sdds.compose.uikit.adjustBy -import com.sdds.compose.uikit.interactions.asInteractive import com.sdds.compose.uikit.interactions.asStatefulValue import com.sdds.compose.uikit.style.BuilderWrapper import com.sdds.compose.uikit.style.wrap @@ -51,7 +50,7 @@ private val TooltipStyleBuilder.invariantProps: TooltipStyleBuilder .colors { backgroundColor(SolidColor(StarDsTheme.colors.surfaceOnDarkSolidSecondary).asStatefulValue()) textColor(SolidColor(StarDsTheme.colors.textOnDarkPrimary).asStatefulValue()) - contentStartColor(StarDsTheme.colors.textOnDarkSecondary.asInteractive()) + contentStartColor(SolidColor(StarDsTheme.colors.textOnDarkSecondary).asStatefulValue()) } .dimensions { contentStartSize(dimensionResource(R.dimen.sdkit_cmp_tooltip_content_start_size)) diff --git a/tokens/plasma-stards-compose/src/main/theme-builder-res/values/style-dimens.xml b/tokens/plasma-stards-compose/src/main/theme-builder-res/values/style-dimens.xml index 83804f7e70..5da03b9aac 100644 --- a/tokens/plasma-stards-compose/src/main/theme-builder-res/values/style-dimens.xml +++ b/tokens/plasma-stards-compose/src/main/theme-builder-res/values/style-dimens.xml @@ -582,6 +582,7 @@ 0.0dp 8.0dp 8.0dp + 2.0dp 16.0dp 16.0dp 8.0dp @@ -684,6 +685,7 @@ 0.0dp 8.0dp 8.0dp + 2.0dp 16.0dp 16.0dp 8.0dp diff --git a/tokens/plasma.giga.compose/gradle.properties b/tokens/plasma.giga.compose/gradle.properties index b111d2600b..73ed10cd8a 100644 --- a/tokens/plasma.giga.compose/gradle.properties +++ b/tokens/plasma.giga.compose/gradle.properties @@ -6,7 +6,7 @@ versionMinor=40 versionPatch=0 theme-version=latest -components-version=0.15.0 +components-version=0.16.0 summary.key=plasmaGigaApp diff --git a/tokens/plasma.giga.compose/src/main/kotlin/com/sdds/plasma/giga/styles/avatargroup/AvatarGroupStyles.kt b/tokens/plasma.giga.compose/src/main/kotlin/com/sdds/plasma/giga/styles/avatargroup/AvatarGroupStyles.kt index f68e49e0a1..3162f2dbc3 100644 --- a/tokens/plasma.giga.compose/src/main/kotlin/com/sdds/plasma/giga/styles/avatargroup/AvatarGroupStyles.kt +++ b/tokens/plasma.giga.compose/src/main/kotlin/com/sdds/plasma/giga/styles/avatargroup/AvatarGroupStyles.kt @@ -43,7 +43,7 @@ public val AvatarGroup.S: WrapperAvatarGroupS get() = AvatarGroupStyle.builder(this) .invariantProps .avatarStyle(Avatar.S.style()) - .dimensions { + .dimensionValues { itemOffset(16.0.dp) itemSpacing(2.0.dp) } diff --git a/tokens/plasma.homeds.compose/gradle.properties b/tokens/plasma.homeds.compose/gradle.properties index d05a06048b..73645f8ccb 100644 --- a/tokens/plasma.homeds.compose/gradle.properties +++ b/tokens/plasma.homeds.compose/gradle.properties @@ -6,7 +6,7 @@ versionMinor=32 versionPatch=0 theme-version=0.4.0 -components-version=0.17.0 +components-version=0.18.0 summary.key=plasmaHomeDS diff --git a/tokens/plasma.homeds.compose/src/main/kotlin/com/sdds/plasma/homeds/styles/avatargroup/AvatarGroupStyles.kt b/tokens/plasma.homeds.compose/src/main/kotlin/com/sdds/plasma/homeds/styles/avatargroup/AvatarGroupStyles.kt index 3f07e0f719..028aeb46a0 100644 --- a/tokens/plasma.homeds.compose/src/main/kotlin/com/sdds/plasma/homeds/styles/avatargroup/AvatarGroupStyles.kt +++ b/tokens/plasma.homeds.compose/src/main/kotlin/com/sdds/plasma/homeds/styles/avatargroup/AvatarGroupStyles.kt @@ -43,7 +43,7 @@ public val AvatarGroup.S: WrapperAvatarGroupS get() = AvatarGroupStyle.builder(this) .invariantProps .avatarStyle(Avatar.S.style()) - .dimensions { + .dimensionValues { itemOffset(16.0.dp) itemSpacing(2.0.dp) } diff --git a/tokens/plasma.sd.service.compose/gradle.properties b/tokens/plasma.sd.service.compose/gradle.properties index d8bf9018e1..29f924f13d 100644 --- a/tokens/plasma.sd.service.compose/gradle.properties +++ b/tokens/plasma.sd.service.compose/gradle.properties @@ -7,7 +7,7 @@ versionPatch=0 # ?????? plasma_b2c_ACTUAL_TYPOGRAPHY theme-version=0.2.0 -components-version=0.15.0 +components-version=0.16.0 summary.key=plasmaSDService diff --git a/tokens/plasma.sd.service.compose/src/main/kotlin/com/sdds/plasma/sd/service/styles/avatargroup/AvatarGroupStyles.kt b/tokens/plasma.sd.service.compose/src/main/kotlin/com/sdds/plasma/sd/service/styles/avatargroup/AvatarGroupStyles.kt index bcaeb67f38..b6e26770e0 100644 --- a/tokens/plasma.sd.service.compose/src/main/kotlin/com/sdds/plasma/sd/service/styles/avatargroup/AvatarGroupStyles.kt +++ b/tokens/plasma.sd.service.compose/src/main/kotlin/com/sdds/plasma/sd/service/styles/avatargroup/AvatarGroupStyles.kt @@ -43,7 +43,7 @@ public val AvatarGroup.S: WrapperAvatarGroupS get() = AvatarGroupStyle.builder(this) .invariantProps .avatarStyle(Avatar.S.style()) - .dimensions { + .dimensionValues { itemOffset(16.0.dp) itemSpacing(2.0.dp) } diff --git a/tokens/plasma.sd.service.compose/src/main/kotlin/com/sdds/plasma/sd/service/styles/tooltip/TooltipStyles.kt b/tokens/plasma.sd.service.compose/src/main/kotlin/com/sdds/plasma/sd/service/styles/tooltip/TooltipStyles.kt index 7f0dcbd6a3..9953ed47dd 100644 --- a/tokens/plasma.sd.service.compose/src/main/kotlin/com/sdds/plasma/sd/service/styles/tooltip/TooltipStyles.kt +++ b/tokens/plasma.sd.service.compose/src/main/kotlin/com/sdds/plasma/sd/service/styles/tooltip/TooltipStyles.kt @@ -13,7 +13,6 @@ import androidx.compose.ui.unit.dp import com.sdds.compose.uikit.TooltipStyle import com.sdds.compose.uikit.TooltipStyleBuilder import com.sdds.compose.uikit.adjustBy -import com.sdds.compose.uikit.interactions.asInteractive import com.sdds.compose.uikit.interactions.asStatefulValue import com.sdds.compose.uikit.style.BuilderWrapper import com.sdds.compose.uikit.style.wrap @@ -50,7 +49,7 @@ private val TooltipStyleBuilder.invariantProps: TooltipStyleBuilder .colors { backgroundColor(SolidColor(PlasmaSdServiceTheme.colors.surfaceDefaultSolidCard).asStatefulValue()) textColor(SolidColor(PlasmaSdServiceTheme.colors.textDefaultPrimary).asStatefulValue()) - contentStartColor(PlasmaSdServiceTheme.colors.textDefaultSecondary.asInteractive()) + contentStartColor(SolidColor(PlasmaSdServiceTheme.colors.textDefaultSecondary).asStatefulValue()) } .dimensions { contentStartSize(16.0.dp) diff --git a/tokens/sdds-finai-compose/src/commonMain/kotlin/com/sdds/finai/styles/avatargroup/AvatarGroupStyles.kt b/tokens/sdds-finai-compose/src/commonMain/kotlin/com/sdds/finai/styles/avatargroup/AvatarGroupStyles.kt index 8c68819d2e..234c57205d 100644 --- a/tokens/sdds-finai-compose/src/commonMain/kotlin/com/sdds/finai/styles/avatargroup/AvatarGroupStyles.kt +++ b/tokens/sdds-finai-compose/src/commonMain/kotlin/com/sdds/finai/styles/avatargroup/AvatarGroupStyles.kt @@ -43,7 +43,7 @@ public val AvatarGroup.S: WrapperAvatarGroupS get() = AvatarGroupStyle.builder(this) .invariantProps .avatarStyle(Avatar.S.style()) - .dimensions { + .dimensionValues { itemOffset(16.0.dp) itemSpacing(2.0.dp) } diff --git a/tokens/sdds-sbcom-compose/gradle.properties b/tokens/sdds-sbcom-compose/gradle.properties index 36e58c17c6..924cffd3ab 100644 --- a/tokens/sdds-sbcom-compose/gradle.properties +++ b/tokens/sdds-sbcom-compose/gradle.properties @@ -5,7 +5,7 @@ versionMajor=0 versionMinor=17 versionPatch=0 -components-version=0.9.0 +components-version=0.10.0 summary.key=sdds-sbcom diff --git a/tokens/sdds-sbcom-compose/src/main/kotlin/com/sdds/sbcom/styles/avatargroup/AvatarGroupStyles.kt b/tokens/sdds-sbcom-compose/src/main/kotlin/com/sdds/sbcom/styles/avatargroup/AvatarGroupStyles.kt index fc97774f7a..40a0da99aa 100644 --- a/tokens/sdds-sbcom-compose/src/main/kotlin/com/sdds/sbcom/styles/avatargroup/AvatarGroupStyles.kt +++ b/tokens/sdds-sbcom-compose/src/main/kotlin/com/sdds/sbcom/styles/avatargroup/AvatarGroupStyles.kt @@ -43,7 +43,7 @@ public val AvatarGroup.Size24: WrapperAvatarGroupSize24 get() = AvatarGroupStyle.builder(this) .invariantProps .avatarStyle(Avatar.Size24.style()) - .dimensions { + .dimensionValues { itemOffset(14.0.dp) itemSpacing(2.0.dp) } diff --git a/tokens/sdds-sbcom-compose/src/main/kotlin/com/sdds/sbcom/styles/tooltip/TooltipStyles.kt b/tokens/sdds-sbcom-compose/src/main/kotlin/com/sdds/sbcom/styles/tooltip/TooltipStyles.kt index d8223c7819..7e4add92b7 100644 --- a/tokens/sdds-sbcom-compose/src/main/kotlin/com/sdds/sbcom/styles/tooltip/TooltipStyles.kt +++ b/tokens/sdds-sbcom-compose/src/main/kotlin/com/sdds/sbcom/styles/tooltip/TooltipStyles.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.unit.dp import com.sdds.compose.uikit.TooltipStyle import com.sdds.compose.uikit.TooltipStyleBuilder -import com.sdds.compose.uikit.interactions.asInteractive import com.sdds.compose.uikit.interactions.asStatefulValue import com.sdds.compose.uikit.style.BuilderWrapper import com.sdds.compose.uikit.style.wrap @@ -44,7 +43,7 @@ public val Tooltip.Default: WrapperTooltipDefault .colors { backgroundColor(SolidColor(SddsSbComTheme.colors.surfaceDefaultSolidDefault).asStatefulValue()) textColor(SolidColor(SddsSbComTheme.colors.textInversePrimary).asStatefulValue()) - contentStartColor(SddsSbComTheme.colors.textInversePrimary.asInteractive()) + contentStartColor(SolidColor(SddsSbComTheme.colors.textInversePrimary).asStatefulValue()) } .dimensions { offset(8.0.dp) diff --git a/tokens/sdds.serv.compose/gradle.properties b/tokens/sdds.serv.compose/gradle.properties index 448c67b0e8..920b2da69d 100644 --- a/tokens/sdds.serv.compose/gradle.properties +++ b/tokens/sdds.serv.compose/gradle.properties @@ -6,7 +6,7 @@ versionMinor=47 versionPatch=0 theme-version=0.9.0 -components-version=0.18.0 +components-version=0.19.0 summary.key=SDDSService diff --git a/tokens/sdds.serv.compose/src/commonMain/kotlin/com/sdds/serv/styles/avatargroup/AvatarGroupStyles.kt b/tokens/sdds.serv.compose/src/commonMain/kotlin/com/sdds/serv/styles/avatargroup/AvatarGroupStyles.kt index cc2ab2d149..c0597bbf1b 100644 --- a/tokens/sdds.serv.compose/src/commonMain/kotlin/com/sdds/serv/styles/avatargroup/AvatarGroupStyles.kt +++ b/tokens/sdds.serv.compose/src/commonMain/kotlin/com/sdds/serv/styles/avatargroup/AvatarGroupStyles.kt @@ -43,7 +43,7 @@ public val AvatarGroup.S: WrapperAvatarGroupS get() = AvatarGroupStyle.builder(this) .invariantProps .avatarStyle(Avatar.S.style()) - .dimensions { + .dimensionValues { itemOffset(16.0.dp) itemSpacing(2.0.dp) } diff --git a/tokens/sdds.serv.compose/src/commonMain/kotlin/com/sdds/serv/styles/tooltip/TooltipStyles.kt b/tokens/sdds.serv.compose/src/commonMain/kotlin/com/sdds/serv/styles/tooltip/TooltipStyles.kt index 7de77a57e0..fd063651f1 100644 --- a/tokens/sdds.serv.compose/src/commonMain/kotlin/com/sdds/serv/styles/tooltip/TooltipStyles.kt +++ b/tokens/sdds.serv.compose/src/commonMain/kotlin/com/sdds/serv/styles/tooltip/TooltipStyles.kt @@ -13,7 +13,6 @@ import androidx.compose.ui.unit.dp import com.sdds.compose.uikit.TooltipStyle import com.sdds.compose.uikit.TooltipStyleBuilder import com.sdds.compose.uikit.adjustBy -import com.sdds.compose.uikit.interactions.asInteractive import com.sdds.compose.uikit.interactions.asStatefulValue import com.sdds.compose.uikit.style.BuilderWrapper import com.sdds.compose.uikit.style.wrap @@ -50,7 +49,7 @@ private val TooltipStyleBuilder.invariantProps: TooltipStyleBuilder .colors { backgroundColor(SolidColor(SddsServTheme.colors.surfaceDefaultSolidCard).asStatefulValue()) textColor(SolidColor(SddsServTheme.colors.textDefaultPrimary).asStatefulValue()) - contentStartColor(SddsServTheme.colors.textDefaultSecondary.asInteractive()) + contentStartColor(SolidColor(SddsServTheme.colors.textDefaultSecondary).asStatefulValue()) } .dimensions { contentStartSize(16.0.dp)