forked from MapComplete/MapComplete
		
	Chore: remove obsolete documentation, add explanation
This commit is contained in:
		
							parent
							
								
									58c4dd7cfc
								
							
						
					
					
						commit
						e09af8fcb9
					
				
					 2 changed files with 1 additions and 222 deletions
				
			
		|  | @ -1,221 +0,0 @@ | |||
| Architecture | ||||
| ============ | ||||
| 
 | ||||
| This document aims to give an architectural overview of how MapComplete is built. It should give some feeling on how | ||||
| everything fits together. | ||||
| 
 | ||||
| Servers? | ||||
| -------- | ||||
| 
 | ||||
| There are no servers for MapComplete, all services are configured by third parties. | ||||
| 
 | ||||
| Minimal HTML - Minimal CSS | ||||
| -------------------------- | ||||
| 
 | ||||
| There is quasi no HTML. Most of the components are generated by TypeScript and attached dynamically. The HTML is a | ||||
| barebones skeleton which serves every theme. | ||||
| 
 | ||||
| 
 | ||||
| The UIEventSource | ||||
| ----------------- | ||||
| 
 | ||||
| Most (but not all) objects in MapComplete get all the state they need as a parameter in the constructor. However, as is | ||||
| the case with most graphical applications, there are quite some dynamical values. | ||||
| 
 | ||||
| All values which change regularly are wrapped into | ||||
| a [`UIEventSource`](../Logic/UIEventSource.ts). A `UIEventSource` is a | ||||
| wrapper containing a value and offers the possibility to add a callback function which is called every time the value is | ||||
| changed (with `setData`) | ||||
| 
 | ||||
| Furthermore, there are various helper functions, the most widely used one being `map` - generating a new event source | ||||
| with the new value applied. Note that `map` will also absorb some changes, | ||||
| e.g. `const someEventSource : UIEventSource<string[]> = ... ; someEventSource.map(list = list.length)` will only trigger | ||||
| when the length of the list has changed. | ||||
| 
 | ||||
| An object which receives a `UIEventSource` is responsible of responding to changes of this object. This is especially | ||||
| true for UI-components. | ||||
| 
 | ||||
| UI | ||||
| -- | ||||
| ```typescript | ||||
| 
 | ||||
| export default class MyComponent { | ||||
| 
 | ||||
|     constructor(neededParameters, neededUIEventSources) { | ||||
|      | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| The Graphical User Interface is composed of various UI-elements. For every UI-element, there is a `BaseUIElement` which creates the actual `HTMLElement` when needed. | ||||
| 
 | ||||
| There are some basic elements, such as: | ||||
| 
 | ||||
| - `FixedUIElement` which shows a fixed, unchangeable element | ||||
| - `Img` to show an image | ||||
| - `Combine` which wraps everything given (strings and other elements) in a div | ||||
| - `List` | ||||
| 
 | ||||
| There is one special component: the `VariableUIElement` | ||||
| The `VariableUIElement` takes a `UIEventSource<string|BaseUIElement>` and will dynamically show whatever the `UIEventSource` contains at the moment. | ||||
| 
 | ||||
| For example: | ||||
| 
 | ||||
| ```typescript | ||||
| 
 | ||||
| const src : UIEventSource<string> = ... // E.g. user input, data that will be updated... new VariableUIElement(src) | ||||
| .AttachTo('some-id') // attach it to the html | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| Note that every component offers support for `onClick( someCallBack)` | ||||
| 
 | ||||
| ### Translations | ||||
| 
 | ||||
| To add a translation: | ||||
| 
 | ||||
| 1. Open `langs/en.json` | ||||
| 2. Find a correct spot for your translation in the tree | ||||
| 3. run `npm run generate:translations` | ||||
| 4. `import Translations` | ||||
| 5. `Translations.t.<your-translation>.Clone()` is the `UIElement` offering your translation | ||||
| 
 | ||||
| ### Input elements | ||||
| 
 | ||||
| Input elements are a special kind of BaseElement which offer a piece of a form to the user, e.g. a TextField, a Radio button, a dropdown, ... | ||||
| 
 | ||||
| The constructor will ask all the parameters to configure them. The actual value can be obtained via `inputElement.GetValue()`, which is a `UIEventSource` that will be triggered every time the user changes the input. | ||||
| 
 | ||||
| ### Advanced elements | ||||
| 
 | ||||
| There are some components which offer useful functionality: | ||||
| 
 | ||||
| 
 | ||||
| - The `subtleButton` which is a friendly, big button | ||||
| - The Toggle: `const t = new Toggle( componentA, componentB, source)` is a `UIEventSource` which shows `componentA` as long as `source` contains `true` and will show `componentB` otherwise. | ||||
| 
 | ||||
| 
 | ||||
| ### Styling | ||||
| 
 | ||||
