Browse Source

Maintenance: Desktop - Align form field toggle with the agreed design.

Dusan Vuckovic 1 year ago
parent
commit
a2cacaf538

+ 2 - 1
.cypress/utils.ts

@@ -4,7 +4,7 @@ import { merge } from 'lodash-es'
 import initializeStore from '#shared/stores/index.ts'
 import initializeGlobalComponents from '#shared/initializer/globalComponents.ts'
 import initializeGlobalProperties from '#shared/initializer/globalProperties.ts'
-import initializeForm from '#mobile/form/index.ts'
+import { initializeForm, initializeFormFields } from '#mobile/form/index.ts'
 
 // imported only for types
 // for some reason adding it to tsconfig doesn't work
@@ -38,6 +38,7 @@ export const mountComponent: typeof mount = (
   plugins.push(initializeGlobalComponents)
   plugins.push(initializeGlobalProperties)
   plugins.push(initializeForm)
+  plugins.push(initializeFormFields)
   plugins.push((app: App) => router.install(app))
 
   return cy.mount(component, merge({ global: { plugins } }, options))

+ 1 - 1
app/frontend/apps/desktop/components/Form/fields/FieldSelect/FieldSelectInput.vue

@@ -115,7 +115,7 @@ setupMissingOrDisabledOptionHandling()
     ref="input"
     :class="[
       context.classes.input,
-      'flex h-auto min-h-10 bg-blue-200 dark:bg-gray-700 duration-200 has-[output:focus,input:focus]:outline has-[output:focus,input:focus]:outline-1 has-[output:focus,input:focus]:outline-offset-1 has-[output:focus,input:focus]:outline-blue-800',
+      `flex h-auto min-h-10 bg-blue-200 dark:bg-gray-700 hover:outline hover:outline-1 hover:outline-offset-1 hover:outline-blue-600 dark:hover:outline-blue-900 has-[output:focus,input:focus]:outline has-[output:focus,input:focus]:outline-1 has-[output:focus,input:focus]:outline-offset-1 has-[output:focus,input:focus]:outline-blue-800 dark:has-[output:focus,input:focus]:outline-blue-800`,
       {
         'rounded-lg': !select?.isOpen,
         'rounded-t-lg': select?.isOpen && !isBelowHalfScreen,

+ 10 - 2
app/frontend/apps/desktop/form/index.ts

@@ -9,6 +9,7 @@ import type {
   InitializeAppForm,
 } from '#shared/types/form.ts'
 import type { ImportGlobEagerOutput } from '#shared/types/utils.ts'
+import { initializeToggleClasses } from '#shared/components/Form/fields/FieldToggle/initializeToggleClasses.ts'
 import { getCoreDesktopClasses } from './theme/global/getCoreDesktopClasses.ts'
 
 const pluginModules: ImportGlobEagerOutput<FormKitPlugin> = import.meta.glob(
@@ -20,7 +21,7 @@ export const desktopFormFieldModules: ImportGlobEagerOutput<FormFieldTypeImportM
 const themeExtensionModules: ImportGlobEagerOutput<FormThemeExtension> =
   import.meta.glob('./theme/global/extensions/*.ts', { eager: true })
 
-const initializeForm: InitializeAppForm = (app: App) => {
+export const initializeForm: InitializeAppForm = (app: App) => {
   const plugins = getFormPlugins(pluginModules)
   const theme = {
     coreClasses: getCoreDesktopClasses,
@@ -42,4 +43,11 @@ const initializeForm: InitializeAppForm = (app: App) => {
   )
 }
 
-export default initializeForm
+export const initializeFormFields = () => {
+  initializeToggleClasses({
+    track:
+      'bg-stone-200 dark:bg-gray-500 ring-1 ring-neutral-100 dark:ring-gray-900 hover:outline hover:outline-1 hover:outline-offset-2 hover:outline-blue-600 dark:hover:outline-blue-900 focus:outline focus:outline-1 focus:outline-offset-2 focus:outline-blue-800 hover:focus:outline-blue-800 dark:hover:focus:outline-blue-800 formkit-invalid:outline formkit-invalid:outline-1 formkit-invalid:outline-offset-2 formkit-invalid:outline-red-500 dark:hover:formkit-invalid:outline-red-500 formkit-errors:outline formkit-errors:outline-1 formkit-errors:outline-offset-2 formkit-errors:outline-red-500 dark:hover:formkit-errors:outline-red-500',
+    trackOn: '!bg-blue-800',
+    knob: 'bg-white',
+  })
+}

+ 5 - 0
app/frontend/apps/desktop/form/theme/global/getCoreDesktopClasses.ts

@@ -69,5 +69,10 @@ export const getCoreDesktopClasses: FormThemeExtension = (
       inner:
         'w-full formkit-invalid:outline formkit-invalid:outline-1 formkit-invalid:outline-offset-1 formkit-invalid:outline-red-500 formkit-errors:outline formkit-errors:outline-1 formkit-errors:outline-offset-1 formkit-errors:outline-red-500',
     }),
+    toggle: extendClasses(classes.toggle, {
+      wrapper: 'h-10 flex flex-row-reverse items-center gap-1.5',
+      label: '!mb-0 grow',
+      inner: 'h-6',
+    }),
   }
 }

+ 2 - 1
app/frontend/apps/desktop/main.ts

@@ -19,7 +19,7 @@ import { initializeDesktopIcons } from '#desktop/initializer/initializeDesktopIc
 import { initializeGlobalComponentStyles } from '#desktop/initializer/initializeGlobalComponentStyles.ts'
 import initializeApolloClient from '#desktop/server/apollo/index.ts'
 import initializeRouter from '#desktop/router/index.ts'
-import initializeForm from '#desktop/form/index.ts'
+import { initializeForm, initializeFormFields } from '#desktop/form/index.ts'
 
 import { ensureAfterAuth } from './pages/authentication/after-auth/composable/useAfterAuthPlugins.ts'
 
@@ -33,6 +33,7 @@ export const mountApp = async () => {
   initializeStore(app)
   initializeDesktopIcons()
   initializeForm(app)
+  initializeFormFields()
   initializeGlobalComponentStyles()
   initializeGlobalComponents(app)
   initializeAppName('desktop')

+ 42 - 0
app/frontend/apps/desktop/pages/home/views/Playground.vue

@@ -76,6 +76,48 @@ const formSchema = [
       options: iconOptions.value,
     },
   },
+  {
+    isLayout: true,
+    element: 'div',
+    attrs: {
+      class: 'grid grid-cols-2 gap-y-2.5 gap-x-3',
+    },
+    children: [
+      {
+        name: 'text_1',
+        label: 'Text input',
+        type: 'text',
+        outerClass: 'col-span-1',
+        props: {
+          maxLength: 150,
+        },
+      },
+      {
+        name: 'toggle_1',
+        label: 'Column toggle',
+        type: 'toggle',
+        outerClass: 'col-span-1',
+        wrapperClass: 'mt-6',
+        props: {
+          variants: {
+            true: 'yes',
+            false: 'no',
+          },
+        },
+      },
+      {
+        name: 'toggle_2',
+        label: 'Row toggle',
+        type: 'toggle',
+        props: {
+          variants: {
+            true: 'yes',
+            false: 'no',
+          },
+        },
+      },
+    ],
+  },
 ]
 </script>
 

+ 10 - 2
app/frontend/apps/mobile/form/index.ts

@@ -9,6 +9,7 @@ import type {
   InitializeAppForm,
 } from '#shared/types/form.ts'
 import type { ImportGlobEagerOutput } from '#shared/types/utils.ts'
+import { initializeToggleClasses } from '#shared/components/Form/fields/FieldToggle/initializeToggleClasses.ts'
 import getCoreClasses from './theme/global/getCoreMobileClasses.ts'
 
 const pluginModules: ImportGlobEagerOutput<FormKitPlugin> = import.meta.glob(
@@ -20,7 +21,7 @@ export const mobileFormFieldModules: ImportGlobEagerOutput<FormFieldTypeImportMo
 const themeExtensionModules: ImportGlobEagerOutput<FormThemeExtension> =
   import.meta.glob('./theme/global/extensions/*.ts', { eager: true })
 
-const initializeForm: InitializeAppForm = (app: App) => {
+export const initializeForm: InitializeAppForm = (app: App) => {
   const plugins = getFormPlugins(pluginModules)
   const theme = {
     coreClasses: getCoreClasses,
@@ -30,4 +31,11 @@ const initializeForm: InitializeAppForm = (app: App) => {
   mainInitializeForm(app, undefined, mobileFormFieldModules, plugins, theme)
 }
 
-export default initializeForm
+export const initializeFormFields = () => {
+  initializeToggleClasses({
+    track:
+      'bg-gray-300 border border-transparent focus-within:ring-1 focus-within:ring-white focus-within:ring-opacity-75 focus:outline-none formkit-invalid:border-solid formkit-invalid:border-red',
+    trackOn: '!bg-blue',
+    knob: 'bg-white shadow-lg ring-0',
+  })
+}

+ 2 - 1
app/frontend/apps/mobile/initialize.ts

@@ -8,7 +8,7 @@ import initializeStore from '#shared/stores/index.ts'
 import initializeGlobalComponents from '#shared/initializer/globalComponents.ts'
 import { initializeAppName } from '#shared/composables/useAppName.ts'
 import initializeGlobalProperties from '#shared/initializer/globalProperties.ts'
-import initializeForm from '#mobile/form/index.ts'
+import { initializeForm, initializeFormFields } from '#mobile/form/index.ts'
 import { initializeMobileVisuals } from './initializer/mobileVisuals.ts'
 import { initializeMobileIcons } from './initializer/initializeMobileIcons.ts'
 import { initializeGlobalComponentStyles } from './initializer/initializeGlobalComponentStyles.ts'
@@ -21,6 +21,7 @@ export default function initializeApp(app: App) {
   initializeGlobalProperties(app)
   initializeMobileIcons()
   initializeForm(app)
+  initializeFormFields()
   initializeMobileVisuals()
 
   return app

+ 20 - 7
app/frontend/shared/components/Form/fields/FieldToggle/FieldToggleInput.vue

@@ -1,9 +1,10 @@
 <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
 
 <script setup lang="ts">
-import stopEvent from '#shared/utils/events.ts'
 import { computed, nextTick, toRef, watch } from 'vue'
+import stopEvent from '#shared/utils/events.ts'
 import useValue from '../../composables/useValue.ts'
+import { getToggleClasses } from './initializeToggleClasses.ts'
 import type { FormFieldContext } from '../../types/field.ts'
 
 const props = defineProps<{
@@ -76,6 +77,8 @@ const updateLocalValue = (e: Event) => {
     localValue.value = newValue === 'true'
   }
 }
+
+const classMap = getToggleClasses()
 </script>
 
 <template>
@@ -83,8 +86,14 @@ const updateLocalValue = (e: Event) => {
     :id="context.id"
     type="button"
     role="switch"
-    class="relative inline-flex h-6 w-10 flex-shrink-0 cursor-pointer rounded-full border border-transparent bg-gray-300 transition-colors duration-200 ease-in-out focus-within:ring-1 focus-within:ring-white focus-within:ring-opacity-75 focus:outline-none formkit-invalid:border-solid formkit-invalid:border-red"
-    :class="[context.classes.input, { '!bg-blue': localValue }]"
+    class="relative inline-flex items-center h-6 w-10 flex-shrink-0 cursor-pointer rounded-full transition-colors duration-200 ease-in-out"
+    :class="[
+      context.classes.input,
+      classMap.track,
+      {
+        [classMap.trackOn]: localValue,
+      },
+    ]"
     :aria-labelledby="`label-${context.id}`"
     :aria-disabled="disabled"
     :aria-checked="localValue"
@@ -93,10 +102,14 @@ const updateLocalValue = (e: Event) => {
     @keydown.space="updateLocalValue"
   >
     <div
-      class="pointer-events-none inline-block h-[22px] w-[22px] transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out ltr:translate-x-0 rtl:-translate-x-0"
-      :class="{
-        'ltr:translate-x-4 rtl:-translate-x-4': localValue,
-      }"
+      class="pointer-events-none inline-block h-[22px] w-[22px] transform rounded-full transition duration-200 ease-in-out"
+      :class="[
+        classMap.knob,
+        {
+          'ltr:translate-x-px rtl:-translate-x-px': !localValue,
+          'ltr:translate-x-[17px] rtl:-translate-x-[17px]': localValue,
+        },
+      ]"
     ></div>
   </button>
 </template>

+ 16 - 0
app/frontend/shared/components/Form/fields/FieldToggle/initializeToggleClasses.ts

@@ -0,0 +1,16 @@
+// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+import type { ToggleClassMap } from './types.ts'
+
+// Provide your own map with the following keys, the values given here are just examples.
+let toggleClasses: ToggleClassMap = {
+  track: 'field-toggle-track',
+  trackOn: 'field-toggle-track--on',
+  knob: 'field-toggle-knob',
+}
+
+export const initializeToggleClasses = (classes: ToggleClassMap) => {
+  toggleClasses = classes
+}
+
+export const getToggleClasses = () => toggleClasses

Some files were not shown because too many files changed in this diff