diff --git a/Projects/Shared/DesignSystem/Sources/Color/ShapeStyle+.swift b/Projects/Shared/DesignSystem/Sources/Color/ShapeStyle+.swift index 492bc3a..cb0d4b7 100644 --- a/Projects/Shared/DesignSystem/Sources/Color/ShapeStyle+.swift +++ b/Projects/Shared/DesignSystem/Sources/Color/ShapeStyle+.swift @@ -91,4 +91,31 @@ public extension ShapeStyle where Self == Color { static var statusError: Color { .init(hex: "C92D33") } static var statusWarningAlpha: Color { .init(hex: "FFB400", alpha: 0.4) } static var statusWarning: Color { .init(hex: "FFB400") } + // MARK: - Component + static var bedgeFilledBackgroundDefault: Color { .beige600 } + static var bedgeFilledBackgroundInverse: Color { .primary500 } + static var bedgeFilledTextDefault: Color { .primary500 } + static var bedgeFilledTextInverse: Color { .beige50 } + static var bedgeOutlineBackround: Color { .beige50 } + static var bedgeOutlineBorder: Color { .primary100 } + static var bedgeOutlineText: Color { .primary500 } + static var buttonPrimaryBackgroundDefault: Color { .primary500 } + static var buttonPrimaryBackgroundDisabled: Color { .primary200 } + static var buttonPrimaryBackgroundPressed: Color { .primary800 } + static var buttonPrimaryTextDefault: Color { .beige50 } + static var buttonSecondaryBackgroundDefault: Color { .beige300 } + static var buttonSecondaryBackgroundPressed: Color { .beige400 } + static var buttonSecondaryBorderDefault: Color { .beige600 } + static var buttonSecondaryBorderPressed: Color { .secondary500 } + static var buttonSecondaryTextDefault: Color { .neutral600 } + static var buttonSecondaryTextDisabled: Color { .neutral300 } + static var inputBorderActive: Color { .beige700 } + static var inputBorderDefault: Color { .beige600 } + static var inputBorderError: Color { .borderError } + static var inputSurfaceDefault: Color { .beige50 } + static var inputSurfaceDisabled: Color { .beige300 } + static var inputTextActive: Color { .neutral500 } + static var inputTextDefault: Color { .neutral300 } + static var inputTextError: Color { .statusError } + } diff --git a/Projects/Shared/DesignSystem/Sources/Extension/CGFloat/CGFloat+Component+.swift b/Projects/Shared/DesignSystem/Sources/Extension/CGFloat/CGFloat+Component+.swift new file mode 100644 index 0000000..663eae5 --- /dev/null +++ b/Projects/Shared/DesignSystem/Sources/Extension/CGFloat/CGFloat+Component+.swift @@ -0,0 +1,10 @@ +// AUTO-GENERATED by Tools/TokenGenerator.swift — DO NOT EDIT +// Source: Projects/Shared/DesignSystem/Resources/Mode 1.tokens.json + +import CoreGraphics + +public extension CGFloat { + + // MARK: - Component + static let buttonRadius: CGFloat = .`default` +} diff --git a/Projects/Shared/DesignSystem/Sources/UI/Token/ComponentToken.swift b/Projects/Shared/DesignSystem/Sources/UI/Token/ComponentToken.swift index 6a3e717..6b2e567 100644 --- a/Projects/Shared/DesignSystem/Sources/UI/Token/ComponentToken.swift +++ b/Projects/Shared/DesignSystem/Sources/UI/Token/ComponentToken.swift @@ -7,72 +7,72 @@ public enum ComponentToken { public enum Bedge { public enum Filled { public enum Background { - public static var `default`: Color { .beige600 } - public static var inverse: Color { .primary500 } + public static var `default`: Color { .bedgeFilledBackgroundDefault } + public static var inverse: Color { .bedgeFilledBackgroundInverse } } public enum Text { - public static var `default`: Color { .primary500 } - public static var inverse: Color { .beige50 } + public static var `default`: Color { .bedgeFilledTextDefault } + public static var inverse: Color { .bedgeFilledTextInverse } } } public enum Outline { - public static var backround: Color { .beige50 } - public static var border: Color { .primary100 } - public static var text: Color { .primary500 } + public static var backround: Color { .bedgeOutlineBackround } + public static var border: Color { .bedgeOutlineBorder } + public static var text: Color { .bedgeOutlineText } } } public enum Button { - public static var radius: CGFloat { .`default` } + public static var radius: CGFloat { .buttonRadius } public enum Primary { public enum Background { - public static var `default`: Color { .primary500 } - public static var disabled: Color { .primary200 } - public static var pressed: Color { .primary800 } + public static var `default`: Color { .buttonPrimaryBackgroundDefault } + public static var disabled: Color { .buttonPrimaryBackgroundDisabled } + public static var pressed: Color { .buttonPrimaryBackgroundPressed } } public enum Text { - public static var `default`: Color { .beige50 } + public static var `default`: Color { .buttonPrimaryTextDefault } } } public enum Secondary { public enum Background { - public static var `default`: Color { .beige300 } - public static var pressed: Color { .beige400 } + public static var `default`: Color { .buttonSecondaryBackgroundDefault } + public static var pressed: Color { .buttonSecondaryBackgroundPressed } } public enum Border { - public static var `default`: Color { .beige600 } - public static var pressed: Color { .secondary500 } + public static var `default`: Color { .buttonSecondaryBorderDefault } + public static var pressed: Color { .buttonSecondaryBorderPressed } } public enum Text { - public static var `default`: Color { .neutral600 } - public static var disabled: Color { .neutral300 } + public static var `default`: Color { .buttonSecondaryTextDefault } + public static var disabled: Color { .buttonSecondaryTextDisabled } } } } public enum Input { public enum Border { - public static var active: Color { .beige700 } - public static var `default`: Color { .beige600 } - public static var error: Color { .borderError } + public static var active: Color { .inputBorderActive } + public static var `default`: Color { .inputBorderDefault } + public static var error: Color { .inputBorderError } } public enum Surface { - public static var `default`: Color { .beige50 } - public static var disabled: Color { .beige300 } + public static var `default`: Color { .inputSurfaceDefault } + public static var disabled: Color { .inputSurfaceDisabled } } public enum Text { - public static var active: Color { .neutral500 } - public static var `default`: Color { .neutral300 } - public static var error: Color { .statusError } + public static var active: Color { .inputTextActive } + public static var `default`: Color { .inputTextDefault } + public static var error: Color { .inputTextError } } } } diff --git a/Tools/TokenGenerator.swift b/Tools/TokenGenerator.swift index 5b5488f..a73f300 100644 --- a/Tools/TokenGenerator.swift +++ b/Tools/TokenGenerator.swift @@ -16,7 +16,8 @@ let colorOut = "\(sourcesDir)/Color/ShapeStyle+.swift" let cgfloatDir = "\(sourcesDir)/Extension/CGFloat" let radiusOut = "\(cgfloatDir)/CGFloat+Radius+.swift" let spacingOut = "\(cgfloatDir)/CGFloat+Spacing+.swift" -let componentOut = "\(sourcesDir)/UI/Token/ComponentToken.swift" +let componentOut = "\(sourcesDir)/UI/Token/ComponentToken.swift" // legacy nested file (deleted at end) +let componentNumberOut = "\(cgfloatDir)/CGFloat+Component+.swift" try? FileManager.default.createDirectory(atPath: "\(sourcesDir)/UI/Token", withIntermediateDirectories: true) try? FileManager.default.createDirectory(atPath: cgfloatDir, withIntermediateDirectories: true) @@ -124,35 +125,80 @@ func resolveComponentNumber(_ node: [String: Any]) -> String? { return nil } -func walkComponent(_ node: [String: Any], indent: String, out: inout [String]) { +// Component subtree 를 flat path 로 풀어 ShapeStyle / CGFloat 확장에 직접 추가한다. +// Component.button.primary.background.default → buttonPrimaryBackgroundDefault +// Component.button.radius → buttonRadius +func walkComponentFlat( + _ node: [String: Any], + pathPrefix: [String], + colorLines: inout [String], + numberLines: inout [String] +) { let keys = node.keys.sorted() - // 리프(컬러/숫자) 먼저, 그 다음 그룹(중첩 enum) let leafKeys = keys.filter { (node[$0] as? [String: Any])?["$type"] != nil } let groupKeys = keys.filter { (node[$0] as? [String: Any])?["$type"] == nil } for key in leafKeys { guard let child = node[key] as? [String: Any], let type = child["$type"] as? String else { continue } + let propName = flatPropertyName(pathPrefix + [key]) switch type { case "color": if let expr = resolveComponentColor(child) { - out.append("\(indent)public static var \(swiftKey(key)): Color { \(expr) }") + colorLines.append(" static var \(propName): Color { \(expr) }") } case "number": if let expr = resolveComponentNumber(child) { - out.append("\(indent)public static var \(swiftKey(key)): CGFloat { \(expr) }") + numberLines.append(" static let \(propName): CGFloat = \(expr)") } default: continue } } + for key in groupKeys { + guard let child = node[key] as? [String: Any] else { continue } + walkComponentFlat(child, pathPrefix: pathPrefix + [key], colorLines: &colorLines, numberLines: &numberLines) + } +} + +// Component subtree 의 nested ComponentToken enum. 값은 flat 정의를 forwarding 하므로 +// source of truth 는 항상 ShapeStyle / CGFloat 확장 한 곳. +// public static var `default`: Color { .buttonPrimaryBackgroundDefault } +func walkComponentNested( + _ node: [String: Any], + pathPrefix: [String], + indent: String, + out: inout [String] +) { + let keys = node.keys.sorted() + let leafKeys = keys.filter { (node[$0] as? [String: Any])?["$type"] != nil } + let groupKeys = keys.filter { (node[$0] as? [String: Any])?["$type"] == nil } + for key in leafKeys { + guard let child = node[key] as? [String: Any], let type = child["$type"] as? String else { continue } + let flatName = flatPropertyName(pathPrefix + [key]) + switch type { + case "color": + out.append("\(indent)public static var \(swiftKey(key)): Color { .\(flatName) }") + case "number": + out.append("\(indent)public static var \(swiftKey(key)): CGFloat { .\(flatName) }") + default: continue + } + } for (i, key) in groupKeys.enumerated() { guard let child = node[key] as? [String: Any] else { continue } if i == 0, !leafKeys.isEmpty { out.append("") } if i > 0 { out.append("") } out.append("\(indent)public enum \(capitalizeFirst(key)) {") - walkComponent(child, indent: indent + " ", out: &out) + walkComponentNested(child, pathPrefix: pathPrefix + [key], indent: indent + " ", out: &out) out.append("\(indent)}") } } +// ["button", "primary", "background", "default"] → "buttonPrimaryBackgroundDefault" +func flatPropertyName(_ segs: [String]) -> String { + guard let first = segs.first else { return "" } + let head = first.prefix(1).lowercased() + first.dropFirst() + let tail = segs.dropFirst().map(capitalizeFirst).joined() + return swiftKey(head + tail) +} + func capitalizeFirst(_ s: String) -> String { s.prefix(1).uppercased() + s.dropFirst() } @@ -251,6 +297,17 @@ if let status = semantic["status"] as? [String: Any] { } } +// Component colors — flat ShapeStyle 확장에 직접 합쳐 ComponentToken 중첩 enum 을 폐기. +let component = json["Component"] as! [String: Any] +var componentColorLines: [String] = [] +var componentNumberLines: [String] = [] +walkComponentFlat(component, pathPrefix: [], colorLines: &componentColorLines, numberLines: &componentNumberLines) +if !componentColorLines.isEmpty { + lines.append(" // MARK: - Component") + lines.append(contentsOf: componentColorLines) + lines.append("") +} + lines.append("}") lines.append("") try writeFile(colorOut, lines.joined(separator: "\n")) @@ -286,13 +343,27 @@ sLines.append("}") sLines.append("") try writeFile(spacingOut, sLines.joined(separator: "\n")) -// MARK: - Component +// MARK: - Component (numbers) -let component = json["Component"] as! [String: Any] -var compLines: [String] = [header, "", "import SwiftUI", "", "public enum ComponentToken {"] -walkComponent(component, indent: " ", out: &compLines) -compLines.append("}") -compLines.append("") -try writeFile(componentOut, compLines.joined(separator: "\n")) +// 색상은 위에서 ShapeStyle+.swift 에 이미 추가됨. 숫자만 CGFloat 확장으로 별도 출력. + +if !componentNumberLines.isEmpty { + var cLines: [String] = [header, "", "import CoreGraphics", "", "public extension CGFloat {", ""] + cLines.append(" // MARK: - Component") + cLines.append(contentsOf: componentNumberLines) + cLines.append("}") + cLines.append("") + try writeFile(componentNumberOut, cLines.joined(separator: "\n")) +} + +// MARK: - Component (nested ComponentToken) + +// flat ShapeStyle / CGFloat 확장을 forwarding 하는 구조적 접근용 enum. +// 그룹 단위 캡처/자동완성 탐색에 사용. 값의 source of truth 는 flat 정의 한 곳. +var ctLines: [String] = [header, "", "import SwiftUI", "", "public enum ComponentToken {"] +walkComponentNested(component, pathPrefix: [], indent: " ", out: &ctLines) +ctLines.append("}") +ctLines.append("") +try writeFile(componentOut, ctLines.joined(separator: "\n")) print("[token-gen] done.")