diff --git a/DesignToolbox/DesignToolbox.xcworkspace/xcuserdata/xhrs0459.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/DesignToolbox/DesignToolbox.xcworkspace/xcuserdata/xhrs0459.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 48d5dd34f..42f14aa71 100644 --- a/DesignToolbox/DesignToolbox.xcworkspace/xcuserdata/xhrs0459.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/DesignToolbox/DesignToolbox.xcworkspace/xcuserdata/xhrs0459.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -3,4 +3,22 @@ uuid = "B4A15A02-0B7C-4A17-81BE-9CA389B02C0B" type = "0" version = "2.0"> + + + + + + diff --git a/DesignToolbox/DesignToolbox/Pages/Components/Checkbox/CheckboxConfiguration.swift b/DesignToolbox/DesignToolbox/Pages/Components/Checkbox/CheckboxConfiguration.swift index 5d329c995..25cef881e 100644 --- a/DesignToolbox/DesignToolbox/Pages/Components/Checkbox/CheckboxConfiguration.swift +++ b/DesignToolbox/DesignToolbox/Pages/Components/Checkbox/CheckboxConfiguration.swift @@ -21,7 +21,7 @@ final class CheckboxConfigurationModel: ComponentConfiguration { // MARK: - Properties - @Published var isEnabled: Bool { + @Published var status: DesignToolboxCheckboxStatus { didSet { updateCode() } } @@ -55,7 +55,27 @@ final class CheckboxConfigurationModel: ComponentConfiguration { // MARK: - Internal types - enum DesignToolboxCheckboxLayout: CaseIterable, CustomStringConvertible { // OUDSCheckbox.Layouy is not accessible + enum DesignToolboxCheckboxStatus: CaseIterable, CustomStringConvertible { // CheckboxInternalState is not accessible + case enabled + case disabled + case readOnly + + // No l10n, tehchnical names + var description: String { + switch self { + case .enabled: + "Enabled" + case .disabled: + "Disabled" + case .readOnly: + "Read only" + } + } + + var id: String { description } + } + + enum DesignToolboxCheckboxLayout: CaseIterable, CustomStringConvertible { // OUDSCheckbox.Layout is not accessible case selectorOnly case `default` case inverse @@ -78,7 +98,7 @@ final class CheckboxConfigurationModel: ComponentConfiguration { // MARK: - Initializer override init() { - isEnabled = true + status = .enabled selectorState = .selected layout = .selectorOnly helperText = true @@ -110,7 +130,7 @@ final class CheckboxConfigurationModel: ComponentConfiguration { } private var disableCode: String { - ".disable(\(isEnabled ? "false" : "true"))" + ".disable(\(status != .enabled ? "false" : "true"))" } private var helperTextPatern: String { @@ -134,7 +154,7 @@ final class CheckboxConfigurationModel: ComponentConfiguration { } private var isErrorPattern: String { - if isError && isEnabled { + if isError && status == .enabled { return ", isError: true" } else { return "" @@ -157,9 +177,11 @@ struct CheckboxConfiguration: View { var body: some View { VStack(alignment: .leading, spacing: theme.spaces.spaceFixedMedium) { - Toggle("app_common_enabled_label", isOn: $model.isEnabled) - .typeHeadingMedium(theme) - .foregroundStyle(theme.colors.colorContentDefault.color(for: colorScheme)) + DesignToolboxChoicePicker(title: "app_common_enabled_label", selection: $model.status) { + ForEach(CheckboxConfigurationModel.DesignToolboxCheckboxStatus.allCases, id: \.id) { state in + Text(LocalizedStringKey(state.description)).tag(state) + } + } DesignToolboxChoicePicker(title: "app_components_checkbox_selection_label", selection: $model.selectorState) { ForEach(OUDSCheckbox.SelectorState.allCases, id: \.id) { state in @@ -191,7 +213,7 @@ struct CheckboxConfiguration: View { Toggle("app_components_common_onError_label", isOn: $model.isError) .typeHeadingMedium(theme) .foregroundStyle(theme.colors.colorContentDefault.color(for: colorScheme)) - .disabled(!model.isEnabled) + .disabled(model.status != .enabled) DisclosureGroup("Edit texts") { DesignToolboxTextField(text: $model.labelContent, diff --git a/DesignToolbox/DesignToolbox/Pages/Components/Checkbox/CheckboxPage.swift b/DesignToolbox/DesignToolbox/Pages/Components/Checkbox/CheckboxPage.swift index ab9f4b090..eadaec2f5 100644 --- a/DesignToolbox/DesignToolbox/Pages/Components/Checkbox/CheckboxPage.swift +++ b/DesignToolbox/DesignToolbox/Pages/Components/Checkbox/CheckboxPage.swift @@ -77,8 +77,10 @@ private struct CheckboxDemo: View { if model.layout == .selectorOnly { HStack(alignment: .center) { Spacer() - OUDSCheckbox(state: $model.selectorState) - .disabled(!model.isEnabled) + OUDSCheckbox(state: $model.selectorState, + isError: model.isError && model.status == CheckboxConfigurationModel.DesignToolboxCheckboxStatus.enabled, + isReadOnly: model.status == CheckboxConfigurationModel.DesignToolboxCheckboxStatus.readOnly) + .disabled(isDisabled()) Spacer() } } else { @@ -87,9 +89,10 @@ private struct CheckboxDemo: View { helperText: helperTextContent, icon: icon, isInversed: model.layout == CheckboxConfigurationModel.DesignToolboxCheckboxLayout.inverse, - isError: model.isError && model.isEnabled, + isError: model.isError && model.status == CheckboxConfigurationModel.DesignToolboxCheckboxStatus.enabled, + isReadOnly: model.status == CheckboxConfigurationModel.DesignToolboxCheckboxStatus.readOnly, divider: model.divider) - .disabled(!model.isEnabled) + .disabled(isDisabled()) } } .padding(.all, theme.spaces.spaceFixedMedium) @@ -103,4 +106,8 @@ private struct CheckboxDemo: View { private var icon: Image? { model.icon ? Image(decorative: "ic_heart") : nil } + + private func isDisabled() -> Bool { + model.status == CheckboxConfigurationModel.DesignToolboxCheckboxStatus.disabled + } } diff --git a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabel.swift b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabel.swift index 0e3cb9009..ebb5c1862 100644 --- a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabel.swift +++ b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabel.swift @@ -93,7 +93,7 @@ struct OUDSCheckboxLabel: View { private var labelColor: Color { switch internalState { - case .enabled, .pressed, .hover: + case .enabled, .pressed, .hover, .readOnly: (items.isError ? theme.colors.colorContentStatusNegative : theme.colors.colorContentDefault) .color(for: colorScheme) case .disabled: @@ -103,7 +103,7 @@ struct OUDSCheckboxLabel: View { private var iconColor: Color { switch internalState { - case .enabled, .pressed, .hover: + case .enabled, .pressed, .hover, .readOnly: theme.colors.colorContentDefault.color(for: colorScheme) case .disabled: theme.colors.colorContentDisabled.color(for: colorScheme) @@ -112,7 +112,7 @@ struct OUDSCheckboxLabel: View { private var helperTextColor: Color { switch internalState { - case .enabled, .pressed, .hover: + case .enabled, .pressed, .hover, .readOnly: theme.colors.colorContentMuted.color(for: colorScheme) case .disabled: theme.colors.colorContentDisabled.color(for: colorScheme) diff --git a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabeledStyle.swift b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabeledStyle.swift index 3690d9e2c..ae6afdd15 100644 --- a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabeledStyle.swift +++ b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabeledStyle.swift @@ -23,6 +23,7 @@ struct OUDSCheckboxLabeledStyle: ButtonStyle { let selectorState: OUDSCheckbox.SelectorState let items: OUDSCheckboxLabel.Items let isInversed: Bool + let isReadOnly: Bool @State private var isHover: Bool = false @Environment(\.isEnabled) private var isEnabled @@ -72,12 +73,16 @@ struct OUDSCheckboxLabeledStyle: ButtonStyle { theme.select.selectColorBgHover.color(for: colorScheme) case .pressed: theme.select.selectColorBgPressed.color(for: colorScheme) - case .disabled: + case .disabled, .readOnly: theme.select.selectColorBgDisabled.color(for: colorScheme) } } private func internalState(isPressed: Bool) -> OUDSInternalCheckboxState { + if isReadOnly { + return .readOnly + } + if !isEnabled { return .disabled } diff --git a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxNestedStyle.swift b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxNestedStyle.swift index 0934abdf3..cc01c699e 100644 --- a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxNestedStyle.swift +++ b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxNestedStyle.swift @@ -22,6 +22,7 @@ struct OUDSCheckboxNestedStyle: ButtonStyle { let selectorState: OUDSCheckbox.SelectorState let isError: Bool + let isReadOnly: Bool @State private var isHover: Bool = false @Environment(\.isEnabled) private var isEnabled @@ -38,6 +39,10 @@ struct OUDSCheckboxNestedStyle: ButtonStyle { // MARK: - Helpers private func internalState(isPressed: Bool) -> OUDSInternalCheckboxState { + if isReadOnly { + return .readOnly + } + if !isEnabled { return .disabled } diff --git a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxSelectorButtonStyle.swift b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxSelectorButtonStyle.swift index 23e241d13..e8f0b8fa5 100644 --- a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxSelectorButtonStyle.swift +++ b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxSelectorButtonStyle.swift @@ -69,7 +69,7 @@ private struct CheckboxSelectorButtonForegroundModifier: ViewModifier { return hoverColor.color(for: colorScheme) case .pressed: return pressedColor.color(for: colorScheme) - case .disabled: + case .disabled, .readOnly: return disabledColor.color(for: colorScheme) } } @@ -134,7 +134,7 @@ private struct CheckboxSelectorButtonBackgroundModifier: ViewModifier { return hoverColor case .pressed: return pressedColor - case .disabled: + case .disabled, .readOnly: return disabledColor } } @@ -261,7 +261,7 @@ private struct CheckboxSelectorButtonBorderModifier: ViewModifier { return hoverColor case .pressed: return pressedColor - case .disabled: + case .disabled, .readOnly: return disabledColor } } @@ -313,7 +313,7 @@ private struct CheckboxSelectorButtonBorderModifier: ViewModifier { return hoverWidth case .pressed: return pressedWidth - case .disabled: + case .disabled, .readOnly: return disabledWidth } } diff --git a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSInternalCheckboxState.swift b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSInternalCheckboxState.swift index d726c6b46..1a76e824a 100644 --- a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSInternalCheckboxState.swift +++ b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSInternalCheckboxState.swift @@ -23,10 +23,12 @@ enum OUDSInternalCheckboxState { /// The component is being pressed case pressed - /// The user cannot interart with the component. Also "read only" cases. + /// The user cannot interart with the component. case disabled - // .loading not managed yet, for next version + /// The component is not disabled but user cannoit interact with it still. Almost enabled. + case readOnly + // .focus not managed as not that much customizable // .skeleton not managed as dedicated view in the end } diff --git a/OUDS/Core/Components/Sources/Checkbox/OUDSCheckbox.swift b/OUDS/Core/Components/Sources/Checkbox/OUDSCheckbox.swift index 786db9438..0b4f4e1ca 100644 --- a/OUDS/Core/Components/Sources/Checkbox/OUDSCheckbox.swift +++ b/OUDS/Core/Components/Sources/Checkbox/OUDSCheckbox.swift @@ -45,10 +45,14 @@ import SwiftUI /// // The nested layout will be used here. /// OUDSCheckbox(state: $state) /// -/// // A leading checkbox with a label +/// // A leading checkbox with a label. /// // The default layout will be used here. /// OUDSCheckbox(state: $state, label: "Hello world") /// +/// // A leading checkbox with a label, but in read only mode (user cannot interact yet, but not disabled). +/// // The default layout will be used here. +/// OUDSCheckbox(state: $state, label: "Hello world", isReadOnly: true) +/// /// // A leading checkbox with a label, and an helper text. /// // The default layout will be used here. /// OUDSCheckbox(state: $state, label: "Bazinga!", helperText: "Doll-Dagga Buzz-Buzz Ziggety-Zag") @@ -111,16 +115,18 @@ public struct OUDSCheckbox: View { /// The three available layouts for this component private enum Layout { - /// Displays only the checkbox selector, wiht a flag saying if there is an error context - case selectorOnly(Bool) + /// Displays only the checkbox selector, with a first flag saying if there is an error context and a second is in read only mode + case selectorOnly(Bool, Bool) /// Checkbox selector in leading position, icon in trailing position, like LTR mode. /// Details are defined in the ``OUDSCheckboxLabel.Items``. - case `default`(OUDSCheckboxLabel.Items) + /// Contains a flag saying if read only mode or not. + case `default`(OUDSCheckboxLabel.Items, Bool) /// Icon in leading position, checkbox selector in trailing position, like RTL mode /// Details are defined in the ``OUDSCheckboxLabel.Items``. - case inverse(OUDSCheckboxLabel.Items) + /// Contains a flag saying if read only mode or not. + case inverse(OUDSCheckboxLabel.Items, Bool) } // MARK: - Initializers @@ -130,9 +136,12 @@ public struct OUDSCheckbox: View { /// - Parameters: /// - state: A binding to a property that determines wether the selector is ticked, unticked or preticked. /// - isError: True if the look and feel of the component must reflect an error state, default set to `false` - public init(state: Binding, isError: Bool = false) { + /// - isReadOnly: True if component is in read only, i.e. not really disabled but user cannot interact with it yet, default set to `false` + public init(state: Binding, + isError: Bool = false, + isReadOnly: Bool = false) { self._state = state - self.layout = .selectorOnly(isError) + self.layout = .selectorOnly(isError, isReadOnly) } /// Creates a checkbox with label and optional helper text, icon, divider. @@ -144,6 +153,7 @@ public struct OUDSCheckbox: View { /// - icon: An optional icon /// - isInversed: `True` of the checkbox selector must be in trailing position,` false` otherwise. Default to `false` /// - isError: `True` if the look and feel of the component must reflect an error state, default set to `false` + /// - isReadOnly: True if component is in read only, i.e. not really disabled but user cannot interact with it yet, default set to `false` /// - divider: If `true` a divider is added at the bottom of the view. public init(state: Binding, label: String, @@ -151,12 +161,23 @@ public struct OUDSCheckbox: View { icon: Image? = nil, isInversed: Bool = false, isError: Bool = false, + isReadOnly: Bool = false, divider: Bool = false) { self._state = state if isInversed { - self.layout = .inverse(.init(label: label, helperText: helperText, icon: icon, isError: isError, divider: divider)) + self.layout = .inverse(.init(label: label, + helperText: helperText, + icon: icon, + isError: isError, + divider: divider), + isReadOnly) } else { - self.layout = .default(.init(label: label, helperText: helperText, icon: icon, isError: isError, divider: divider)) + self.layout = .default(.init(label: label, + helperText: helperText, + icon: icon, + isError: isError, + divider: divider), + isReadOnly) } } @@ -164,21 +185,27 @@ public struct OUDSCheckbox: View { public var body: some View { switch layout { - case .default(let label): + case let .default(label, isReadOnly): Button("") { - $state.wrappedValue.toggle() + if !isReadOnly { + $state.wrappedValue.toggle() + } } - .buttonStyle(OUDSCheckboxLabeledStyle(selectorState: $state.wrappedValue, items: label, isInversed: false)) - case .inverse(let label): + .buttonStyle(OUDSCheckboxLabeledStyle(selectorState: $state.wrappedValue, items: label, isInversed: false, isReadOnly: isReadOnly)) + case let .inverse(label, isReadOnly): Button("") { - $state.wrappedValue.toggle() + if !isReadOnly { + $state.wrappedValue.toggle() + } } - .buttonStyle(OUDSCheckboxLabeledStyle(selectorState: $state.wrappedValue, items: label, isInversed: true)) - case .selectorOnly(let isError): + .buttonStyle(OUDSCheckboxLabeledStyle(selectorState: $state.wrappedValue, items: label, isInversed: true, isReadOnly: isReadOnly)) + case let .selectorOnly(isError, isReadOnly): Button("") { - $state.wrappedValue.toggle() + if !isReadOnly { + $state.wrappedValue.toggle() + } } - .buttonStyle(OUDSCheckboxNestedStyle(selectorState: $state.wrappedValue, isError: isError)) + .buttonStyle(OUDSCheckboxNestedStyle(selectorState: $state.wrappedValue, isError: isError, isReadOnly: isReadOnly)) } } }