0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-08 07:50:43 -05:00
penpot/docs/02-Frontend-Developer-Guide.md

284 lines
7.5 KiB
Markdown
Raw Normal View History

2019-12-09 10:28:15 -05:00
# Frontend Guide #
This guide intends to explain the essential details of the frontend
application.
2020-01-11 10:28:54 -05:00
2020-04-28 04:54:10 -05:00
## Visual debug mode and utilities
Debugging a problem in the viewport algorithms for grouping and
rotating is difficult. We have set a visual debug mode that displays
some annotations on screen, to help understanding what's happening.
2020-04-28 04:54:10 -05:00
To activate it, open the javascript console and type
```javascript
2020-08-18 12:40:49 -05:00
app.util.debug.toggle_debug("option")
2020-04-28 04:54:10 -05:00
```
Current options are `bounding-boxes`, `group`, `events` and
`rotation-handler`.
2020-04-28 04:54:10 -05:00
You can also activate or deactivate all visual aids with
```javascript
2020-08-18 12:40:49 -05:00
app.util.debug.debug_all()
app.util.debug.debug_none()
2020-04-28 04:54:10 -05:00
```
## Logging, Tracing & Debugging
As a traditional way for debugging and tracing you have the followimg approach:
Print data to the devtool console using clojurescript helper:
**prn**. This helper automatically formats the clojure and js data
structures as plain EDN for easy visual inspection of the data and the
type of the data.
```clojure
(prn "message" expression)
```
An alternative is using the pprint function, usefull for pretty
printing a medium-big data sturcture for completly understand it.
```clojure
(:require [cljs.pprint :refer [pprint]])
(pprint expression)
; Outputs a clojure value as a string, nicely formatted and with data type information.
```
Use the js native functions for printing data. The clj->js converts
the clojure data sturcture to js data sturcture and it is
inspeccionable in the devtools console.
```clojure
(js/console.log "message" (clj->js expression))
```
Also we can insert breakpoints in the code with this function:
```clojure
(js-debugger)
```
You can also set a breakpoint from the sources tab in devtools. One
way of locating a source file is to output a trace with
(js/console.log) and then clicking in the source link that shows in
the console.
### Logging framework
Additionally to the traditional way of putting traces in the code, we
have a logging framework with steroids. It is usefull for casual
debugging (as replacement for a `prn` and `js/console.log`) and as a
permanent traces in the code.
You have the ability to specify the logging level per namespace and
all logging is ellided in production build.
Lets start with a simple example:
```clojure
(ns some.ns
(:require [app.util.logging :as log]))
;; This function sets the level to the current namespace; messages
;; with level behind this will not be printed.
(log/set-level! :info)
;; Log some data; The `app.util.logging` has the following
;; functions/macros:
(log/error :msg "error message")
(log/warn :msg "warn message")
(log/info :msg "info message")
(log/debug :msg "debug message")
(log/trace :msg "trace message")
```
Each macro accept arbitrary number of key values pairs:
```clojure
(log/info :foo "bar" :msg "test" :value 1 :items #{1 2 3})
```
Some keys ara treated as special cases for helping in debugging:
```clojure
;; The special case for :js/whatever; if you namespace the key
;; with `js/`, the variable will be printed as javascript
;; inspectionable object.
(let [foobar {:a 1 :b 2}]
(log/info :msg "Some data" :js/data foobar))
;; The special case for `:err`; If you attach this key, the
;; exception stack trace is printed as additional log entry.
```
## Access to clojure from javascript console
2020-10-28 03:34:47 -05:00
The penpot namespace of the main application is exported, so that is
accessible from javascript console in Chrome developer tools. Object
names and data types are converted to javascript style. For example
you can emit the event to reset zoom level by typing this at the
console (there is autocompletion for help):
```javascript
app.main.store.emit_BANG_(app.main.data.workspace.reset_zoom)
```
2020-04-27 07:31:57 -05:00
## Debug state and objects
There are also some useful functions to visualize the global state or
any complex object. To use them from clojure:
2020-04-27 07:31:57 -05:00
2020-04-28 04:54:10 -05:00
```clojure
2020-08-18 12:40:49 -05:00
(ns app.util.debug)
2020-04-28 04:58:33 -05:00
(logjs <msg> <var>) ; to print the value of a variable
(tap <fn>) ; to include a function with side effect (e.g. logjs) in a transducer.
2020-04-27 07:31:57 -05:00
2020-08-18 12:40:49 -05:00
(ns app.main.store)
2020-04-27 07:31:57 -05:00
(dump-state) ; to print in console all the global state
(dump-objects) ; to print in console all objects in workspace
2020-04-28 04:54:10 -05:00
```
2020-01-11 10:28:54 -05:00
But last ones are most commonly used from javscript console:
```javascript
2020-08-18 12:40:49 -05:00
app.main.store.dump_state()
app.main.store.dump_objects()
```
And we have also exported `pprint` and `clj->js` functions for the console:
```javascript
pp(js_expression) // equivalent to cljs.pprint.pprint(js_expression)
dbg(js_expression) // equivalent to cljs.core.clj__GT_js(js_expression)
```
2020-01-11 10:28:54 -05:00
2020-03-08 07:13:32 -05:00
## Icons & Assets
The icons used on the frontend application are loaded using svgsprite
(properly handled by the gulp watch task). All icons should be on SVG
format located in `resources/images/icons`. The gulp task will
generate the sprite and the embedd it into the `index.html`.
Then, you can reference the icon from the sprite using the
2020-08-18 12:40:49 -05:00
`app.builtins.icons/icon-xref` macro:
2020-03-08 07:13:32 -05:00
```clojure
(ns some.namespace
2020-08-18 12:40:49 -05:00
(:require-macros [app.main.ui.icons :refer [icon-xref]]))
2020-03-08 07:13:32 -05:00
(icon-xref :arrow)
```
For performance reasons, all used icons are statically defined in the
2020-08-18 12:40:49 -05:00
`src/app/main/ui/icons.cljs` file.
2020-03-08 07:13:32 -05:00
2020-01-11 10:28:54 -05:00
## Translations (I18N) ##
### How it Works ###
All the translation strings of this application are stored in
`resources/locales.json` file. It has a self explanatory format that
looks like this:
```json
{
"auth.email-or-username" : {
2020-08-18 12:40:49 -05:00
"used-in" : [ "src/app/main/ui/auth/login.cljs:61" ],
2020-01-11 10:28:54 -05:00
"translations" : {
"en" : "Email or Username",
"fr" : "adresse email ou nom d'utilisateur"
}
},
"ds.num-projects" : {
"translations": {
"en": ["1 project", "%s projects"]
}
},
}
```
For development convenience, you can forget about the specific format
of that file, and just add a simple key-value entry pairs like this:
```
{
[...],
"foo1": "bar1",
"foo2": "bar2"
}
```
The file is automatically bundled into the `index.html` file on
compile time (in development and production). The bundled content is a
simplified version of this data structure for avoid load unnecesary
data.
The development environment has a watch process that detect changes on
that file and recompiles the `index.html`. **There are no hot reload
for translations strings**, you just need to refresh the browser tab
for refresh the translations in the running the application.
If you have used the short (key-value) format, the watch process will
automatically convert it to the apropriate format before generate the
`index.html`.
Finally, when you have finished to adding texts, execute the following
command for reformat the file, and track the usage locations (the
"used-in" list) before commiting the file into the repository:
```bash
2020-08-18 12:40:49 -05:00
clojure -Adev locales.clj collect src/app/main/ resources/locales.json
2020-01-11 10:28:54 -05:00
```
NOTE: Later, we will need to think and implement the way to export and
import to other formats (mainly for transifex and similar services
compatibility).
### How to use it ###
You have two aproaches for translate strings: one for general purpose
and other specific for React components (that leverages reactivity for
language changes).
2020-08-18 12:40:49 -05:00
The `app.util.i18n/tr` is the general purpose function. This is a
2020-01-11 10:28:54 -05:00
simple use case example:
```clojure
2020-08-18 12:40:49 -05:00
(require '[app.util.i18n :refer [tr])
2020-01-11 10:28:54 -05:00
2020-01-16 13:28:19 -05:00
(tr "auth.email-or-username")
2020-01-11 10:28:54 -05:00
;; => "Email or Username"
```
If you have defined plurals for some translation resource, then you
need to pass an additional parameter marked as counter in order to
allow the system know when to show the plural:
```clojure
2020-08-18 12:40:49 -05:00
(require '[app.util.i18n :as i18n :refer [tr]])
2020-01-11 10:28:54 -05:00
2020-01-16 13:28:19 -05:00
(tr "ds.num-projects" (i18n/c 10))
2020-01-11 10:28:54 -05:00
;; => "10 projects"
2020-01-16 13:28:19 -05:00
(tr "ds.num-projects" (i18n/c 1))
2020-01-11 10:28:54 -05:00
;; => "1 project"
```