| Styling is done as much as possible with [TailwindCSS](https://tailwindcss.com/). It contains a ton of utility classes, each of them containing a few rules. | ||||
| 
 | ||||
| For example: ` someBaseUIElement.SetClass("flex flex-col border border-black rounded-full")` will set the component to be a flex object, as column, with a black border and pill-shaped. | ||||
| 
 | ||||
| If Tailwind is not enough, use `baseUiElement.SetStyle("background: red; someOtherCssRule: abc;")`. | ||||
| 
 | ||||
| ### An example | ||||
| 
 | ||||
| For example: the user should input whether or not a shop is closed during public holidays. There are three options: | ||||
| 
 | ||||
| 1. closed | ||||
| 2. opened as usual | ||||
| 3. opened with different hours as usual | ||||
| 
 | ||||
| In the case of different hours, input hours should be too. | ||||
| 
 | ||||
| This can be constructed as following: | ||||
| 
 | ||||
| 
 | ||||
| ```typescript | ||||
| 
 | ||||
|     // We construct the dropdown element with values and labelshttps://tailwindcss.com/ | ||||
|     const isOpened = new Dropdown<string>(Translations.t.is_this_shop_opened_during_holidays, | ||||
|         [ | ||||
|          { value: "closed", Translation.t.shop_closed_during_holidays.Clone()}, | ||||
|          { value: "open", Translations.t.shop_opened_as_usual.Clone()}, | ||||
|          { value: "hours", Translations.t.shop_opened_with_other_hours.Clone()} | ||||
|         ] ) | ||||
|          | ||||
|      const startHour = new DateInput(...)drop | ||||
|      const endHour = new DateInput( ... )    | ||||
|      // We construct a toggle which'll only show the extra questions if needed | ||||
|      const extraQuestion = new Toggle( | ||||
|             new Combine([Translations.t.openFrom, startHour, Translations.t.openTill, endHour]), | ||||
|             undefined, | ||||
|             isOpened.GetValue().map(isopened => isopened === "hours") | ||||
|          ) | ||||
| 
 | ||||
|     return new Combine([isOpened, extraQuestion]) | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| ### Constructing a special class | ||||
| 
 | ||||
| If you make a specialized class to offer a certain functionality, you can organize it as following: | ||||
| 
 | ||||
| 1. Create a new class: | ||||
| 
 | ||||
| ```typescript | ||||
| 
 | ||||
| export default class MyComponent { | ||||
| 
 | ||||
|     constructor(neededParameters, neededUIEventSources) { | ||||
|      | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| 2. Construct the needed UI in the constructor | ||||
| 
 | ||||
| 
 | ||||
| ```typescript | ||||
| 
 | ||||
| export default class MyComponent { | ||||
| 
 | ||||
|     constructor(neededParameters, neededUIEventSources) { | ||||
|      | ||||
|      | ||||
|             const component = ... | ||||
|             const toggle = ... | ||||
|             ... other components ... | ||||
|              | ||||
|             toggle.GetValue.AddCallbackAndRun(isSelected => { .. some actions ... } | ||||
|              | ||||
|             new Combine([everything, ...] ) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| 3. You'll notice that you'll end up with one certain component (in this example the combine) to wrap it all together. Change the class to extend this type of component and use `super()` to wrap it all up: | ||||
| 
 | ||||
| 
 | ||||
| ```typescript | ||||
| 
 | ||||
| export default class MyComponent extends Combine { | ||||
| 
 | ||||
|     constructor(...) { | ||||
|      | ||||
|         ... | ||||
|         super([everything, ...]) | ||||
|      | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| Assets | ||||
| ------ | ||||
| 
 | ||||
| ### Themes | ||||
| 
 | ||||
| Theme and layer configuration files go into `assets/layers` and `assets/themes`. | ||||
| 
 | ||||
| ### Images | ||||
| 
 | ||||
| Other files (mostly images that are part of the core of MapComplete) go into `assets/svg` and are usable with `Svg.image_file_ui()`. Run `npm run generate:images` if you added a new image. | ||||
| 
 | ||||
| 
 | ||||
| Logic | ||||
| ----- | ||||
| 
 | ||||
| The last part is the business logic of the application, found in the directory [Logic](../Logic). Actors are small objects which react to `UIEventSources` to update other eventSources. | ||||
| 
 | ||||
| `State.state` is a big singleton object containing a lot of the state of the entire application. That one is a bit of a mess. | ||||
| 
 | ||||
|  | @ -392,7 +392,7 @@ export class ChangesetHandler { | |||
|         return [ | ||||
|             ["created_by", `MapComplete ${Constants.vNumber}`], | ||||
|             ["locale", Locale.language.data], | ||||
|             ["host", `${window.location.origin}${window.location.pathname}`], | ||||
|             ["host", `${window.location.origin}${window.location.pathname}`], // Note: deferred changes might give a different hostpath then the theme with which the changes were made
 | ||||
|             ["source", setSourceAsSurvey ? "survey" : undefined], | ||||
|             ["imagery", this.changes.state["backgroundLayer"]?.data?.id], | ||||
|         ].map(([key, value]) => ({ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue