Add Quo component library base
Add text component Add header component Change animated root ns Add spacing design tokens Add components preview Add Readme Add safe area components Use reanimated from component library Add colors tokens Add inherit style for text Add animated header component A view with header which can be animated on scroll, used in walled, profile and contact screens Persist previews navigation state Rename component library into Quo Document colors Extend readme Header handle long titles Add InterStatus as monospace font Signed-off-by: Gheorghe Pinzaru <feross95@gmail.com>
This commit is contained in:
parent
0a4547e652
commit
38125173cb
Binary file not shown.
|
@ -90,6 +90,7 @@
|
|||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = StatusIm/main.m; sourceTree = "<group>"; };
|
||||
1426DF592BA248FC81D955CB /* Inter-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-Regular.otf"; path = "../resources/fonts/Inter-Regular.otf"; sourceTree = "<group>"; };
|
||||
38A44830EC5708E89387F641 /* Pods-StatusIm.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StatusIm.release.xcconfig"; path = "Pods/Target Support Files/Pods-StatusIm/Pods-StatusIm.release.xcconfig"; sourceTree = "<group>"; };
|
||||
3A0B103024581B74004B0F23 /* InterStatus-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "InterStatus-Regular.otf"; path = "../resources/fonts/InterStatus-Regular.otf"; sourceTree = "<group>"; };
|
||||
439B6B4B407A4E2AACAFE5BE /* RCTStatus.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTStatus.xcodeproj; path = "../modules/react-native-status/ios/RCTStatus/RCTStatus.xcodeproj"; sourceTree = "<group>"; };
|
||||
4C16DE0B1F89508700AA10DB /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
4E586E1B0E544F64AA9F5BD1 /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
|
@ -180,6 +181,7 @@
|
|||
CD4A2C27D6D5473184DC1F7E /* Inter-Bold.otf */,
|
||||
B321D25F4493470980039457 /* Inter-BoldItalic.otf */,
|
||||
B07176ACDAA1422E8F0A3D6B /* Inter-Italic.otf */,
|
||||
3A0B103024581B74004B0F23 /* InterStatus-Regular.otf */,
|
||||
B2A38FC3D3954DE7B2B171F8 /* Inter-Medium.otf */,
|
||||
C6B1215047604CD59A4C74D6 /* Inter-MediumItalic.otf */,
|
||||
1426DF592BA248FC81D955CB /* Inter-Regular.otf */,
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,49 @@
|
|||
# Status Quo Components
|
||||
All components in **Quo** should be independent of the app state. They should be pure,
|
||||
and easy to reason about. This is required to make the library independent and
|
||||
easily pulled off as a separate repository when needed.
|
||||
Do avoid high coupling and direct use of internal styling, the components should be
|
||||
exported via namespace `quo.core` and used by status app only from here. This will
|
||||
allow a more flexible way to update components without possible breakages into the
|
||||
app style.
|
||||
|
||||
**Quo** components should and not have any dependency on the status app, this
|
||||
will avoid circular dependency and also benefit the independence of the components.
|
||||
|
||||
All components are stored inside `components` namespaces. They are stateless and do
|
||||
not dispatch and subscribe to re-frame database. All state should be passed by props
|
||||
and all events can be passed as functions. Avoiding direct connection with re-frame
|
||||
will allow components to grow and be reused in different places without the
|
||||
conditionals hell.
|
||||
|
||||
All style system constants are stored inside `design-system` namespaces. They are used
|
||||
to build components and can be directly required by the status app. Avoid
|
||||
duplication of these vars and do not use them in code directly as a value.
|
||||
|
||||
For each component introduced, add previews of all possible states.
|
||||
|
||||
Do not introduce components for slightly modified existing components, if they are
|
||||
not a part of the design system. In case they are required in one place in the app,
|
||||
use style override.
|
||||
|
||||
# Code style
|
||||
Ensure that your changes match the style of the rest of the code.
|
||||
This library uses Clojure Community code style [The Clojure Style Guide](https://github.com/bbatsov/clojure-style-guide)
|
||||
To ensure consistency run [clj-kondo linter](https://github.com/borkdude/clj-kondo)
|
||||
|
||||
# Best practices
|
||||
|
||||
- Desing components atomically and compose them into bigger components.
|
||||
- Do not export individual atoms, only components. This way we can limit design
|
||||
system to be used in too many way which can creating disjointed experiences.
|
||||
- Avoid external margins for atom components, it can be added on the wrapper
|
||||
where they are used but can't be removed without overriding.
|
||||
[Max Stoiber article on margins](https://mxstbr.com/thoughts/margin)
|
||||
- Design reusable components into [Layout Isolated Components](https://visly.app/blog/layout-isolated-components)
|
||||
(Article more relates to web, but ideas fits also to mobile dev**
|
||||
- Explicit is better than implicit, do not rely on platform default, if you expect
|
||||
a specific value, then override it
|
||||
|
||||
**TBD:**
|
||||
- Components documentation
|
||||
- Check props using spec in pre conditions.
|
|
@ -1,4 +1,4 @@
|
|||
(ns status-im.ui.components.reanimated
|
||||
(ns quo.animated
|
||||
(:refer-clojure :exclude [set])
|
||||
(:require [reagent.core :as reagent]
|
||||
[oops.core :refer [oget ocall]]
|
||||
|
@ -101,45 +101,6 @@
|
|||
|
||||
(def extrapolate {:clamp (oget animated "Extrapolate" "CLAMP")})
|
||||
|
||||
;; Gesture handler
|
||||
|
||||
(def tap-gesture-handler
|
||||
(reagent/adapt-react-class
|
||||
(oget js-deps/react-native-gesture-handler "TapGestureHandler")))
|
||||
|
||||
(def pan-gesture-handler
|
||||
(reagent/adapt-react-class
|
||||
(oget js-deps/react-native-gesture-handler "PanGestureHandler")))
|
||||
|
||||
(def long-press-gesture-handler
|
||||
(reagent/adapt-react-class
|
||||
(oget js-deps/react-native-gesture-handler "LongPressGestureHandler")))
|
||||
|
||||
(def pure-native-button (oget js-deps/react-native-gesture-handler "PureNativeButton"))
|
||||
|
||||
(def touchable-without-feedback-class
|
||||
(oget js-deps/react-native-gesture-handler "TouchableWithoutFeedback"))
|
||||
|
||||
(def createNativeWrapper
|
||||
(oget js-deps/react-native-gesture-handler "createNativeWrapper"))
|
||||
|
||||
(def touchable-without-feedback
|
||||
(reagent/adapt-react-class touchable-without-feedback-class))
|
||||
|
||||
(def animated-raw-button
|
||||
(reagent/adapt-react-class
|
||||
(createNativeWrapper
|
||||
(createAnimatedComponent touchable-without-feedback-class))))
|
||||
|
||||
(def state (oget js-deps/react-native-gesture-handler "State"))
|
||||
|
||||
(def states {:began (oget state "BEGAN")
|
||||
:active (oget state "ACTIVE")
|
||||
:cancelled (oget state "CANCELLED")
|
||||
:end (oget state "END")
|
||||
:failed (oget state "FAILED")
|
||||
:undetermined (oget state "UNDETERMINED")})
|
||||
|
||||
;; utilities
|
||||
|
||||
(def redash js-deps/react-native-redash)
|
|
@ -0,0 +1,82 @@
|
|||
(ns quo.components.animated-header
|
||||
(:require [oops.core :refer [oget]]
|
||||
[quo.animated :as animated]
|
||||
[quo.components.header :as header]
|
||||
[quo.components.safe-area :as safe-area]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.platform :as platform]
|
||||
[quo.react-native :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn header-wrapper-style [{:keys [value offset]}]
|
||||
(merge
|
||||
{:background-color :white}
|
||||
(when (and offset platform/android?)
|
||||
{:elevation (animated/interpolate
|
||||
value
|
||||
{:inputRange [0 offset]
|
||||
:outputRange [0 4]
|
||||
:extrapolate (:clamp animated/extrapolate)})})
|
||||
(when (and offset platform/ios?)
|
||||
{:shadow-opacity (animated/interpolate
|
||||
value
|
||||
{:inputRange [0 offset]
|
||||
:outputRange [0 1]
|
||||
:extrapolate (:clamp animated/extrapolate)})
|
||||
:shadow-radius 16
|
||||
:z-index 2
|
||||
:shadow-color (:shadow-01 @colors/theme)
|
||||
:shadow-offset {:width 0 :height 4}})))
|
||||
|
||||
(defn header-opened-style [{:keys [value offset]}]
|
||||
(merge
|
||||
{:position :absolute
|
||||
:top 0
|
||||
:left 0
|
||||
:right 0}
|
||||
(when offset
|
||||
{:transform [{:translateY
|
||||
(animated/interpolate
|
||||
value
|
||||
{:inputRange [0 offset]
|
||||
:outputRange [0 (- header/header-height)]
|
||||
:extrapolateRight (:clamp animated/extrapolate)})}]})))
|
||||
|
||||
(defn header-container []
|
||||
(let [y (animated/value 0)
|
||||
on-scroll (animated/on-scroll {:y y})
|
||||
layout (reagent/atom {})
|
||||
offset (reagent/atom 0)
|
||||
on-layout (fn [evt]
|
||||
(reset! offset (oget evt "nativeEvent" "layout" "height")))]
|
||||
(fn [{:keys [extended-header] :as props} & children]
|
||||
[animated/view {:flex 1
|
||||
:pointer-events :box-none}
|
||||
[animated/view {:pointer-events :box-none
|
||||
:style (header-wrapper-style {:value y
|
||||
:offset @offset})}
|
||||
[header/header (merge {:get-layout (fn [el l] (swap! layout assoc el l))}
|
||||
(dissoc props :extended-header))]
|
||||
[rn/view {:pointer-events :box-none}
|
||||
[animated/view {:style (header-opened-style {:value y
|
||||
:offset @offset})
|
||||
:pointer-events :box-none
|
||||
:on-layout on-layout}
|
||||
[extended-header {:value y
|
||||
:layout @layout
|
||||
:offset @offset}]]]]
|
||||
(into [animated/scroll-view {:on-scroll on-scroll
|
||||
:scrollEventThrottle 1}
|
||||
[rn/view {:pointer-events :box-none
|
||||
:height @offset}]]
|
||||
children)])))
|
||||
|
||||
(defn header [{:keys [use-insets] :as props} & children]
|
||||
(if use-insets
|
||||
[safe-area/consumer
|
||||
(fn [insets]
|
||||
[header-container (-> props
|
||||
(dissoc :use-insets)
|
||||
(assoc :insets insets))
|
||||
children])]
|
||||
[header-container props children]))
|
|
@ -0,0 +1,175 @@
|
|||
(ns quo.components.header
|
||||
(:require [oops.core :refer [oget]]
|
||||
[quo.animated :as animated]
|
||||
[quo.components.text :as text]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.design-system.spacing :as spacing]
|
||||
[quo.react :as react]
|
||||
[quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.ui.components.icons.vector-icons :as icons]))
|
||||
|
||||
(def header-height 56)
|
||||
|
||||
(defn header-wrapper-style [{:keys [height border-bottom]}]
|
||||
(merge
|
||||
{:background-color (:ui-background @colors/theme)
|
||||
:height height}
|
||||
(when border-bottom
|
||||
{:border-bottom-width 1
|
||||
:border-bottom-color (:ui-02 @colors/theme)})))
|
||||
|
||||
(def absolute-fill {:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0})
|
||||
|
||||
(def content {:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :center})
|
||||
|
||||
(def left {:position :absolute
|
||||
:left 0
|
||||
:top 0
|
||||
:bottom 0
|
||||
:justify-content :center
|
||||
:align-items :flex-start})
|
||||
|
||||
(def right {:position :absolute
|
||||
:right 0
|
||||
:top 0
|
||||
:bottom 0
|
||||
:justify-content :center
|
||||
:align-items :flex-end})
|
||||
|
||||
(defn title-style [{:keys [left right]} title-align]
|
||||
(merge
|
||||
{:position :absolute
|
||||
:justify-content :center
|
||||
:top 0
|
||||
:bottom 0}
|
||||
(:tiny spacing/padding-horizontal)
|
||||
(case title-align
|
||||
:left {:left (:width left)
|
||||
:right (:width right)}
|
||||
{:align-items :center
|
||||
:left (max (:width left) (:width right))
|
||||
:right (max (:width left) (:width right))})))
|
||||
|
||||
(def header-actions-style
|
||||
(merge
|
||||
{:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :center}
|
||||
(:tiny spacing/padding-horizontal)))
|
||||
|
||||
(def header-action-placeholder
|
||||
{:width (:tiny spacing/spacing)})
|
||||
|
||||
(def header-icon-touchable
|
||||
(merge
|
||||
{:flex 1
|
||||
:align-items :center
|
||||
:justify-content :center}
|
||||
(:tiny spacing/padding-horizontal)))
|
||||
|
||||
(def element {:align-items :center
|
||||
:justify-content :center
|
||||
:flex 1})
|
||||
|
||||
(defn header-action [{:keys [icon label on-press accessibility-label]}]
|
||||
[rn/touchable-opacity {:on-press on-press}
|
||||
[rn/view (merge {:style header-icon-touchable}
|
||||
(when accessibility-label
|
||||
{:accessibility-label accessibility-label}))
|
||||
(cond
|
||||
icon [icons/icon icon]
|
||||
label [text/text {:color :link} label])]])
|
||||
|
||||
(defn header-actions [{:keys [accessories component]}]
|
||||
[rn/view {:style element}
|
||||
(cond
|
||||
(seq accessories)
|
||||
(into [rn/view {:style header-actions-style}]
|
||||
(map header-action accessories))
|
||||
|
||||
component component
|
||||
|
||||
:else
|
||||
[rn/view {:style header-action-placeholder}])])
|
||||
|
||||
(defn header-title [{:keys [title subtitle component title-align]}]
|
||||
[react/fragment
|
||||
(cond
|
||||
component component
|
||||
|
||||
(and title subtitle)
|
||||
[react/fragment
|
||||
[text/text {:weight :medium
|
||||
:number-of-lines 1}
|
||||
title]
|
||||
[text/text {:weight :regular
|
||||
:number-of-lines 1}
|
||||
subtitle]]
|
||||
|
||||
title [text/text {:weight :bold
|
||||
:number-of-lines 2
|
||||
:align title-align
|
||||
:size :large}
|
||||
title])])
|
||||
|
||||
(defn header []
|
||||
(let [layout (reagent/atom {:left {:width 8
|
||||
:height header-height}
|
||||
:right {:width 8
|
||||
:height header-height}
|
||||
:title {:width 0
|
||||
:height header-height}})
|
||||
handle-layout (fn [el get-layout]
|
||||
(fn [evt]
|
||||
(let [width (oget evt "nativeEvent" "layout" "width")
|
||||
height (oget evt "nativeEvent" "layout" "height")]
|
||||
(when get-layout
|
||||
(get-layout el {:width width
|
||||
:height height}))
|
||||
(swap! layout assoc el {:width width
|
||||
:height height}))))]
|
||||
(fn [{:keys [left-accessories left-component border-bottom
|
||||
right-accessories right-component insets get-layout
|
||||
title subtitle title-component style title-align]
|
||||
:or {title-align :center}}]
|
||||
(let [status-bar-height (get insets :top 0)
|
||||
height (+ header-height status-bar-height)]
|
||||
[animated/view {:style (header-wrapper-style {:height height
|
||||
:border-bottom border-bottom})}
|
||||
[rn/view {:pointer-events :box-none
|
||||
:height status-bar-height}]
|
||||
[rn/view {:style (merge {:height header-height}
|
||||
style)
|
||||
:pointer-events :box-none}
|
||||
[rn/view {:style absolute-fill
|
||||
:pointer-events :box-none}
|
||||
[rn/view {:style content
|
||||
:pointer-events :box-none}
|
||||
[rn/view {:style left
|
||||
:on-layout (handle-layout :left get-layout)
|
||||
:pointer-events :box-none}
|
||||
[header-actions {:accessories left-accessories
|
||||
:component left-component}]]
|
||||
|
||||
[rn/view {:style (title-style @layout title-align)
|
||||
:on-layout (handle-layout :title get-layout)
|
||||
:pointer-events :box-none}
|
||||
[header-title {:title title
|
||||
:subtitle subtitle
|
||||
:title-align title-align
|
||||
:component title-component}]]
|
||||
|
||||
[rn/view {:style right
|
||||
:on-layout (handle-layout :right get-layout)
|
||||
:pointer-events :box-none}
|
||||
[header-actions {:accessories right-accessories
|
||||
:component right-component}]]]]]]))))
|
|
@ -0,0 +1,14 @@
|
|||
(ns quo.components.safe-area
|
||||
(:require [status-im.react-native.js-dependencies :refer [safe-area-context]]
|
||||
[reagent.core :as reagent]
|
||||
[oops.core :refer [oget]]))
|
||||
|
||||
(def provider (reagent/adapt-react-class (oget safe-area-context "SafeAreaProvider")))
|
||||
(def ^:private consumer-raw (reagent/adapt-react-class (oget safe-area-context "SafeAreaConsumer")))
|
||||
(def view (reagent/adapt-react-class (oget safe-area-context "SafeAreaView")))
|
||||
|
||||
(defn consumer [component]
|
||||
[consumer-raw
|
||||
(fn [insets]
|
||||
(reagent/as-element
|
||||
[component (js->clj insets :keywordize-keys true)]))])
|
|
@ -0,0 +1,47 @@
|
|||
(ns quo.components.text
|
||||
(:require [quo.animated :as animated]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.design-system.typography :as typography]
|
||||
[quo.react-native :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn text-style [{:keys [size align weight color style]
|
||||
:or {size :base
|
||||
weight :regular
|
||||
align :auto
|
||||
color :main}}]
|
||||
(merge (case weight
|
||||
:regular typography/font-regular
|
||||
:medium typography/font-medium
|
||||
:semi-bold typography/font-semi-bold
|
||||
:bold typography/font-bold
|
||||
:monospace typography/monospace
|
||||
:inherit nil)
|
||||
(case color
|
||||
:main {:color (:text-01 @colors/theme)}
|
||||
:secondary {:color (:text-02 @colors/theme)}
|
||||
:secondary-inverse {:color (:text-03 @colors/theme)}
|
||||
:link {:color (:text-04 @colors/theme)}
|
||||
:positive {:color (:positive-01 @colors/theme)}
|
||||
:negative {:color (:negative-01 @colors/theme)}
|
||||
:inherit nil)
|
||||
(case size
|
||||
:tiny typography/tiny
|
||||
:small typography/small
|
||||
:base typography/base
|
||||
:large typography/large
|
||||
:x-large typography/x-large
|
||||
:xx-large typography/xx-large
|
||||
:inherit nil)
|
||||
{:text-align align}
|
||||
style))
|
||||
|
||||
(defn text []
|
||||
(let [this (reagent/current-component)
|
||||
props (reagent/props this)
|
||||
component (if (:animated? props) animated/text rn/text)]
|
||||
(into [component (merge {:style (text-style props)}
|
||||
(dissoc props
|
||||
:style :size :weight :color
|
||||
:align :animated?))]
|
||||
(reagent/children this))))
|
|
@ -0,0 +1,13 @@
|
|||
(ns quo.core
|
||||
(:require [quo.components.animated-header :as animated-header]
|
||||
[quo.components.header :as header]
|
||||
[quo.components.safe-area :as safe-area]
|
||||
[quo.components.text :as text]))
|
||||
|
||||
(def text text/text)
|
||||
(def header header/header)
|
||||
(def animated-header animated-header/header)
|
||||
|
||||
(def safe-area-provider safe-area/provider)
|
||||
(def safe-area-consumer safe-area/consumer)
|
||||
(def safe-area-view safe-area/view)
|
|
@ -0,0 +1,65 @@
|
|||
(ns quo.design-system.colors
|
||||
(:require [reagent.core :as reagent]))
|
||||
|
||||
(def white "#FFFFFF")
|
||||
(def black "#000000")
|
||||
|
||||
;; Colors mapping from figma to code, note that theme is more extended and
|
||||
;; one can follow the comments from the light theme to choose what to use in a component.
|
||||
(comment
|
||||
{"Accent blue, #4360DF" [:interactive-01 :text-04]
|
||||
"Accent blue as background, #ECEFFC" [:interactive-02]
|
||||
"Dark grey, #939BA1" [:text-02 :icon-02]
|
||||
"Black" [:text-01 :icon-01]
|
||||
"Main Green/Success, #4EBC60" [:positive-01]
|
||||
"Shades 10% green, #EDFBEF" [:positive-02]
|
||||
"Main Red/Error, #FF2D55" [:negative-01]
|
||||
"Shades 10% Red, #FFEAEE" [:negative-02]
|
||||
"Light grey, #EEF2F5" [:ui-01]
|
||||
"White, #FFFFFF" [:ui-background :icon-04]
|
||||
"Devider, 0.1 of black" [:ui-02]})
|
||||
|
||||
(def light-theme
|
||||
{:positive-01 "rgba(68,208,88,1)" ; Primary Positive, text, icons color
|
||||
:positive-02 "rgba(78,188,96,0.1)" ; Secondary Positive, Supporting color for success illustrations
|
||||
:negative-01 "rgba(255,45,85,1)" ; Primary Negative, text, icons color
|
||||
:negative-02 "rgba(255,45,85,0.1))" ; Secondary Negative, Supporting color for errors illustrations
|
||||
:interactive-01 "rgba(67,96,223,1)" ; Accent color, buttons, own message, actions,active state
|
||||
:interactive-02 "rgba(236,239,252,1)" ; Light Accent, buttons background, actions background, messages
|
||||
:interactive-03 "rgba(255,255,255,0.1)" ; Background for interactive above accent
|
||||
:ui-background "rgba(255,255,255,1)" ; Default view background
|
||||
:ui-01 "rgba(238,242,245,1)" ; Secondary background
|
||||
:ui-02 "rgba(0,0,0,0.1)" ; Deviders
|
||||
:text-01 "rgba(0,0,0,1)" ; Main text color
|
||||
:text-02 "rgba(147,155,161,1)" ; Secondary text
|
||||
:text-03 "rgba(255,255,255,0.7)" ; Secondary on accent
|
||||
:text-04 "rgba(67,96,223,1)" ; Links text color
|
||||
:icon-01 "rgba(0,0,0,1)" ; Primary icons
|
||||
:icon-02 "rgba(147,155,161,1)" ; Secondary icons
|
||||
:icon-03 "rgba(255,255,255,0.4)" ; Secondary icons on accent bg
|
||||
:icon-04 "rgba(255,255,255,1)" ; Icons inverse on accent background
|
||||
:shadow-01 "rgba(0,9,26,0.12)" ; Main shadow color
|
||||
})
|
||||
|
||||
(def dark-theme
|
||||
{:positive-01 "rgba(68,208,88,1)"
|
||||
:positive-02 "rgba(78,188,96,0.1)"
|
||||
:negative-01 "rgba(252,95,95,1)"
|
||||
:negative-02 "rgba(252,95,95,0.1)"
|
||||
:interactive-01 "rgba(97,119,229,1)"
|
||||
:interactive-02 "rgba(35,37,47,1)"
|
||||
:interactive-03 "rgba(255,255,255,0.1)"
|
||||
:ui-background "rgba(20,20,20,1)"
|
||||
:ui-01 "rgba(37,37,40,1)"
|
||||
:ui-02 "rgba(0,0,0,0.1)"
|
||||
:text-01 "rgba(255,255,255,1)"
|
||||
:text-02 "rgba(131,140,145,1)"
|
||||
:text-03 "rgba(255,255,255,0.7)"
|
||||
:text-04 "rgba(97,119,229,1)"
|
||||
:icon-01 "rgba(255,255,255,1)"
|
||||
:icon-02 "rgba(131,140,145,1)"
|
||||
:icon-03 "rgba(255,255,255,0.4)"
|
||||
:icon-04 "rgba(20,20,20,1)"
|
||||
:shadow-01 "rgba(0,0,0,0.75)"})
|
||||
|
||||
(def theme (reagent/atom light-theme))
|
|
@ -0,0 +1,19 @@
|
|||
(ns quo.design-system.spacing)
|
||||
|
||||
(def spacing {:x-tiny 4
|
||||
:tiny 8
|
||||
:small 12
|
||||
:base 16
|
||||
:large 24
|
||||
:x-large 32
|
||||
:xx-large 48})
|
||||
|
||||
(def padding-horizontal (reduce-kv (fn [m k v]
|
||||
(assoc m k {:padding-horizontal v}))
|
||||
{}
|
||||
spacing))
|
||||
|
||||
(def padding-vertical (reduce-kv (fn [m k v]
|
||||
(assoc m k {:padding-vertical v}))
|
||||
{}
|
||||
spacing))
|
|
@ -0,0 +1,30 @@
|
|||
(ns quo.design-system.typography
|
||||
(:require [quo.platform :as platform]))
|
||||
|
||||
(def tiny {:font-size 10
|
||||
:line-height 14})
|
||||
|
||||
(def small {:font-size 13
|
||||
:line-height 18})
|
||||
|
||||
(def base {:font-size 15
|
||||
:line-height 22})
|
||||
|
||||
(def large {:font-size 17
|
||||
:line-height 24})
|
||||
|
||||
(def x-large {:font-size 22
|
||||
:line-height 30})
|
||||
|
||||
(def xx-large {:font-size 28
|
||||
:line-height 38})
|
||||
|
||||
(def font-regular {:font-family "Inter-Regular"}) ; 400
|
||||
|
||||
(def font-medium {:font-family "Inter-Medium"}) ; 500 ff
|
||||
|
||||
(def font-semi-bold {:font-family "Inter-SemiBold"}) ; 600
|
||||
|
||||
(def font-bold {:font-family "Inter-Bold"}) ; 700
|
||||
|
||||
(def monospace {:font-family "InterStatus-Regular"})
|
|
@ -0,0 +1,42 @@
|
|||
(ns quo.gesture-handler
|
||||
(:require [oops.core :refer [oget]]
|
||||
[quo.animated :as animated]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.react-native.js-dependencies :as js-deps]))
|
||||
|
||||
(def tap-gesture-handler
|
||||
(reagent/adapt-react-class
|
||||
(oget js-deps/react-native-gesture-handler "TapGestureHandler")))
|
||||
|
||||
(def pan-gesture-handler
|
||||
(reagent/adapt-react-class
|
||||
(oget js-deps/react-native-gesture-handler "PanGestureHandler")))
|
||||
|
||||
(def long-press-gesture-handler
|
||||
(reagent/adapt-react-class
|
||||
(oget js-deps/react-native-gesture-handler "LongPressGestureHandler")))
|
||||
|
||||
(def pure-native-button (oget js-deps/react-native-gesture-handler "PureNativeButton"))
|
||||
|
||||
(def touchable-without-feedback-class
|
||||
(oget js-deps/react-native-gesture-handler "TouchableWithoutFeedback"))
|
||||
|
||||
(def createNativeWrapper
|
||||
(oget js-deps/react-native-gesture-handler "createNativeWrapper"))
|
||||
|
||||
(def touchable-without-feedback
|
||||
(reagent/adapt-react-class touchable-without-feedback-class))
|
||||
|
||||
(def animated-raw-button
|
||||
(reagent/adapt-react-class
|
||||
(createNativeWrapper
|
||||
(animated/createAnimatedComponent touchable-without-feedback-class))))
|
||||
|
||||
(def state (oget js-deps/react-native-gesture-handler "State"))
|
||||
|
||||
(def states {:began (oget state "BEGAN")
|
||||
:active (oget state "ACTIVE")
|
||||
:cancelled (oget state "CANCELLED")
|
||||
:end (oget state "END")
|
||||
:failed (oget state "FAILED")
|
||||
:undetermined (oget state "UNDETERMINED")})
|
|
@ -0,0 +1,7 @@
|
|||
(ns quo.platform
|
||||
(:require [quo.react-native :as rn]))
|
||||
|
||||
(def os (when rn/platform (.-OS rn/platform)))
|
||||
|
||||
(def android? (= os "android"))
|
||||
(def ios? (= os "ios"))
|
|
@ -0,0 +1,32 @@
|
|||
(ns quo.previews.header
|
||||
(:require [quo.core :as quo]
|
||||
[quo.react-native :as rn]))
|
||||
|
||||
(def accessories [nil
|
||||
[{:icon :main-icons/close
|
||||
:on-press identity}]
|
||||
[{:icon :main-icons/close
|
||||
:on-press identity}
|
||||
{:icon :main-icons/add
|
||||
:on-press identity}]
|
||||
[{:icon :main-icons/add
|
||||
:on-press identity}
|
||||
{:label "Text"
|
||||
:on-press identity}]
|
||||
[{:label "Text"
|
||||
:on-press identity}]])
|
||||
|
||||
(defn preview-header []
|
||||
[rn/scroll-view {:flex 1}
|
||||
(for [left-accessories accessories
|
||||
right-accessories accessories
|
||||
title [nil "This is a title" "This is a very long super title"]
|
||||
subtitle [nil "This is a subtitle"]
|
||||
title-align [:left :center]]
|
||||
[rn/view {:border-bottom-color "#EEF2F5"
|
||||
:border-bottom-width 2}
|
||||
[quo/header {:left-accessories left-accessories
|
||||
:right-accessories right-accessories
|
||||
:title title
|
||||
:subtitle subtitle
|
||||
:title-align title-align}]])])
|
|
@ -0,0 +1,53 @@
|
|||
(ns quo.previews.main
|
||||
(:require [oops.core :refer [ocall]]
|
||||
[quo.previews.header :as header]
|
||||
[quo.previews.text :as text]
|
||||
[quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.react-native.js-dependencies :refer [react-native]]
|
||||
[status-im.ui.screens.routing.core :as navigation]))
|
||||
|
||||
(def screens [{:name :texts
|
||||
:insets {:top false}
|
||||
:component text/preview-text}
|
||||
{:name :headers
|
||||
:insets {:top false}
|
||||
:component header/preview-header}])
|
||||
|
||||
(defn main-screen []
|
||||
[rn/scroll-view {:flex 1
|
||||
:padding-vertical 8
|
||||
:padding-horizontal 16}
|
||||
[rn/view
|
||||
(for [{:keys [name]} screens]
|
||||
[rn/touchable-opacity {:on-press #(navigation/navigate-to name nil)}
|
||||
[rn/view {:style {:padding-vertical 8}}
|
||||
[rn/text (str "Preview " name)]]])]])
|
||||
|
||||
(defonce navigation-state (atom nil))
|
||||
|
||||
(defn- persist-state! [state-obj]
|
||||
(js/Promise.
|
||||
(fn [resolve _]
|
||||
(reset! navigation-state state-obj)
|
||||
(resolve true))))
|
||||
|
||||
(defn preview-screens []
|
||||
(let [stack (navigation/create-stack)]
|
||||
[navigation/navigation-container
|
||||
{:ref navigation/set-navigator-ref
|
||||
:initial-state @navigation-state
|
||||
:on-state-change persist-state!}
|
||||
[stack {}
|
||||
(into [{:name :main
|
||||
:insets {:top false}
|
||||
:component main-screen}]
|
||||
screens)]]))
|
||||
|
||||
;; TODO(Ferossgp): Add separate build when shadow-cljs will be integrated
|
||||
;; NOTE(Ferossgp): Separate app can be used to preview all available
|
||||
;; and possible state for componetns, and for UI testing based on screenshots
|
||||
(defn init []
|
||||
(ocall react-native ["AppRegistry" "registerComponent"]
|
||||
"StatusIm"
|
||||
#(reagent/reactify-component preview-screens)))
|
|
@ -0,0 +1,14 @@
|
|||
(ns quo.previews.text
|
||||
(:require [quo.core :as quo]
|
||||
[quo.react-native :as rn]))
|
||||
|
||||
(defn preview-text []
|
||||
[rn/scroll-view {:flex 1
|
||||
:padding-horizontal 16}
|
||||
(for [size [:tiny :small :base :large :x-large :xx-large]
|
||||
weight [:regular :medium :semi-bold :bold :monospace]]
|
||||
^{:key (str)}
|
||||
[rn/view {:padding-vertical 16}
|
||||
[quo/text {:weight weight
|
||||
:size size}
|
||||
(str "Text size " size ", font weight " weight)]])])
|
|
@ -0,0 +1,7 @@
|
|||
(ns quo.react
|
||||
(:require [oops.core :refer [oget]]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.react-native.js-dependencies :refer [react]]))
|
||||
|
||||
;; NOTE(Ferossgp): Available in new versions of reagent as `:<>`
|
||||
(def fragment (reagent/adapt-react-class (oget react "Fragment")))
|
|
@ -0,0 +1,15 @@
|
|||
(ns quo.react-native
|
||||
(:require [oops.core :refer [oget]]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.react-native.js-dependencies :refer [react-native]]))
|
||||
|
||||
(def platform (oget react-native "Platform"))
|
||||
|
||||
(def view (reagent/adapt-react-class (oget react-native "View")))
|
||||
|
||||
(def text (reagent/adapt-react-class (oget react-native "Text")))
|
||||
|
||||
(def scroll-view (reagent/adapt-react-class (oget react-native "ScrollView")))
|
||||
|
||||
(def touchable-opacity (reagent/adapt-react-class (oget react-native "TouchableOpacity")))
|
||||
(def touchable-highlight (reagent/adapt-react-class (oget react-native "TouchableHighlight")))
|
|
@ -0,0 +1,7 @@
|
|||
(ns quo.theme
|
||||
(:require [quo.design-system.colors :as colors]))
|
||||
|
||||
(defn set-theme [theme]
|
||||
(reset! colors/theme (case theme
|
||||
:dark colors/dark-theme
|
||||
colors/light-theme)))
|
|
@ -1,15 +1,16 @@
|
|||
(ns status-im.init.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
(:require [clojure.string :as string]
|
||||
[quo.theme :as quo-theme]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.multiaccounts.login.core :as multiaccounts.login]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.network.net-info :as network]
|
||||
[status-im.react-native.js-dependencies :as rn-dependencies]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.screens.db :refer [app-db]]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.platform :as platform]
|
||||
[clojure.string :as string]
|
||||
[status-im.utils.theme :as theme]
|
||||
[status-im.ui.components.colors :as colors]))
|
||||
[status-im.utils.theme :as theme]))
|
||||
|
||||
(defn restore-native-settings! []
|
||||
(when platform/desktop?
|
||||
|
@ -95,4 +96,5 @@
|
|||
(fn []
|
||||
(theme/add-mode-change-listener #(re-frame/dispatch [:system-theme-mode-changed %]))
|
||||
(when (theme/is-dark-mode)
|
||||
(colors/set-theme :dark))))
|
||||
(quo-theme/set-theme :dark)
|
||||
(colors/set-theme :dark))))
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
(ns status-im.multiaccounts.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
(:require [quo.theme :as quo-theme]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ethereum.stateofus :as stateofus]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.native-module.core :as native-module]
|
||||
[status-im.notifications.core :as notifications]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.handlers]
|
||||
[status-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.gfycat.core :as gfycat]
|
||||
status-im.utils.handlers
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.utils.theme :as theme]))
|
||||
|
||||
(defn displayed-name
|
||||
|
@ -101,15 +102,16 @@
|
|||
|
||||
(re-frame/reg-fx
|
||||
::switch-theme
|
||||
(fn [theme]
|
||||
(colors/set-theme
|
||||
(if (or (= 2 theme) (and (= 0 theme) (theme/is-dark-mode)))
|
||||
:dark
|
||||
:light))))
|
||||
(fn [theme-id]
|
||||
(let [theme (if (or (= 2 theme-id) (and (= 0 theme-id) (theme/is-dark-mode)))
|
||||
:dark
|
||||
:light)]
|
||||
(quo-theme/set-theme theme)
|
||||
(colors/set-theme theme))))
|
||||
|
||||
(fx/defn switch-appearance
|
||||
{:events [:multiaccounts.ui/appearance-switched]}
|
||||
[cofx theme]
|
||||
(fx/merge cofx
|
||||
{::switch-theme theme}
|
||||
(multiaccounts.update/multiaccount-update :appearance theme {})))
|
||||
(multiaccounts.update/multiaccount-update :appearance theme {})))
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
(ns status-im.ui.components.tabbar.core
|
||||
(:require
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[status-im.ui.components.reanimated :as reanimated]
|
||||
[status-im.ui.components.tabbar.styles :as tabs.styles]
|
||||
[reagent.core :as reagent]
|
||||
[oops.core :refer [oget]]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
||||
[status-im.ui.components.badge :as badge]
|
||||
[status-im.i18n :as i18n]
|
||||
[re-frame.core :as re-frame]))
|
||||
(:require [oops.core :refer [oget]]
|
||||
[quo.gesture-handler :as gesture-handler]
|
||||
[re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[status-im.ui.components.badge :as badge]
|
||||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.tabbar.styles :as tabs.styles]
|
||||
[status-im.utils.platform :as platform]))
|
||||
|
||||
(defonce visible-native (animation/create-value 0))
|
||||
(defonce last-to-value (atom 1))
|
||||
|
@ -69,7 +68,7 @@
|
|||
accessibility-label count-subscription]}]
|
||||
(let [count (when count-subscription @(re-frame/subscribe [count-subscription]))]
|
||||
[react/view {:style tabs.styles/touchable-container}
|
||||
[reanimated/touchable-without-feedback
|
||||
[gesture-handler/touchable-without-feedback
|
||||
{:style {:height "100%"
|
||||
:width "100%"}
|
||||
:on-press on-press
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
(ns status-im.ui.screens.wallet.accounts.styles
|
||||
(:require [status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.reanimated :as reanimated]
|
||||
(:require [quo.animated :as reanimated]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.utils.platform :as platform]))
|
||||
|
||||
(def ^:const tabbar-height 56)
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
(ns status-im.ui.screens.wallet.accounts.views
|
||||
(:require-macros [status-im.utils.views :as views])
|
||||
(:require [status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.icons.vector-icons :as icons]
|
||||
[status-im.ui.components.toolbar.styles :as toolbar.styles]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.ui.components.list-item.views :as list-item]
|
||||
[status-im.wallet.utils :as wallet.utils]
|
||||
[reagent.core :as reagent]
|
||||
(:require [oops.core :refer [oget]]
|
||||
[quo.animated :as reanimated]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ui.components.reanimated :as reanimated]
|
||||
[oops.core :refer [oget]]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.icons.vector-icons :as icons]
|
||||
[status-im.ui.components.list-item.views :as list-item]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.toolbar.styles :as toolbar.styles]
|
||||
[status-im.ui.screens.wallet.accounts.sheets :as sheets]
|
||||
[status-im.ui.screens.wallet.accounts.styles :as styles]
|
||||
[status-im.utils.utils :as utils.utils]))
|
||||
[status-im.utils.utils :as utils.utils]
|
||||
[status-im.wallet.utils :as wallet.utils])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(def state (reagent/atom {:tab :assets}))
|
||||
|
||||
|
|
Loading…
Reference in New Issue