| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- //
- // Category.swift
- // Todos
- //
- // Created by Sam Jaffe on 3/2/26.
- //
- import Foundation
- import SwiftUI
- // https://gist.github.com/peterfriese/bb2fc5df202f6a15cc807bd87ff15193
- // Inspired by https://cocoacasts.com/from-hex-to-uicolor-and-back-in-swift
- // Make Color codable. This includes support for transparency.
- // See https://www.digitalocean.com/community/tutorials/css-hex-code-colors-alpha-values
- extension Color: @retroactive Codable {
- #if os(macOS)
- fileprivate typealias UnifiedColor = NSColor
- #endif
- #if os(iOS)
- fileprivate typealias UnifiedColor = UIColor
- #endif
- init(hex: String) {
- var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
- hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
- var rgb: UInt64 = 0
- var red: CGFloat = 0.0
- var green: CGFloat = 0.0
- var blue: CGFloat = 0.0
- var opacity: CGFloat = 1.0
- let length = hexSanitized.count
- Scanner(string: hexSanitized).scanHexInt64(&rgb)
- if length == 6 {
- red = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
- green = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
- blue = CGFloat(rgb & 0x0000FF) / 255.0
- } else if length == 8 {
- red = CGFloat((rgb & 0xFF000000) >> 24) / 255.0
- green = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0
- blue = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0
- opacity = CGFloat(rgb & 0x000000FF) / 255.0
- }
- self.init(.sRGB,
- red: Double(red),
- green: Double(green),
- blue: Double(blue),
- opacity: Double(opacity))
- }
- public init(from decoder: Decoder) throws {
- let container = try decoder.singleValueContainer()
- let hex = try container.decode(String.self)
- self.init(hex: hex)
- }
- public func encode(to encoder: Encoder) throws {
- var container = encoder.singleValueContainer()
- try container.encode(hex)
- }
- var hex: String? {
- return toHex()
- }
- func toHex(alpha: Bool = false) -> String? {
- guard let components = UnifiedColor(self).cgColor.components, components.count >= 3 else {
- return nil
- }
- let red = Float(components[0])
- let green = Float(components[1])
- let blue = Float(components[2])
- var opacity = Float(1.0)
- if components.count >= 4 {
- opacity = Float(components[3])
- }
- if alpha {
- return String(format: "%02lX%02lX%02lX%02lX",
- lroundf(red * 255),
- lroundf(green * 255),
- lroundf(blue * 255),
- lroundf(opacity * 255))
- } else {
- return String(format: "%02lX%02lX%02lX",
- lroundf(red * 255),
- lroundf(green * 255),
- lroundf(blue * 255))
- }
- }
- }
- final class Category: Identifiable, Codable {
- var name: String = ""
- var color: Color = Color(.gray)
- var id: String { name }
- init() {}
- init(name: String, color: Color) {
- self.name = name
- self.color = color
- }
- var valid: Bool { !name.isEmpty }
- }
|