-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNeoComponents.kt
More file actions
356 lines (337 loc) · 12.2 KB
/
NeoComponents.kt
File metadata and controls
356 lines (337 loc) · 12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
package com.fourshil.musicya.ui.components
import androidx.compose.foundation.background
import com.fourshil.musicya.ui.theme.*
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Check
/**
* Soft Neo-Brutalism Design System with Claude Palette
*
* Key Principles:
* - Softer shadows (with transparency) instead of hard black shadows
* - Thinner, consistent borders (1.5dp default, 2dp emphasis)
* - Warm color palette based on Claude Orange (#D97757)
* - Friendly, approachable aesthetic while maintaining bold typography
*/
// Soft shadow color with transparency for gentle depth
private val CurrentNeoBorder: Color
@Composable
get() = if (isSystemInDarkTheme()) SoftBorderDark else SoftBorderLight
private val CurrentNeoShadow: Color
@Composable
get() = if (isSystemInDarkTheme()) SoftShadowDark else SoftShadowLight
/**
* NeoScaffold
* A wrapper around Scaffold that enforces the Neo design tokens.
*/
@Composable
fun NeoScaffold(
modifier: Modifier = Modifier,
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
containerColor: Color = MaterialTheme.colorScheme.background,
contentColor: Color = MaterialTheme.colorScheme.onBackground,
content: @Composable (PaddingValues) -> Unit
) {
Scaffold(
modifier = modifier,
topBar = topBar,
bottomBar = bottomBar,
snackbarHost = snackbarHost,
floatingActionButton = floatingActionButton,
floatingActionButtonPosition = floatingActionButtonPosition,
containerColor = containerColor,
contentColor = contentColor,
content = content
)
}
/**
* NeoCard - Soft Neo-Brutalist Card
* Features softer shadows with transparency, thinner borders, friendly corners
*/
@Composable
fun NeoCard(
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
borderColor: Color = CurrentNeoBorder,
borderWidth: Dp = NeoDimens.BorderDefault,
shadowSize: Dp = NeoDimens.ShadowDefault,
shape: Shape = RoundedCornerShape(NeoDimens.CornerMedium),
onClick: (() -> Unit)? = null,
content: @Composable BoxScope.() -> Unit
) {
Box(
modifier = modifier
.padding(bottom = shadowSize, end = shadowSize)
) {
// Soft Shadow with transparency
Box(
modifier = Modifier
.matchParentSize()
.offset(x = shadowSize, y = shadowSize)
.background(CurrentNeoShadow, shape)
)
// Card Surface
Box(
modifier = Modifier
.clip(shape)
.background(backgroundColor)
.border(borderWidth, borderColor, shape)
.then(
if (onClick != null) {
Modifier.clickable(
onClick = onClick,
indication = null,
interactionSource = remember { MutableInteractionSource() }
)
} else {
Modifier
}
),
content = content
)
}
}
/**
* Soft Neo-Brutalist Button
* Characteristics:
* - Thinner border (2dp instead of 4dp)
* - Soft shadow with transparency
* - Click animation (press down)
* - Claude Orange primary color
*/
@Composable
fun NeoButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
shape: Shape = RoundedCornerShape(NeoDimens.CornerMedium),
borderWidth: Dp = NeoDimens.BorderBold,
shadowSize: Dp = NeoDimens.ShadowDefault,
content: @Composable BoxScope.() -> Unit
) {
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
// Offset logic: when pressed, shadow disappears (button moves down-right)
val translationX = if (isPressed) shadowSize else 0.dp
val translationY = if (isPressed) shadowSize else 0.dp
val shadowAlpha = if (isPressed) 0f else 1f
Box(
modifier = modifier
.padding(bottom = shadowSize, end = shadowSize)
) {
// Soft Shadow Layer
Box(
modifier = Modifier
.matchParentSize()
.offset(x = shadowSize, y = shadowSize)
.background(CurrentNeoShadow, shape)
.graphicsLayer { alpha = shadowAlpha }
)
// Button Surface
Box(
modifier = Modifier
.fillMaxSize()
.offset(x = translationX, y = translationY)
.clip(shape)
.background(backgroundColor)
.border(borderWidth, CurrentNeoBorder, shape)
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = onClick
),
contentAlignment = Alignment.Center,
content = content
)
}
}
/**
* Soft Neo-Brutalist Progress Bar
* Thinner border, softer shadow, Claude Orange fill
*/
@Composable
fun NeoProgressBar(
progress: Float,
modifier: Modifier = Modifier,
height: Dp = 20.dp,
fillColor: Color = MaterialTheme.colorScheme.primary,
backgroundColor: Color = MaterialTheme.colorScheme.surfaceVariant
) {
val shadowSize = NeoDimens.ShadowSubtle
val borderColor = CurrentNeoBorder
Box(
modifier = modifier
.padding(bottom = shadowSize, end = shadowSize)
) {
// Soft Shadow
Box(
modifier = Modifier
.fillMaxWidth()
.height(height)
.offset(x = shadowSize, y = shadowSize)
.background(CurrentNeoShadow, RoundedCornerShape(NeoDimens.CornerFull))
)
// Main Container
Box(
modifier = Modifier
.fillMaxWidth()
.height(height)
.clip(RoundedCornerShape(NeoDimens.CornerFull))
.background(backgroundColor)
.border(NeoDimens.BorderDefault, borderColor, RoundedCornerShape(NeoDimens.CornerFull))
) {
// Fill
Box(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(progress)
.background(fillColor)
.drawBehind {
val strokeWidth = NeoDimens.BorderDefault.toPx()
drawLine(
color = borderColor,
start = androidx.compose.ui.geometry.Offset(size.width, 0f),
end = androidx.compose.ui.geometry.Offset(size.width, size.height),
strokeWidth = strokeWidth
)
}
)
}
}
}
/**
* Soft Neo-Brutalist Dialog Wrapper
* Softer shadow, thinner border, friendly corners
*/
@Composable
fun NeoDialogWrapper(
title: String,
onDismiss: () -> Unit,
contentColor: Color = MaterialTheme.colorScheme.onSurface,
surfaceColor: Color = MaterialTheme.colorScheme.surface,
content: @Composable () -> Unit
) {
androidx.compose.ui.window.Dialog(onDismissRequest = onDismiss) {
NeoCard(
modifier = Modifier.fillMaxWidth(),
backgroundColor = surfaceColor,
borderColor = CurrentNeoBorder,
borderWidth = NeoDimens.BorderDefault,
shadowSize = NeoDimens.ShadowProminent,
shape = RoundedCornerShape(NeoDimens.CornerLarge)
) {
Column(
modifier = Modifier.padding(NeoDimens.SpacingXL)
) {
// Header
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = title,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
color = contentColor,
letterSpacing = 0.5.sp
)
IconButton(
onClick = onDismiss,
modifier = Modifier
.size(NeoDimens.TouchTargetMin)
.border(NeoDimens.BorderDefault, CurrentNeoBorder, androidx.compose.foundation.shape.CircleShape)
) {
Icon(
imageVector = androidx.compose.material.icons.Icons.Default.Close,
contentDescription = "Close dialog",
tint = contentColor,
modifier = Modifier.size(NeoDimens.IconMedium)
)
}
}
HorizontalDivider(
modifier = Modifier.padding(vertical = NeoDimens.SpacingL),
thickness = NeoDimens.BorderSubtle,
color = CurrentNeoBorder
)
content()
}
}
}
}
/**
* Soft Neo-Brutalist Selection Item
* Friendly border, proper touch targets, accessible
*/
@Composable
fun NeoSelectionItem(
text: String,
selected: Boolean,
modifier: Modifier = Modifier,
contentColor: Color = MaterialTheme.colorScheme.onSurface,
surfaceColor: Color = MaterialTheme.colorScheme.surface,
onClick: () -> Unit
) {
val backgroundColor = if (selected) MaterialTheme.colorScheme.primary else surfaceColor
val textColor = if (selected) MaterialTheme.colorScheme.onPrimary else contentColor
val borderColor = CurrentNeoBorder
Box(
modifier = modifier
.fillMaxWidth()
.padding(vertical = NeoDimens.SpacingXS)
.clip(RoundedCornerShape(NeoDimens.CornerSmall))
.background(backgroundColor)
.border(NeoDimens.BorderDefault, borderColor, RoundedCornerShape(NeoDimens.CornerSmall))
.clickable(onClick = onClick)
.padding(NeoDimens.SpacingL)
.heightIn(min = NeoDimens.TouchTargetMin)
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = text,
style = MaterialTheme.typography.titleMedium,
fontWeight = if (selected) FontWeight.Bold else FontWeight.Medium,
color = textColor
)
if (selected) {
Icon(
imageVector = androidx.compose.material.icons.Icons.Default.Check,
contentDescription = "Selected",
tint = textColor,
modifier = Modifier.size(NeoDimens.IconMedium)
)
}
}
}
}