mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
Merge main
This commit is contained in:
commit
0c60f9fc7f
@ -1,2 +1,3 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
|
examples/
|
||||||
43
.eslintrc.js
43
.eslintrc.js
@ -12,7 +12,7 @@ module.exports = {
|
|||||||
'plugin:import/recommended',
|
'plugin:import/recommended',
|
||||||
'plugin:import/typescript',
|
'plugin:import/typescript',
|
||||||
// 'plugin:promise/recommended',
|
// 'plugin:promise/recommended',
|
||||||
// 'plugin:security/recommended-legacy',
|
'plugin:security/recommended-legacy',
|
||||||
'plugin:react/recommended',
|
'plugin:react/recommended',
|
||||||
],
|
],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
@ -23,9 +23,9 @@ module.exports = {
|
|||||||
plugins: [
|
plugins: [
|
||||||
'@typescript-eslint',
|
'@typescript-eslint',
|
||||||
'import',
|
'import',
|
||||||
// 'promise',
|
'promise',
|
||||||
// 'security',
|
'security',
|
||||||
// 'no-catch-all',
|
'no-catch-all',
|
||||||
'react',
|
'react',
|
||||||
'react-hooks',
|
'react-hooks',
|
||||||
],
|
],
|
||||||
@ -44,7 +44,7 @@ module.exports = {
|
|||||||
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
|
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
|
||||||
'react-hooks/exhaustive-deps': 'warn', // Checks effect dependencies
|
'react-hooks/exhaustive-deps': 'warn', // Checks effect dependencies
|
||||||
'react/react-in-jsx-scope': 'off', // Disable requirement for React import
|
'react/react-in-jsx-scope': 'off', // Disable requirement for React import
|
||||||
// 'no-catch-all/no-catch-all': 'error',
|
'no-catch-all/no-catch-all': 'error',
|
||||||
'no-console': 'error',
|
'no-console': 'error',
|
||||||
'no-debugger': 'error',
|
'no-debugger': 'error',
|
||||||
camelcase: 'error',
|
camelcase: 'error',
|
||||||
@ -79,7 +79,7 @@ module.exports = {
|
|||||||
'import/no-relative-parent-imports': [
|
'import/no-relative-parent-imports': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
ignore: ['#[src,root,components,utils]/*'],
|
ignore: ['#[src,types,root,components,utils]/*'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'import/no-self-import': 'error',
|
'import/no-self-import': 'error',
|
||||||
@ -122,21 +122,21 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
'import/prefer-default-export': 'off',
|
'import/prefer-default-export': 'off',
|
||||||
// // promise
|
// promise
|
||||||
// 'promise/catch-or-return': 'error',
|
'promise/catch-or-return': 'error',
|
||||||
// 'promise/no-return-wrap': 'error',
|
'promise/no-return-wrap': 'error',
|
||||||
// 'promise/param-names': 'error',
|
'promise/param-names': 'error',
|
||||||
// 'promise/always-return': 'error',
|
'promise/always-return': 'error',
|
||||||
// 'promise/no-native': 'off',
|
'promise/no-native': 'off',
|
||||||
// 'promise/no-nesting': 'warn',
|
'promise/no-nesting': 'warn',
|
||||||
// 'promise/no-promise-in-callback': 'warn',
|
'promise/no-promise-in-callback': 'warn',
|
||||||
// 'promise/no-callback-in-promise': 'warn',
|
'promise/no-callback-in-promise': 'warn',
|
||||||
// 'promise/avoid-new': 'warn',
|
'promise/avoid-new': 'warn',
|
||||||
// 'promise/no-new-statics': 'error',
|
'promise/no-new-statics': 'error',
|
||||||
// 'promise/no-return-in-finally': 'warn',
|
'promise/no-return-in-finally': 'warn',
|
||||||
// 'promise/valid-params': 'warn',
|
'promise/valid-params': 'warn',
|
||||||
// 'promise/prefer-await-to-callbacks': 'error',
|
'promise/prefer-await-to-callbacks': 'error',
|
||||||
// 'promise/no-multiple-resolved': 'error',
|
'promise/no-multiple-resolved': 'error',
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
@ -156,6 +156,7 @@ module.exports = {
|
|||||||
'plugin:@typescript-eslint/strict',
|
'plugin:@typescript-eslint/strict',
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
|
'@typescript-eslint/consistent-type-imports': 'error',
|
||||||
// allow explicitly defined dangling promises
|
// allow explicitly defined dangling promises
|
||||||
'@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }],
|
'@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }],
|
||||||
'no-void': ['error', { allowAsStatement: true }],
|
'no-void': ['error', { allowAsStatement: true }],
|
||||||
|
|||||||
3
.github/workflows/test.lint.pr.yml
vendored
3
.github/workflows/test.lint.pr.yml
vendored
@ -33,6 +33,7 @@ jobs:
|
|||||||
docker
|
docker
|
||||||
release
|
release
|
||||||
workflow
|
workflow
|
||||||
|
source
|
||||||
other
|
other
|
||||||
# Configure that a scope must always be provided.
|
# Configure that a scope must always be provided.
|
||||||
requireScope: true
|
requireScope: true
|
||||||
@ -73,4 +74,4 @@ jobs:
|
|||||||
# special "[WIP]" prefix to indicate this state. This will avoid the
|
# special "[WIP]" prefix to indicate this state. This will avoid the
|
||||||
# validation of the PR title and the pull request checks remain pending.
|
# validation of the PR title and the pull request checks remain pending.
|
||||||
# Note that a second check will be reported if this is enabled.
|
# Note that a second check will be reported if this is enabled.
|
||||||
wip: true
|
wip: true
|
||||||
|
|||||||
108
CODE_OF_CONDUCT.md
Normal file
108
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
# Code of Conduct
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Our community is dedicated to fostering an open, inclusive, and respectful environment. This Code of Conduct outlines our expectations for all participants and the steps for addressing any unacceptable behavior. By adhering to these guidelines, we can create a space where collaboration and learning thrive.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Ensure that your contributions are relevant to the discussion and meeting agendas.
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the overall
|
||||||
|
community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||||
|
any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Repeatedly deviating from the agenda during meetings, interrupting discussions, or monopolizing speaking time with irrelevant or incoherent contributions.
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email address,
|
||||||
|
without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines when determining consequences for violations of this Code of Conduct.
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact:** Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence:** A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact:** A violation through a single incident or series of actions.
|
||||||
|
|
||||||
|
**Consequence:** A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact:** A serious violation of community standards, including sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence:** A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact:** Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence:** A permanent ban from any sort of public interaction within the community.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conflict Resolution Process
|
||||||
|
|
||||||
|
To address and resolve conflicts effectively, the community will follow these steps:
|
||||||
|
|
||||||
|
### Step 1: Direct Communication
|
||||||
|
|
||||||
|
If a conflict arises, the involved parties are encouraged to address the issue directly and respectfully with one another to seek resolution. This conversation should focus on the behavior or issue at hand, avoiding personal attacks or assumptions.
|
||||||
|
|
||||||
|
### Step 2: Mediation
|
||||||
|
|
||||||
|
If direct communication does not resolve the issue, either party may request mediation from a neutral community leader. The mediator will facilitate a structured conversation to help clarify misunderstandings and find common ground.
|
||||||
|
|
||||||
|
### Step 3: Formal Review
|
||||||
|
|
||||||
|
If the conflict remains unresolved, the issue will be escalated to the community leadership team for review. The leadership team will gather relevant information, including accounts from all parties involved, and make a decision based on the Code of Conduct and the community’s best interests.
|
||||||
|
|
||||||
|
### Step 4: Resolution and Follow-Up
|
||||||
|
|
||||||
|
The leadership team will communicate their decision and any actions to be taken to the involved parties. Follow-up will be conducted to ensure that the resolution is effective and that no further issues arise.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies to all community spaces, including but not limited to:
|
||||||
|
|
||||||
|
- Online forums, repositories, and communication channels.
|
||||||
|
- In-person or virtual meetings and events.
|
||||||
|
- External channels where the community is represented (e.g., social media).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reporting
|
||||||
|
|
||||||
|
If you experience or witness behavior that violates this Code of Conduct, please report it to the community leaders via [info@utopia-lab.org](mailto:info@utopia-lab.org). All reports will be handled with discretion and confidentiality.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
||||||
|
|
||||||
3
Components.svg
Normal file
3
Components.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 15 KiB |
115
README.md
115
README.md
@ -1,125 +1,36 @@
|
|||||||
# Utopia UI [](https://www.npmjs.com/package/utopia-ui)
|
# Utopia UI [](https://www.npmjs.com/package/utopia-ui)  
|
||||||
|
|
||||||
**UI Framework for Real-Life-Networking-Apps**
|
**UI Framework for Real-Life-Networking-Apps**
|
||||||
|
|
||||||
*Real change happens in real life when we meet in person and connect as local communities manifesting their ideas with the earth. When we help each other to step out of capitalism and individualism and start building common infrastructure to meet human needs in harmony with Mother Earth.*
|
*Real change happens in real life when we meet in person and connect as local communities manifesting their ideas with the earth. When we help each other to step out of our bubbles at home and start building common infrastructure to meet human needs in harmony with Mother Earth.*
|
||||||
|
|
||||||
*That is why Utopia UI exists. It is a UI kit for minimalist, fast, intuitive and mobile-first map apps, as a tool for local connection and decentralised networking. It can work with any backend or p2p database and any kind of data structure.*
|
*That is why Utopia UI exists. It is a UI kit for minimalist, fast, intuitive and mobile-first map apps, as a tool for local connection and decentralised networking. We believe in maps as the perfect link between digital tools and real life action*
|
||||||
|
|
||||||
|
*It can work with any backend or p2p database and any kind of data structure.*
|
||||||
|
|
||||||
## Mission
|
## Mission
|
||||||
Utopia UIs mission is to provide open source building blocks to create beautiful applications with a focus on real life impact, local communities and gamification.
|
Utopia UIs mission is to provide open source building blocks to create beautiful applications with a focus on real life impact, local communities and gamification.
|
||||||
|
|
||||||
The building blocks are designed to allow different networks and communities to assemble their map and app for their specific needs and purpose.
|
The building blocks are designed to allow different networks and communities to assemble their map and app for their specific needs and purpose.
|
||||||
|
|
||||||
Utopia Game is one of the apps made with Utopia UI. It is an attempt to use gamification to get users to take action and make the map even more alive. Check it out at [utopia-game.org](https://utopia-game.org/) or see the code in the [repository](https://github.com/utopia-os/utopia-game)
|
It is the base of [Utopia Map](https://github.com/utopia-os/utopia-map) and [Utopia Game](https://github.com/utopia-os/utopia-game).
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Interactive Component Map with customizable Layers (like Projects, Event, People)
|
* Interactive Component Map with customizable Layers (like Projects, Event, People)
|
||||||
* Flexible API-Interface to make it work with every backend or p2p database
|
* Flexible API-Interface to make it work with every backend or p2p database
|
||||||
* Create, Update, Delete Items
|
* Create, Update, Delete Items
|
||||||
* User Authentification API-Interface
|
* User authentification API-Interface
|
||||||
* User Profiles
|
* Customizable Profiles for users and other items
|
||||||
* App Shell
|
* App shell with navigation bar and sidebar
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
### Basic Map
|
See our [`/exampes`](/examples)
|
||||||
In this tutorial we learn how we create a basic React app with a Map component using [utopia-ui](https://github.com/utopia-os/utopia-ui) library.
|
|
||||||
|
|
||||||
For this tutorial we use Vite to create an empty React app called "utopia-static-map"
|
## Components
|
||||||
|
|
||||||
```shell
|

|
||||||
npm create vite@latest utopia-static-map -- --template react
|
|
||||||
```
|
|
||||||
|
|
||||||
We open our new app in the terminal and install the [utopia-ui](https://github.com/utopia-os/utopia-ui) package
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cd utopia-static-map
|
|
||||||
npm install utopia-ui
|
|
||||||
```
|
|
||||||
|
|
||||||
We open our `src/App.jsx` and we replace the content with
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
import { UtopiaMap } from "utopia-ui"
|
|
||||||
|
|
||||||
function App() {
|
|
||||||
return (
|
|
||||||
<UtopiaMap center={[50.6, 9.5]} zoom={5} height='100dvh' width="100dvw">
|
|
||||||
</UtopiaMap>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Then we start the development server to check out the result in our browser:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
And can open our first map app in the browser 🙂
|
|
||||||
|
|
||||||
### Static Layers
|
|
||||||
|
|
||||||
Now we add some static layer.
|
|
||||||
|
|
||||||
First we put some sample data in a new file called `src/sample-data.js`
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export const places = [{
|
|
||||||
"id": 51,
|
|
||||||
"name": "Stadtgemüse",
|
|
||||||
"text": "Stadtgemüse Fulda ist eine Gemüsegärtnerei in Maberzell, die es sich zur Aufgabe gemacht hat, die Stadt und seine Bewohner:innen mit regionalem, frischem und natürlich angebautem Gemüse mittels Gemüsekisten zu versorgen. Es gibt also jede Woche, von Frühjahr bis Herbst, angepasst an die Saison eine Kiste mit schmackhaftem und frischem Gemüse für euch, welche ihr direkt vor Ort abholen könnt. \r\n\r\nhttps://stadtgemuese-fulda.de",
|
|
||||||
"position": { "type": "Point", "coordinates": [9.632435, 50.560342] },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 166,
|
|
||||||
"name": "Weidendom",
|
|
||||||
"text": "free camping",
|
|
||||||
"position": { "type": "Point", "coordinates": [9.438793, 50.560112] },
|
|
||||||
}];
|
|
||||||
|
|
||||||
export const events = [
|
|
||||||
{
|
|
||||||
"id": 423,
|
|
||||||
"name": "Hackathon",
|
|
||||||
"text": "still in progress",
|
|
||||||
"position": { "type": "Point", "coordinates": [10.5, 51.62] },
|
|
||||||
"start": "2022-03-25T12:00:00",
|
|
||||||
"end": "2022-05-12T12:00:00",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
We want to create two Layers. One we want to call *Places* and the other *Events*
|
|
||||||
|
|
||||||
we import our sample data to the `src/App.jsx`
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
import { events, places } from "./sample-data"
|
|
||||||
```
|
|
||||||
and than we create our two `<Layer>` inside of our `<UtopiaMap>` component
|
|
||||||
```jsx
|
|
||||||
<UtopiaMap center={[50.6, 15.5]} zoom={5} height='100dvh' width="100dvw">
|
|
||||||
<Layer
|
|
||||||
name='events'
|
|
||||||
markerIcon='calendar'
|
|
||||||
markerShape='square'
|
|
||||||
markerDefaultColor='#700'
|
|
||||||
data={events} />
|
|
||||||
<Layer
|
|
||||||
name='places'
|
|
||||||
markerIcon='point'
|
|
||||||
markerShape='circle'
|
|
||||||
markerDefaultColor='#007'
|
|
||||||
data={places} />
|
|
||||||
</UtopiaMap>
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Map Component
|
## Map Component
|
||||||
The map shows various Layers (like places, events, profiles ...) of Items at their respective position whith nice and informative Popup and Profiles.
|
The map shows various Layers (like places, events, profiles ...) of Items at their respective position whith nice and informative Popup and Profiles.
|
||||||
|
|||||||
24
examples/1-basic-map/.gitignore
vendored
Normal file
24
examples/1-basic-map/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
1
examples/1-basic-map/.nvmrc
Normal file
1
examples/1-basic-map/.nvmrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
v18.19.1
|
||||||
42
examples/1-basic-map/README.md
Normal file
42
examples/1-basic-map/README.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Example 1: Basic Map
|
||||||
|
|
||||||
|
In this example we see how we create a basic React app with a Map component using [utopia-ui](https://github.com/utopia-os/utopia-ui) library.
|
||||||
|
|
||||||
|
For this example we use Vite to create an empty React app called "utopia-static-map"
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm create vite@latest utopia-static-map -- --template react-ts
|
||||||
|
```
|
||||||
|
|
||||||
|
We open our new app in the terminal and install the [utopia-ui](https://github.com/utopia-os/utopia-ui) package
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd utopia-static-map
|
||||||
|
npm install utopia-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
We open our `src/App.tsx` and we replace the content with
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { UtopiaMap } from "utopia-ui"
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<UtopiaMap center={[50.6, 9.5]} zoom={5} height='100dvh' width="100dvw">
|
||||||
|
</UtopiaMap>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Then we start the development server to check out the result in our browser:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
And can open our first map app in the browser 🙂
|
||||||
|
|
||||||
|
In [Tutorial 2](../2-static-layers/) we gonna add some static data to our map
|
||||||
25
examples/1-basic-map/eslint.config.js
Normal file
25
examples/1-basic-map/eslint.config.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import js from '@eslint/js'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
|
import globals from 'globals'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{ ignores: ['dist'] },
|
||||||
|
{
|
||||||
|
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'react-hooks': reactHooks,
|
||||||
|
'react-refresh': reactRefresh,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
13
examples/1-basic-map/index.html
Normal file
13
examples/1-basic-map/index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + React + TS</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
5350
examples/1-basic-map/package-lock.json
generated
Normal file
5350
examples/1-basic-map/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
examples/1-basic-map/package.json
Normal file
30
examples/1-basic-map/package.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "tutorial-1-basic-map",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc -b && vite build",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1",
|
||||||
|
"utopia-ui": "^3.0.35"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.17.0",
|
||||||
|
"@types/react": "^18.3.18",
|
||||||
|
"@types/react-dom": "^18.3.5",
|
||||||
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
|
"eslint": "^9.17.0",
|
||||||
|
"eslint-plugin-react-hooks": "^5.0.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.16",
|
||||||
|
"globals": "^15.14.0",
|
||||||
|
"typescript": "~5.6.2",
|
||||||
|
"typescript-eslint": "^8.18.2",
|
||||||
|
"vite": "^6.0.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
examples/1-basic-map/public/vite.svg
Normal file
1
examples/1-basic-map/public/vite.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
10
examples/1-basic-map/src/App.tsx
Normal file
10
examples/1-basic-map/src/App.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { UtopiaMap } from "utopia-ui"
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<UtopiaMap center={[50.6, 9.5]} zoom={5} height='100dvh' width="100dvw">
|
||||||
|
</UtopiaMap>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
9
examples/1-basic-map/src/main.tsx
Normal file
9
examples/1-basic-map/src/main.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { StrictMode } from 'react'
|
||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import App from './App.tsx'
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(
|
||||||
|
<StrictMode>
|
||||||
|
<App />
|
||||||
|
</StrictMode>,
|
||||||
|
)
|
||||||
1
examples/1-basic-map/src/vite-env.d.ts
vendored
Normal file
1
examples/1-basic-map/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
26
examples/1-basic-map/tsconfig.app.json
Normal file
26
examples/1-basic-map/tsconfig.app.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
7
examples/1-basic-map/tsconfig.json
Normal file
7
examples/1-basic-map/tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{ "path": "./tsconfig.app.json" },
|
||||||
|
{ "path": "./tsconfig.node.json" }
|
||||||
|
]
|
||||||
|
}
|
||||||
24
examples/1-basic-map/tsconfig.node.json
Normal file
24
examples/1-basic-map/tsconfig.node.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
7
examples/1-basic-map/vite.config.ts
Normal file
7
examples/1-basic-map/vite.config.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
})
|
||||||
24
examples/2-static-layers/.gitignore
vendored
Normal file
24
examples/2-static-layers/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
1
examples/2-static-layers/.nvmrc
Normal file
1
examples/2-static-layers/.nvmrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
v18.19.1
|
||||||
62
examples/2-static-layers/README.md
Normal file
62
examples/2-static-layers/README.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Example 2: Static Layers
|
||||||
|
|
||||||
|
[Example 1](/1-basic-map) shows us how we create a basic map app with [utopia-ui](https://github.com/utopia-os/utopia-ui). Now we add some static layer.
|
||||||
|
|
||||||
|
First we put some sample data in a new file called `src/sample-data.ts`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export const places = [{
|
||||||
|
"id": 51,
|
||||||
|
"name": "Stadtgemüse",
|
||||||
|
"text": "Stadtgemüse Fulda ist eine Gemüsegärtnerei in Maberzell, die es sich zur Aufgabe gemacht hat, die Stadt und seine Bewohner:innen mit regionalem, frischem und natürlich angebautem Gemüse mittels Gemüsekisten zu versorgen. Es gibt also jede Woche, von Frühjahr bis Herbst, angepasst an die Saison eine Kiste mit schmackhaftem und frischem Gemüse für euch, welche ihr direkt vor Ort abholen könnt. \r\n\r\nhttps://stadtgemuese-fulda.de",
|
||||||
|
"position": { "type": "Point", "coordinates": [9.632435, 50.560342] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 166,
|
||||||
|
"name": "Weidendom",
|
||||||
|
"text": "free camping",
|
||||||
|
"position": { "type": "Point", "coordinates": [9.438793, 50.560112] },
|
||||||
|
}];
|
||||||
|
|
||||||
|
export const events = [
|
||||||
|
{
|
||||||
|
"id": 423,
|
||||||
|
"name": "Hackathon",
|
||||||
|
"text": "still in progress",
|
||||||
|
"position": { "type": "Point", "coordinates": [10.5, 51.62] },
|
||||||
|
"start": "2022-03-25T12:00:00",
|
||||||
|
"end": "2022-05-12T12:00:00",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
We want to create two Layers. One we want to call *Places* and the other *Events*
|
||||||
|
|
||||||
|
we import our sample data to the `src/App.tsx`
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { events, places } from "./sample-data"
|
||||||
|
```
|
||||||
|
and than we create our two `<Layer>` inside of our `<UtopiaMap>` component
|
||||||
|
```tsx
|
||||||
|
<UtopiaMap center={[50.6, 15.5]} zoom={5} height='100dvh' width="100dvw">
|
||||||
|
<Layer
|
||||||
|
name='events'
|
||||||
|
markerIcon='calendar'
|
||||||
|
markerShape='square'
|
||||||
|
markerDefaultColor='#700'
|
||||||
|
data={events} />
|
||||||
|
<Layer
|
||||||
|
name='places'
|
||||||
|
markerIcon='point'
|
||||||
|
markerShape='circle'
|
||||||
|
markerDefaultColor='#007'
|
||||||
|
data={places} />
|
||||||
|
</UtopiaMap>
|
||||||
|
```
|
||||||
|
|
||||||
|
And we see our map with two layers:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
25
examples/2-static-layers/eslint.config.js
Normal file
25
examples/2-static-layers/eslint.config.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import js from '@eslint/js'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
|
import globals from 'globals'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{ ignores: ['dist'] },
|
||||||
|
{
|
||||||
|
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'react-hooks': reactHooks,
|
||||||
|
'react-refresh': reactRefresh,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
13
examples/2-static-layers/index.html
Normal file
13
examples/2-static-layers/index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + React + TS</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
5350
examples/2-static-layers/package-lock.json
generated
Normal file
5350
examples/2-static-layers/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
examples/2-static-layers/package.json
Normal file
30
examples/2-static-layers/package.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "tutorial-2-static-layer",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc -b && vite build",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1",
|
||||||
|
"utopia-ui": "^3.0.35"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.17.0",
|
||||||
|
"@types/react": "^18.3.18",
|
||||||
|
"@types/react-dom": "^18.3.5",
|
||||||
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
|
"eslint": "^9.17.0",
|
||||||
|
"eslint-plugin-react-hooks": "^5.0.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.16",
|
||||||
|
"globals": "^15.14.0",
|
||||||
|
"typescript": "~5.6.2",
|
||||||
|
"typescript-eslint": "^8.18.2",
|
||||||
|
"vite": "^6.0.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
examples/2-static-layers/public/vite.svg
Normal file
1
examples/2-static-layers/public/vite.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
23
examples/2-static-layers/src/App.tsx
Normal file
23
examples/2-static-layers/src/App.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { UtopiaMap, Layer } from "utopia-ui"
|
||||||
|
import { events, places } from "./sample-data"
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<UtopiaMap center={[50.6, 15.5]} zoom={5} height='100dvh' width="100dvw">
|
||||||
|
<Layer
|
||||||
|
name='events'
|
||||||
|
markerIcon='calendar'
|
||||||
|
markerShape='square'
|
||||||
|
markerDefaultColor='#700'
|
||||||
|
data={events} />
|
||||||
|
<Layer
|
||||||
|
name='places'
|
||||||
|
markerIcon='point'
|
||||||
|
markerShape='circle'
|
||||||
|
markerDefaultColor='#007'
|
||||||
|
data={places} />
|
||||||
|
</UtopiaMap>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
9
examples/2-static-layers/src/main.tsx
Normal file
9
examples/2-static-layers/src/main.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { StrictMode } from 'react'
|
||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import App from './App.tsx'
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(
|
||||||
|
<StrictMode>
|
||||||
|
<App />
|
||||||
|
</StrictMode>,
|
||||||
|
)
|
||||||
23
examples/2-static-layers/src/sample-data.ts
Normal file
23
examples/2-static-layers/src/sample-data.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export const places = [{
|
||||||
|
"id": 51,
|
||||||
|
"name": "Stadtgemüse",
|
||||||
|
"text": "Stadtgemüse Fulda ist eine Gemüsegärtnerei in Maberzell, die es sich zur Aufgabe gemacht hat, die Stadt und seine Bewohner:innen mit regionalem, frischem und natürlich angebautem Gemüse mittels Gemüsekisten zu versorgen. Es gibt also jede Woche, von Frühjahr bis Herbst, angepasst an die Saison eine Kiste mit schmackhaftem und frischem Gemüse für euch, welche ihr direkt vor Ort abholen könnt. \r\n\r\nhttps://stadtgemuese-fulda.de",
|
||||||
|
"position": { "type": "Point", "coordinates": [9.632435, 50.560342] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 166,
|
||||||
|
"name": "Weidendom",
|
||||||
|
"text": "free camping",
|
||||||
|
"position": { "type": "Point", "coordinates": [9.438793, 50.560112] },
|
||||||
|
}];
|
||||||
|
|
||||||
|
export const events = [
|
||||||
|
{
|
||||||
|
"id": 423,
|
||||||
|
"name": "Hackathon",
|
||||||
|
"text": "still in progress",
|
||||||
|
"position": { "type": "Point", "coordinates": [10.5, 51.62] },
|
||||||
|
"start": "2022-03-25T12:00:00",
|
||||||
|
"end": "2022-05-12T12:00:00",
|
||||||
|
}
|
||||||
|
]
|
||||||
1
examples/2-static-layers/src/vite-env.d.ts
vendored
Normal file
1
examples/2-static-layers/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
26
examples/2-static-layers/tsconfig.app.json
Normal file
26
examples/2-static-layers/tsconfig.app.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
7
examples/2-static-layers/tsconfig.json
Normal file
7
examples/2-static-layers/tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{ "path": "./tsconfig.app.json" },
|
||||||
|
{ "path": "./tsconfig.node.json" }
|
||||||
|
]
|
||||||
|
}
|
||||||
24
examples/2-static-layers/tsconfig.node.json
Normal file
24
examples/2-static-layers/tsconfig.node.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
7
examples/2-static-layers/vite.config.ts
Normal file
7
examples/2-static-layers/vite.config.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
})
|
||||||
130
package-lock.json
generated
130
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "utopia-ui",
|
"name": "utopia-ui",
|
||||||
"version": "3.0.19",
|
"version": "3.0.34",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "utopia-ui",
|
"name": "utopia-ui",
|
||||||
"version": "3.0.19",
|
"version": "3.0.34",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@heroicons/react": "^2.0.17",
|
"@heroicons/react": "^2.0.17",
|
||||||
@ -27,7 +27,8 @@
|
|||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
"remark-breaks": "^4.0.0",
|
"remark-breaks": "^4.0.0",
|
||||||
"tributejs": "^5.1.3",
|
"tributejs": "^5.1.3",
|
||||||
"tw-elements": "^1.0.0"
|
"tw-elements": "^1.0.0",
|
||||||
|
"utopia-ui": "^3.0.35"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint-community/eslint-plugin-eslint-comments": "^4.4.1",
|
"@eslint-community/eslint-plugin-eslint-comments": "^4.4.1",
|
||||||
@ -45,9 +46,12 @@
|
|||||||
"eslint-import-resolver-typescript": "^3.6.3",
|
"eslint-import-resolver-typescript": "^3.6.3",
|
||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"eslint-plugin-json": "^3.1.0",
|
"eslint-plugin-json": "^3.1.0",
|
||||||
|
"eslint-plugin-no-catch-all": "^1.1.0",
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
"eslint-plugin-react": "^7.31.8",
|
"eslint-plugin-react": "^7.31.8",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-security": "^3.0.1",
|
||||||
"eslint-plugin-yml": "^1.14.0",
|
"eslint-plugin-yml": "^1.14.0",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
@ -1351,9 +1355,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"path-key": "^3.1.0",
|
"path-key": "^3.1.0",
|
||||||
@ -2371,6 +2375,16 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-plugin-no-catch-all": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-no-catch-all/-/eslint-plugin-no-catch-all-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-VkP62jLTmccPrFGN/W6V7a3SEwdtTZm+Su2k4T3uyJirtkm0OMMm97h7qd8pRFAHus/jQg9FpUpLRc7sAylBEQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">=2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-plugin-prettier": {
|
"node_modules/eslint-plugin-prettier": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz",
|
||||||
@ -2408,7 +2422,6 @@
|
|||||||
"integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==",
|
"integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
},
|
},
|
||||||
@ -2488,6 +2501,22 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-plugin-security": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-XjVGBhtDZJfyuhIxnQ/WMm385RbX3DBu7H1J7HNNhmB2tnGxMeqVSnYv79oAj992ayvIBZghsymwkYFS6cGH4Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-regex": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-plugin-yml": {
|
"node_modules/eslint-plugin-yml": {
|
||||||
"version": "1.14.0",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-yml/-/eslint-plugin-yml-1.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-yml/-/eslint-plugin-yml-1.14.0.tgz",
|
||||||
@ -4759,9 +4788,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.6",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||||
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
|
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -6015,6 +6044,23 @@
|
|||||||
"react": ">=18"
|
"react": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-photo-album": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-photo-album/-/react-photo-album-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-w3+8i6aj9l1jRfcubgVbAlBGSdtiXcqWdcwZcH4/Bavc+v7X7h+S3TkQ723pvDABjhaaxS168g9ECEBP6xnKrQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=18",
|
||||||
|
"react": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "6.16.0",
|
"version": "6.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.16.0.tgz",
|
||||||
@ -6092,6 +6138,16 @@
|
|||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/regexp-tree": {
|
||||||
|
"version": "0.1.27",
|
||||||
|
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz",
|
||||||
|
"integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"regexp-tree": "bin/regexp-tree"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/regexp.prototype.flags": {
|
"node_modules/regexp.prototype.flags": {
|
||||||
"version": "1.5.3",
|
"version": "1.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz",
|
||||||
@ -6405,6 +6461,16 @@
|
|||||||
"integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==",
|
"integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-regex": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"regexp-tree": "~0.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/safe-regex-test": {
|
"node_modules/safe-regex-test": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
|
||||||
@ -7304,6 +7370,38 @@
|
|||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/utopia-ui": {
|
||||||
|
"version": "3.0.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/utopia-ui/-/utopia-ui-3.0.35.tgz",
|
||||||
|
"integrity": "sha512-PtXvwskYuS4ro/gRWoNkKvn/lC0vW6m9ipwnXb7a4u/95wGWDXajhTh70MbAujTszxSH2iBRY6ZTsSFuREHCJQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@heroicons/react": "^2.0.17",
|
||||||
|
"@tanstack/react-query": "^5.17.8",
|
||||||
|
"@types/offscreencanvas": "^2019.7.1",
|
||||||
|
"axios": "^1.6.5",
|
||||||
|
"date-fns": "^3.3.1",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
|
"leaflet.locatecontrol": "^0.79.0",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"react-colorful": "^5.6.1",
|
||||||
|
"react-image-crop": "^10.1.8",
|
||||||
|
"react-leaflet": "^4.2.1",
|
||||||
|
"react-leaflet-cluster": "^2.1.0",
|
||||||
|
"react-markdown": "^9.0.1",
|
||||||
|
"react-photo-album": "^3.0.2",
|
||||||
|
"react-router-dom": "^6.16.0",
|
||||||
|
"react-string-replace": "^1.1.1",
|
||||||
|
"react-toastify": "^9.1.3",
|
||||||
|
"remark-breaks": "^4.0.0",
|
||||||
|
"tributejs": "^5.1.3",
|
||||||
|
"tw-elements": "^1.0.0",
|
||||||
|
"yet-another-react-lightbox": "^3.21.7"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vfile": {
|
"node_modules/vfile": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz",
|
||||||
@ -7483,6 +7581,18 @@
|
|||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/yet-another-react-lightbox": {
|
||||||
|
"version": "3.21.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/yet-another-react-lightbox/-/yet-another-react-lightbox-3.21.7.tgz",
|
||||||
|
"integrity": "sha512-dcdokNuCIl92f0Vl+uzeKULnQhztIGpoZFUMvtVNUPmtwsQWpqWufeieDPeg9JtFyVCcbj4vYw3V00DS0QNoWA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0",
|
||||||
|
"react-dom": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yocto-queue": {
|
"node_modules/yocto-queue": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "utopia-ui",
|
"name": "utopia-ui",
|
||||||
"version": "3.0.19",
|
"version": "3.0.34",
|
||||||
"description": "Reuseable React Components to build mapping apps for real life communities and networks",
|
"description": "Reuseable React Components to build mapping apps for real life communities and networks",
|
||||||
"repository": "https://github.com/utopia-os/utopia-ui",
|
"repository": "https://github.com/utopia-os/utopia-ui",
|
||||||
"homepage:": "https://utopia-os.org/",
|
"homepage:": "https://utopia-os.org/",
|
||||||
@ -33,9 +33,12 @@
|
|||||||
"eslint-import-resolver-typescript": "^3.6.3",
|
"eslint-import-resolver-typescript": "^3.6.3",
|
||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"eslint-plugin-json": "^3.1.0",
|
"eslint-plugin-json": "^3.1.0",
|
||||||
|
"eslint-plugin-no-catch-all": "^1.1.0",
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
"eslint-plugin-react": "^7.31.8",
|
"eslint-plugin-react": "^7.31.8",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-security": "^3.0.1",
|
||||||
"eslint-plugin-yml": "^1.14.0",
|
"eslint-plugin-yml": "^1.14.0",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
@ -76,6 +79,7 @@
|
|||||||
"#components/*": "./src/Components/*",
|
"#components/*": "./src/Components/*",
|
||||||
"#utils/*": "./src/Utils/*",
|
"#utils/*": "./src/Utils/*",
|
||||||
"#src/*": "./src/*",
|
"#src/*": "./src/*",
|
||||||
|
"#types/*": "./types/*",
|
||||||
"#root/*": "./*"
|
"#root/*": "./*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,9 @@ export default {
|
|||||||
external: [
|
external: [
|
||||||
'react',
|
'react',
|
||||||
'react-dom',
|
'react-dom',
|
||||||
|
'react-markdown',
|
||||||
|
'react/jsx-runtime',
|
||||||
|
'remark-breaks',
|
||||||
'leaflet',
|
'leaflet',
|
||||||
'react-leaflet',
|
'react-leaflet',
|
||||||
'react-toastify',
|
'react-toastify',
|
||||||
@ -34,5 +37,16 @@ export default {
|
|||||||
'prop-types',
|
'prop-types',
|
||||||
'leaflet/dist/leaflet.css',
|
'leaflet/dist/leaflet.css',
|
||||||
'@heroicons/react/20/solid',
|
'@heroicons/react/20/solid',
|
||||||
|
'@heroicons/react/24/outline/ChevronRightIcon',
|
||||||
|
'@heroicons/react/24/outline',
|
||||||
|
'date-fns',
|
||||||
|
'@heroicons/react/24/outline/InformationCircleIcon',
|
||||||
|
'@heroicons/react/24/outline/QuestionMarkCircleIcon',
|
||||||
|
'@heroicons/react/24/outline/ChevronDownIcon',
|
||||||
|
'axios',
|
||||||
|
'react-image-crop',
|
||||||
|
'react-image-crop/dist/ReactCrop.css',
|
||||||
|
'react-colorful',
|
||||||
|
'leaflet.locatecontrol/dist/L.Control.Locate.css',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { AssetsApi } from '#src/types'
|
|
||||||
|
|
||||||
import { ContextWrapper } from './ContextWrapper'
|
import { ContextWrapper } from './ContextWrapper'
|
||||||
import NavBar from './NavBar'
|
import NavBar from './NavBar'
|
||||||
import { SetAppState } from './SetAppState'
|
import { SetAppState } from './SetAppState'
|
||||||
|
|
||||||
|
import type { AssetsApi } from '#types/AssetsApi'
|
||||||
|
|
||||||
export function AppShell({
|
export function AppShell({
|
||||||
appName,
|
appName,
|
||||||
children,
|
children,
|
||||||
|
|||||||
@ -28,6 +28,7 @@ export const ContextWrapper = ({ children }) => {
|
|||||||
try {
|
try {
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
location = useLocation()
|
location = useLocation()
|
||||||
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
location = null
|
location = null
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
||||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
|
||||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
||||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
||||||
import QuestionMarkIcon from '@heroicons/react/24/outline/QuestionMarkCircleIcon'
|
import QuestionMarkIcon from '@heroicons/react/24/outline/QuestionMarkCircleIcon'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { Link, useLocation } from 'react-router-dom'
|
import { Link, useLocation } from 'react-router-dom'
|
||||||
@ -16,7 +5,8 @@ import { toast } from 'react-toastify'
|
|||||||
|
|
||||||
import { useAuth } from '#components/Auth'
|
import { useAuth } from '#components/Auth'
|
||||||
import { useItems } from '#components/Map/hooks/useItems'
|
import { useItems } from '#components/Map/hooks/useItems'
|
||||||
import { Item } from '#src/types'
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export default function NavBar({ appName, userType }: { appName: string; userType: string }) {
|
export default function NavBar({ appName, userType }: { appName: string; userType: string }) {
|
||||||
const { isAuthenticated, user, logout } = useAuth()
|
const { isAuthenticated, user, logout } = useAuth()
|
||||||
@ -30,19 +20,19 @@ export default function NavBar({ appName, userType }: { appName: string; userTyp
|
|||||||
items.find((i) => i.user_created?.id === user.id && i.layer?.itemType.name === userType)
|
items.find((i) => i.user_created?.id === user.id && i.layer?.itemType.name === userType)
|
||||||
profile
|
profile
|
||||||
? setUserProfile(profile)
|
? setUserProfile(profile)
|
||||||
: setUserProfile({ id: crypto.randomUUID(), name: user?.first_name, text: '' })
|
: setUserProfile({ id: crypto.randomUUID(), name: user?.first_name ?? '', text: '' })
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [user, items])
|
}, [user, items])
|
||||||
|
|
||||||
useEffect(() => {}, [userProfile])
|
// useEffect(() => {}, [userProfile])
|
||||||
|
|
||||||
const nameRef = useRef<any>(null)
|
const nameRef = useRef<HTMLHeadingElement>(null)
|
||||||
const [nameWidth, setNameWidth] = useState<number>(0)
|
const [nameWidth, setNameWidth] = useState<number>(0)
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const [showNav, setShowNav] = useState<boolean>(false)
|
const [showNav, setShowNav] = useState<boolean>(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
showNav && nameRef && setNameWidth(nameRef.current.scrollWidth)
|
showNav && nameRef.current && setNameWidth(nameRef.current.scrollWidth)
|
||||||
}, [nameRef, appName, showNav])
|
}, [nameRef, appName, showNav])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -51,8 +41,8 @@ export default function NavBar({ appName, userType }: { appName: string; userTyp
|
|||||||
embedded !== 'true' && setShowNav(true)
|
embedded !== 'true' && setShowNav(true)
|
||||||
}, [location])
|
}, [location])
|
||||||
|
|
||||||
const onLogout = () => {
|
const onLogout = async () => {
|
||||||
toast.promise(logout(), {
|
await toast.promise(logout(), {
|
||||||
success: {
|
success: {
|
||||||
render() {
|
render() {
|
||||||
return 'Bye bye'
|
return 'Bye bye'
|
||||||
@ -62,7 +52,7 @@ export default function NavBar({ appName, userType }: { appName: string; userTyp
|
|||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
render({ data }) {
|
render({ data }) {
|
||||||
return `${data}`
|
return JSON.stringify(data)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pending: 'logging out ..',
|
pending: 'logging out ..',
|
||||||
@ -122,7 +112,7 @@ export default function NavBar({ appName, userType }: { appName: string; userTyp
|
|||||||
to={`${userProfile.id && '/item/' + userProfile.id}`}
|
to={`${userProfile.id && '/item/' + userProfile.id}`}
|
||||||
className='tw-flex tw-items-center'
|
className='tw-flex tw-items-center'
|
||||||
>
|
>
|
||||||
{userProfile?.image && (
|
{userProfile.image && (
|
||||||
<div className='tw-avatar'>
|
<div className='tw-avatar'>
|
||||||
<div className='tw-w-10 tw-rounded-full'>
|
<div className='tw-w-10 tw-rounded-full'>
|
||||||
<img src={'https://api.utopia-lab.org/assets/' + userProfile.image} />
|
<img src={'https://api.utopia-lab.org/assets/' + userProfile.image} />
|
||||||
@ -155,7 +145,7 @@ export default function NavBar({ appName, userType }: { appName: string; userTyp
|
|||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onLogout()
|
void onLogout()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Logout
|
Logout
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
import { AssetsApi } from '#src/types'
|
|
||||||
|
|
||||||
import { useSetAppState } from './hooks/useAppState'
|
import { useSetAppState } from './hooks/useAppState'
|
||||||
|
|
||||||
|
import type { AssetsApi } from '#types/AssetsApi'
|
||||||
|
|
||||||
export const SetAppState = ({
|
export const SetAppState = ({
|
||||||
assetsApi,
|
assetsApi,
|
||||||
userType,
|
userType,
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
import { useCallback, useState, createContext, useContext } from 'react'
|
import { useCallback, useState, createContext, useContext } from 'react'
|
||||||
|
|
||||||
import { AssetsApi } from '#src/types'
|
import type { AssetsApi } from '#types/AssetsApi'
|
||||||
|
|
||||||
interface AppState {
|
interface AppState {
|
||||||
assetsApi: AssetsApi
|
assetsApi: AssetsApi
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
import { useCallback, useState, createContext, useContext } from 'react'
|
import { useCallback, useState, createContext, useContext } from 'react'
|
||||||
|
|
||||||
import { AssetsApi } from '#src/types'
|
import type { AssetsApi } from '#types/AssetsApi'
|
||||||
|
|
||||||
type UseAssetManagerResult = ReturnType<typeof useAssetsManager>
|
type UseAssetManagerResult = ReturnType<typeof useAssetsManager>
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
@ -7,7 +5,8 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { createContext, useState, useContext, useEffect } from 'react'
|
import { createContext, useState, useContext, useEffect } from 'react'
|
||||||
|
|
||||||
import { UserApi, UserItem } from '#src/types'
|
import type { UserApi } from '#types/UserApi'
|
||||||
|
import type { UserItem } from '#types/UserItem'
|
||||||
|
|
||||||
interface AuthProviderProps {
|
interface AuthProviderProps {
|
||||||
userApi: UserApi
|
userApi: UserApi
|
||||||
@ -69,6 +68,7 @@ export const AuthProvider = ({ userApi, children }: AuthProviderProps) => {
|
|||||||
setLoading(false)
|
setLoading(false)
|
||||||
return me
|
return me
|
||||||
} else return undefined
|
} else return undefined
|
||||||
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
return undefined
|
return undefined
|
||||||
@ -79,7 +79,7 @@ export const AuthProvider = ({ userApi, children }: AuthProviderProps) => {
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const res = await userApi.login(credentials.email, credentials.password)
|
const res = await userApi.login(credentials.email, credentials.password)
|
||||||
setToken(res.access_token)
|
setToken(res?.access_token)
|
||||||
return await loadUser()
|
return await loadUser()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
export function Modal({
|
export function Modal({
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { useAuth } from '#components/Auth'
|
import { useAuth } from '#components/Auth'
|
||||||
import { useItems } from '#components/Map/hooks/useItems'
|
import { useItems } from '#components/Map/hooks/useItems'
|
||||||
import { Item } from '#src/types'
|
|
||||||
|
|
||||||
import { useQuestsOpen, useSetQuestOpen } from './hooks/useQuests'
|
import { useQuestsOpen, useSetQuestOpen } from './hooks/useQuests'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export function Quests() {
|
export function Quests() {
|
||||||
const questsOpen = useQuestsOpen()
|
const questsOpen = useQuestsOpen()
|
||||||
const setQuestsOpen = useSetQuestOpen()
|
const setQuestsOpen = useSetQuestOpen()
|
||||||
|
|||||||
@ -70,6 +70,7 @@ export const Autocomplete = ({
|
|||||||
break
|
break
|
||||||
case 'Enter':
|
case 'Enter':
|
||||||
if (filteredSuggestions.length > 0) {
|
if (filteredSuggestions.length > 0) {
|
||||||
|
// eslint-disable-next-line security/detect-object-injection
|
||||||
onSelected(filteredSuggestions[heighlightedSuggestion])
|
onSelected(filteredSuggestions[heighlightedSuggestion])
|
||||||
setHeighlightedSuggestion(0)
|
setHeighlightedSuggestion(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ type TextAreaProps = {
|
|||||||
inputStyle?: string
|
inputStyle?: string
|
||||||
defaultValue: string
|
defaultValue: string
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
|
required?: boolean
|
||||||
updateFormValue?: (value: string) => void
|
updateFormValue?: (value: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ export function TextAreaInput({
|
|||||||
inputStyle,
|
inputStyle,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
required = true,
|
||||||
updateFormValue,
|
updateFormValue,
|
||||||
}: TextAreaProps) {
|
}: TextAreaProps) {
|
||||||
const ref = useRef<HTMLTextAreaElement>(null)
|
const ref = useRef<HTMLTextAreaElement>(null)
|
||||||
@ -90,7 +92,7 @@ export function TextAreaInput({
|
|||||||
</label>
|
</label>
|
||||||
) : null}
|
) : null}
|
||||||
<textarea
|
<textarea
|
||||||
required
|
required={required}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
name={dataField}
|
name={dataField}
|
||||||
|
|||||||
@ -13,6 +13,8 @@ type InputTextProps = {
|
|||||||
defaultValue?: string
|
defaultValue?: string
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
autocomplete?: string
|
autocomplete?: string
|
||||||
|
pattern?: string
|
||||||
|
required?: boolean
|
||||||
updateFormValue?: (value: string) => void
|
updateFormValue?: (value: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,6 +28,8 @@ export function TextInput({
|
|||||||
defaultValue,
|
defaultValue,
|
||||||
placeholder,
|
placeholder,
|
||||||
autocomplete,
|
autocomplete,
|
||||||
|
pattern,
|
||||||
|
required = true,
|
||||||
updateFormValue,
|
updateFormValue,
|
||||||
}: InputTextProps) {
|
}: InputTextProps) {
|
||||||
const [inputValue, setInputValue] = useState<string>(defaultValue || '')
|
const [inputValue, setInputValue] = useState<string>(defaultValue || '')
|
||||||
@ -50,7 +54,8 @@ export function TextInput({
|
|||||||
</label>
|
</label>
|
||||||
) : null}
|
) : null}
|
||||||
<input
|
<input
|
||||||
required
|
required={required}
|
||||||
|
pattern={pattern}
|
||||||
type={type || 'text'}
|
type={type || 'text'}
|
||||||
name={dataField}
|
name={dataField}
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { node, string } from 'prop-types'
|
import { node, string } from 'prop-types'
|
||||||
import { Children, cloneElement, isValidElement, useEffect } from 'react'
|
import { Children, cloneElement, isValidElement, useEffect } from 'react'
|
||||||
|
|
||||||
import { Item } from '#src/types'
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const ItemForm = ({
|
export const ItemForm = ({
|
||||||
children,
|
children,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { node, string } from 'prop-types'
|
import { node, string } from 'prop-types'
|
||||||
import { Children, cloneElement, isValidElement } from 'react'
|
import { Children, cloneElement, isValidElement } from 'react'
|
||||||
|
|
||||||
import { Item } from '#src/types'
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const ItemView = ({ children, item }: { children?: React.ReactNode; item?: Item }) => {
|
export const ItemView = ({ children, item }: { children?: React.ReactNode; item?: Item }) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -7,12 +7,10 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
import { Popup } from 'leaflet'
|
|
||||||
import { Children, isValidElement, useEffect, useState } from 'react'
|
import { Children, isValidElement, useEffect, useState } from 'react'
|
||||||
import { Marker, Tooltip, useMap, useMapEvents } from 'react-leaflet'
|
import { Marker, Tooltip, useMap, useMapEvents } from 'react-leaflet'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
|
|
||||||
import { Item, LayerProps, Tag } from '#src/types'
|
|
||||||
import { encodeTag } from '#utils/FormatTags'
|
import { encodeTag } from '#utils/FormatTags'
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
import { hashTagRegex } from '#utils/HashTagRegex'
|
import { hashTagRegex } from '#utils/HashTagRegex'
|
||||||
@ -32,6 +30,11 @@ import { useAddTag, useAllTagsLoaded, useGetItemTags, useTags } from './hooks/us
|
|||||||
import { ItemFormPopup } from './Subcomponents/ItemFormPopup'
|
import { ItemFormPopup } from './Subcomponents/ItemFormPopup'
|
||||||
import { ItemViewPopup } from './Subcomponents/ItemViewPopup'
|
import { ItemViewPopup } from './Subcomponents/ItemViewPopup'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { LayerProps } from '#types/LayerProps'
|
||||||
|
import type { Tag } from '#types/Tag'
|
||||||
|
import type { Popup } from 'leaflet'
|
||||||
|
|
||||||
export const Layer = ({
|
export const Layer = ({
|
||||||
data,
|
data,
|
||||||
children,
|
children,
|
||||||
@ -196,6 +199,7 @@ export const Layer = ({
|
|||||||
} else {
|
} else {
|
||||||
if (window.location.pathname.split('/')[1]) {
|
if (window.location.pathname.split('/')[1]) {
|
||||||
const id = window.location.pathname.split('/')[1]
|
const id = window.location.pathname.split('/')[1]
|
||||||
|
// eslint-disable-next-line security/detect-object-injection
|
||||||
const ref = leafletRefs[id]
|
const ref = leafletRefs[id]
|
||||||
if (ref?.marker && ref.item.layer?.name === name) {
|
if (ref?.marker && ref.item.layer?.name === name) {
|
||||||
ref.marker &&
|
ref.marker &&
|
||||||
@ -261,20 +265,27 @@ export const Layer = ({
|
|||||||
)
|
)
|
||||||
.map((item: Item) => {
|
.map((item: Item) => {
|
||||||
if (getValue(item, itemLongitudeField) && getValue(item, itemLatitudeField)) {
|
if (getValue(item, itemLongitudeField) && getValue(item, itemLatitudeField)) {
|
||||||
|
// eslint-disable-next-line security/detect-object-injection
|
||||||
if (getValue(item, itemTextField)) item[itemTextField] = getValue(item, itemTextField)
|
if (getValue(item, itemTextField)) item[itemTextField] = getValue(item, itemTextField)
|
||||||
|
// eslint-disable-next-line security/detect-object-injection
|
||||||
else item[itemTextField] = ''
|
else item[itemTextField] = ''
|
||||||
|
|
||||||
if (item.tags) {
|
if (item.tags) {
|
||||||
|
// eslint-disable-next-line security/detect-object-injection
|
||||||
item[itemTextField] = item[itemTextField] + '\n\n'
|
item[itemTextField] = item[itemTextField] + '\n\n'
|
||||||
item.tags.map((tag) => {
|
item.tags.map((tag) => {
|
||||||
|
// eslint-disable-next-line security/detect-object-injection
|
||||||
if (!item[itemTextField].includes(`#${encodeTag(tag)}`)) {
|
if (!item[itemTextField].includes(`#${encodeTag(tag)}`)) {
|
||||||
|
// eslint-disable-next-line security/detect-object-injection
|
||||||
return (item[itemTextField] = item[itemTextField] + `#${encodeTag(tag)} `)
|
return (item[itemTextField] = item[itemTextField] + `#${encodeTag(tag)} `)
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line security/detect-object-injection
|
||||||
return item[itemTextField]
|
return item[itemTextField]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allTagsLoaded && allItemsLoaded) {
|
if (allTagsLoaded && allItemsLoaded) {
|
||||||
|
// eslint-disable-next-line security/detect-object-injection
|
||||||
item[itemTextField].match(hashTagRegex)?.map((tag) => {
|
item[itemTextField].match(hashTagRegex)?.map((tag) => {
|
||||||
if (
|
if (
|
||||||
!tags.find(
|
!tags.find(
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
import { useAuth } from '#components/Auth'
|
import { useAuth } from '#components/Auth'
|
||||||
import { ItemsApi, Permission } from '#src/types'
|
|
||||||
|
|
||||||
import { useSetPermissionData, useSetPermissionApi, useSetAdminRole } from './hooks/usePermissions'
|
import { useSetPermissionData, useSetPermissionApi, useSetAdminRole } from './hooks/usePermissions'
|
||||||
|
|
||||||
|
import type { ItemsApi } from '#types/ItemsApi'
|
||||||
|
import type { Permission } from '#types/Permission'
|
||||||
|
|
||||||
export function Permissions({
|
export function Permissions({
|
||||||
data,
|
data,
|
||||||
api,
|
api,
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
||||||
import { useLayers } from '#components/Map/hooks/useLayers'
|
import { useLayers } from '#components/Map/hooks/useLayers'
|
||||||
import { useHasUserPermission } from '#components/Map/hooks/usePermissions'
|
import { useHasUserPermission } from '#components/Map/hooks/usePermissions'
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,6 @@ import { useItems } from '#components/Map/hooks/useItems'
|
|||||||
import { useLeafletRefs } from '#components/Map/hooks/useLeafletRefs'
|
import { useLeafletRefs } from '#components/Map/hooks/useLeafletRefs'
|
||||||
import { useTags } from '#components/Map/hooks/useTags'
|
import { useTags } from '#components/Map/hooks/useTags'
|
||||||
import useWindowDimensions from '#components/Map/hooks/useWindowDimension'
|
import useWindowDimensions from '#components/Map/hooks/useWindowDimension'
|
||||||
import { Item } from '#src/types'
|
|
||||||
import { decodeTag } from '#utils/FormatTags'
|
import { decodeTag } from '#utils/FormatTags'
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
import MarkerIconFactory from '#utils/MarkerIconFactory'
|
import MarkerIconFactory from '#utils/MarkerIconFactory'
|
||||||
@ -30,6 +29,8 @@ import MarkerIconFactory from '#utils/MarkerIconFactory'
|
|||||||
import { LocateControl } from './LocateControl'
|
import { LocateControl } from './LocateControl'
|
||||||
import { SidebarControl } from './SidebarControl'
|
import { SidebarControl } from './SidebarControl'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const SearchControl = () => {
|
export const SearchControl = () => {
|
||||||
const windowDimensions = useWindowDimensions()
|
const windowDimensions = useWindowDimensions()
|
||||||
const [popupOpen, setPopupOpen] = useState(false)
|
const [popupOpen, setPopupOpen] = useState(false)
|
||||||
@ -63,6 +64,7 @@ export const SearchControl = () => {
|
|||||||
try {
|
try {
|
||||||
const { data } = await axios.get(`https://photon.komoot.io/api/?q=${value}&limit=5`)
|
const { data } = await axios.get(`https://photon.komoot.io/api/?q=${value}&limit=5`)
|
||||||
setGeoResults(data.features)
|
setGeoResults(data.features)
|
||||||
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error)
|
console.log(error)
|
||||||
@ -331,6 +333,7 @@ export const SearchControl = () => {
|
|||||||
|
|
||||||
function isGeoCoordinate(input) {
|
function isGeoCoordinate(input) {
|
||||||
const geokoordinatenRegex =
|
const geokoordinatenRegex =
|
||||||
|
// eslint-disable-next-line security/detect-unsafe-regex
|
||||||
/^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$/
|
/^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$/
|
||||||
return geokoordinatenRegex.test(input)
|
return geokoordinatenRegex.test(input)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
import { Children, cloneElement, isValidElement, useEffect, useRef, useState } from 'react'
|
import { Children, cloneElement, isValidElement, useEffect, useRef, useState } from 'react'
|
||||||
@ -17,10 +16,12 @@ import { TextInput } from '#components/Input/TextInput'
|
|||||||
import { useResetFilterTags } from '#components/Map/hooks/useFilter'
|
import { useResetFilterTags } from '#components/Map/hooks/useFilter'
|
||||||
import { useAddItem, useItems, useRemoveItem, useUpdateItem } from '#components/Map/hooks/useItems'
|
import { useAddItem, useItems, useRemoveItem, useUpdateItem } from '#components/Map/hooks/useItems'
|
||||||
import { useAddTag, useTags } from '#components/Map/hooks/useTags'
|
import { useAddTag, useTags } from '#components/Map/hooks/useTags'
|
||||||
import { Geometry, Item, ItemFormPopupProps } from '#src/types'
|
|
||||||
import { hashTagRegex } from '#utils/HashTagRegex'
|
import { hashTagRegex } from '#utils/HashTagRegex'
|
||||||
import { randomColor } from '#utils/RandomColor'
|
import { randomColor } from '#utils/RandomColor'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
||||||
|
|
||||||
export function ItemFormPopup(props: ItemFormPopupProps) {
|
export function ItemFormPopup(props: ItemFormPopupProps) {
|
||||||
const [spinner, setSpinner] = useState(false)
|
const [spinner, setSpinner] = useState(false)
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
formItem[input.name] = input.value
|
formItem[input.name] = input.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
formItem.position = new Geometry(props.position.lng, props.position.lat)
|
formItem.position = { type: 'Point', coordinates: [props.position.lng, props.position.lat] }
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
setSpinner(true)
|
setSpinner(true)
|
||||||
|
|
||||||
@ -70,6 +71,7 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
try {
|
try {
|
||||||
await props.layer.api?.updateItem!({ ...formItem, id: props.item.id })
|
await props.layer.api?.updateItem!({ ...formItem, id: props.item.id })
|
||||||
success = true
|
success = true
|
||||||
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error.toString())
|
toast.error(error.toString())
|
||||||
}
|
}
|
||||||
@ -101,6 +103,7 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
name: formItem.name ? formItem.name : user?.first_name,
|
name: formItem.name ? formItem.name : user?.first_name,
|
||||||
}))
|
}))
|
||||||
success = true
|
success = true
|
||||||
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error.toString())
|
toast.error(error.toString())
|
||||||
}
|
}
|
||||||
@ -109,8 +112,8 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
if (!props.layer.onlyOnePerOwner || !item) {
|
if (!props.layer.onlyOnePerOwner || !item) {
|
||||||
addItem({
|
addItem({
|
||||||
...formItem,
|
...formItem,
|
||||||
name: formItem.name ? formItem.name : user?.first_name,
|
name: (formItem.name ? formItem.name : user?.first_name) ?? '',
|
||||||
user_created: user,
|
user_created: user ?? undefined,
|
||||||
type: props.layer.itemType,
|
type: props.layer.itemType,
|
||||||
id: uuid,
|
id: uuid,
|
||||||
layer: props.layer,
|
layer: props.layer,
|
||||||
|
|||||||
@ -15,9 +15,11 @@ import { useNavigate } from 'react-router-dom'
|
|||||||
import { useAppState } from '#components/AppShell/hooks/useAppState'
|
import { useAppState } from '#components/AppShell/hooks/useAppState'
|
||||||
import { useHasUserPermission } from '#components/Map/hooks/usePermissions'
|
import { useHasUserPermission } from '#components/Map/hooks/usePermissions'
|
||||||
import DialogModal from '#components/Templates/DialogModal'
|
import DialogModal from '#components/Templates/DialogModal'
|
||||||
import { Item, ItemsApi } from '#src/types'
|
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { ItemsApi } from '#types/ItemsApi'
|
||||||
|
|
||||||
export function HeaderView({
|
export function HeaderView({
|
||||||
item,
|
item,
|
||||||
api,
|
api,
|
||||||
@ -103,6 +105,7 @@ export function HeaderView({
|
|||||||
<div className={`${avatar ? 'tw-ml-2' : ''} tw-overflow-hidden`}>
|
<div className={`${avatar ? 'tw-ml-2' : ''} tw-overflow-hidden`}>
|
||||||
<div
|
<div
|
||||||
className={`${big ? 'xl:tw-text-3xl tw-text-2xl' : 'tw-text-xl'} tw-font-semibold tw-truncate`}
|
className={`${big ? 'xl:tw-text-3xl tw-text-2xl' : 'tw-text-xl'} tw-font-semibold tw-truncate`}
|
||||||
|
title={title}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,9 +3,10 @@
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
import { useGetItemTags } from '#components/Map/hooks/useTags'
|
import { useGetItemTags } from '#components/Map/hooks/useTags'
|
||||||
import { Item } from '#src/types'
|
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const PopupButton = ({
|
export const PopupButton = ({
|
||||||
url,
|
url,
|
||||||
parameterField,
|
parameterField,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Item } from '#src/types'
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const PopupCheckboxInput = ({
|
export const PopupCheckboxInput = ({
|
||||||
dataField,
|
dataField,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
||||||
import { TextInput } from '#components/Input'
|
import { TextInput } from '#components/Input'
|
||||||
import { Item } from '#src/types'
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
interface StartEndInputProps {
|
interface StartEndInputProps {
|
||||||
item?: Item
|
item?: Item
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { TextAreaInput } from '#components/Input'
|
import { TextAreaInput } from '#components/Input'
|
||||||
import { Item } from '#src/types'
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const PopupTextAreaInput = ({
|
export const PopupTextAreaInput = ({
|
||||||
dataField,
|
dataField,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { TextInput } from '#components/Input'
|
import { TextInput } from '#components/Input'
|
||||||
import { Item } from '#src/types'
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const PopupTextInput = ({
|
export const PopupTextInput = ({
|
||||||
dataField,
|
dataField,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
||||||
import { Item } from '#src/types'
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const StartEndView = ({ item }: { item?: Item }) => {
|
export const StartEndView = ({ item }: { item?: Item }) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
||||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
@ -13,12 +11,14 @@ import remarkBreaks from 'remark-breaks'
|
|||||||
|
|
||||||
import { useAddFilterTag } from '#components/Map/hooks/useFilter'
|
import { useAddFilterTag } from '#components/Map/hooks/useFilter'
|
||||||
import { useTags } from '#components/Map/hooks/useTags'
|
import { useTags } from '#components/Map/hooks/useTags'
|
||||||
import { Item } from '#src/types'
|
|
||||||
import { decodeTag } from '#utils/FormatTags'
|
import { decodeTag } from '#utils/FormatTags'
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
import { hashTagRegex } from '#utils/HashTagRegex'
|
import { hashTagRegex } from '#utils/HashTagRegex'
|
||||||
import { fixUrls, mailRegex } from '#utils/ReplaceURLs'
|
import { fixUrls, mailRegex } from '#utils/ReplaceURLs'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { Tag } from '#types/Tag'
|
||||||
|
|
||||||
export const TextView = ({
|
export const TextView = ({
|
||||||
item,
|
item,
|
||||||
truncate = false,
|
truncate = false,
|
||||||
@ -76,47 +76,54 @@ export const TextView = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomH1 = ({ children }) => <h1 className='tw-text-xl tw-font-bold'>{children}</h1>
|
const CustomH1 = ({ children }) => <h1 className='tw-text-xl tw-font-bold'>{children}</h1>
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomH2 = ({ children }) => <h2 className='tw-text-lg tw-font-bold'>{children}</h2>
|
const CustomH2 = ({ children }) => <h2 className='tw-text-lg tw-font-bold'>{children}</h2>
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomH3 = ({ children }) => <h3 className='tw-text-base tw-font-bold'>{children}</h3>
|
const CustomH3 = ({ children }) => <h3 className='tw-text-base tw-font-bold'>{children}</h3>
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomH4 = ({ children }) => <h4 className='tw-text-base tw-font-bold'>{children}</h4>
|
const CustomH4 = ({ children }) => <h4 className='tw-text-base tw-font-bold'>{children}</h4>
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomH5 = ({ children }) => <h5 className='tw-text-sm tw-font-bold'>{children}</h5>
|
const CustomH5 = ({ children }) => <h5 className='tw-text-sm tw-font-bold'>{children}</h5>
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomH6 = ({ children }) => <h6 className='tw-text-sm tw-font-bold'>{children}</h6>
|
const CustomH6 = ({ children }) => <h6 className='tw-text-sm tw-font-bold'>{children}</h6>
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomParagraph = ({ children }) => <p className='!tw-my-2'>{children}</p>
|
const CustomParagraph = ({ children }) => <p className='!tw-my-2'>{children}</p>
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomUnorderdList = ({ children }) => (
|
const CustomUnorderdList = ({ children }) => (
|
||||||
<ul className='tw-list-disc tw-list-inside'>{children}</ul>
|
<ul className='tw-list-disc tw-list-inside'>{children}</ul>
|
||||||
)
|
)
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomOrderdList = ({ children }) => (
|
const CustomOrderdList = ({ children }) => (
|
||||||
<ol className='tw-list-decimal tw-list-inside'>{children}</ol>
|
<ol className='tw-list-decimal tw-list-inside'>{children}</ol>
|
||||||
)
|
)
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomHorizontalRow = ({ children }) => <hr className='tw-border-current'>{children}</hr>
|
const CustomHorizontalRow = ({ children }) => <hr className='tw-border-current'>{children}</hr>
|
||||||
// eslint-disable-next-line react/prop-types
|
// eslint-disable-next-line react/prop-types
|
||||||
const CustomImage = ({ alt, src, title }) => (
|
const CustomImage = ({ alt, src, title }) => (
|
||||||
<img className='tw-max-w-full tw-rounded tw-shadow' src={src} alt={alt} title={title} />
|
<img className='tw-max-w-full tw-rounded tw-shadow' src={src} alt={alt} title={title} />
|
||||||
)
|
)
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomExternalLink = ({ href, children }) => (
|
const CustomExternalLink = ({ href, children }) => (
|
||||||
<a className='tw-font-bold tw-underline' href={href} target='_blank' rel='noreferrer'>
|
<a className='tw-font-bold tw-underline' href={href} target='_blank' rel='noreferrer'>
|
||||||
{' '}
|
{' '}
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
)
|
)
|
||||||
/* eslint-disable react/prop-types */
|
|
||||||
const CustomHashTagLink = ({ children, tag, item }) => {
|
const CustomHashTagLink = ({
|
||||||
|
children,
|
||||||
|
tag,
|
||||||
|
item,
|
||||||
|
}: {
|
||||||
|
children: string
|
||||||
|
tag: Tag
|
||||||
|
item?: Item
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
style={{ color: tag ? tag.color : '#faa', fontWeight: 'bold', cursor: 'pointer' }}
|
style={{ color: tag ? tag.color : '#faa', fontWeight: 'bold', cursor: 'pointer' }}
|
||||||
key={tag ? tag.name + item.id : item.id}
|
key={tag ? tag.name + item?.id : item?.id}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
addFilterTag(tag)
|
addFilterTag(tag)
|
||||||
@ -126,7 +133,6 @@ export const TextView = ({
|
|||||||
</a>
|
</a>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/* eslint-enable react/prop-types */
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
const MemoizedVideoEmbed = memo(({ url }: { url: string }) => (
|
const MemoizedVideoEmbed = memo(({ url }: { url: string }) => (
|
||||||
@ -144,32 +150,34 @@ export const TextView = ({
|
|||||||
remarkPlugins={[remarkBreaks]}
|
remarkPlugins={[remarkBreaks]}
|
||||||
components={{
|
components={{
|
||||||
p: CustomParagraph,
|
p: CustomParagraph,
|
||||||
a: ({ href, children }) => {
|
a: ({ href, children }: { href: string; children: string }) => {
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const isYouTubeVideo = href?.startsWith('https://www.youtube.com/watch?v=')
|
const isYouTubeVideo = href?.startsWith('https://www.youtube.com/watch?v=')
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const isRumbleVideo = href?.startsWith('https://rumble.com/embed/')
|
const isRumbleVideo = href?.startsWith('https://rumble.com/embed/')
|
||||||
|
|
||||||
if (isYouTubeVideo) {
|
if (isYouTubeVideo) {
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const videoId = href?.split('v=')[1].split('&')[0]
|
const videoId = href?.split('v=')[1].split('&')[0]
|
||||||
const youtubeEmbedUrl = `https://www.youtube-nocookie.com/embed/${videoId}`
|
const youtubeEmbedUrl = `https://www.youtube-nocookie.com/embed/${videoId}`
|
||||||
|
|
||||||
return <MemoizedVideoEmbed url={youtubeEmbedUrl}></MemoizedVideoEmbed>
|
return <MemoizedVideoEmbed url={youtubeEmbedUrl}></MemoizedVideoEmbed>
|
||||||
}
|
}
|
||||||
if (isRumbleVideo) {
|
if (isRumbleVideo) {
|
||||||
return <MemoizedVideoEmbed url={href!}></MemoizedVideoEmbed>
|
return <MemoizedVideoEmbed url={href}></MemoizedVideoEmbed>
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
if (href?.startsWith('#')) {
|
if (href?.startsWith('#')) {
|
||||||
|
console.log(href.slice(1).toLowerCase())
|
||||||
|
console.log(tags)
|
||||||
const tag = tags.find(
|
const tag = tags.find(
|
||||||
(t) => t.name.toLowerCase() === decodeURI(href).slice(1).toLowerCase(),
|
(t) => t.name.toLowerCase() === decodeURI(href).slice(1).toLowerCase(),
|
||||||
)
|
)
|
||||||
return (
|
if (tag)
|
||||||
<CustomHashTagLink tag={tag} item={item}>
|
return (
|
||||||
{children}
|
<CustomHashTagLink tag={tag} item={item}>
|
||||||
</CustomHashTagLink>
|
{children}
|
||||||
)
|
</CustomHashTagLink>
|
||||||
|
)
|
||||||
|
else return children
|
||||||
} else {
|
} else {
|
||||||
return <CustomExternalLink href={href}>{children}</CustomExternalLink>
|
return <CustomExternalLink href={href}>{children}</CustomExternalLink>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,12 +15,14 @@ import { toast } from 'react-toastify'
|
|||||||
|
|
||||||
import { useRemoveItem, useUpdateItem } from '#components/Map/hooks/useItems'
|
import { useRemoveItem, useUpdateItem } from '#components/Map/hooks/useItems'
|
||||||
import { useSetSelectPosition } from '#components/Map/hooks/useSelectPosition'
|
import { useSetSelectPosition } from '#components/Map/hooks/useSelectPosition'
|
||||||
import { Item, ItemFormPopupProps } from '#src/types'
|
|
||||||
import { timeAgo } from '#utils/TimeAgo'
|
import { timeAgo } from '#utils/TimeAgo'
|
||||||
|
|
||||||
import { HeaderView } from './ItemPopupComponents/HeaderView'
|
import { HeaderView } from './ItemPopupComponents/HeaderView'
|
||||||
import { TextView } from './ItemPopupComponents/TextView'
|
import { TextView } from './ItemPopupComponents/TextView'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
||||||
|
|
||||||
export interface ItemViewPopupProps {
|
export interface ItemViewPopupProps {
|
||||||
item: Item
|
item: Item
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
@ -63,6 +65,7 @@ export const ItemViewPopup = forwardRef((props: ItemViewPopupProps, ref: any) =>
|
|||||||
props.item.layer?.onlyOnePerOwner &&
|
props.item.layer?.onlyOnePerOwner &&
|
||||||
(await props.item.layer.api?.updateItem!({ id: props.item.id, position: null }))
|
(await props.item.layer.api?.updateItem!({ id: props.item.id, position: null }))
|
||||||
success = true
|
success = true
|
||||||
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error.toString())
|
toast.error(error.toString())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
|
|
||||||
import { ItemsApi, Tag } from '#src/types'
|
|
||||||
|
|
||||||
import { useAddFilterTag, useFilterTags, useResetFilterTags } from './hooks/useFilter'
|
import { useAddFilterTag, useFilterTags, useResetFilterTags } from './hooks/useFilter'
|
||||||
import { useSetTagData, useSetTagApi, useTags } from './hooks/useTags'
|
import { useSetTagData, useSetTagApi, useTags } from './hooks/useTags'
|
||||||
|
|
||||||
|
import type { ItemsApi } from '#types/ItemsApi'
|
||||||
|
import type { Tag } from '#types/Tag'
|
||||||
|
|
||||||
export function Tags({ data, api }: { data?: Tag[]; api?: ItemsApi<Tag> }) {
|
export function Tags({ data, api }: { data?: Tag[]; api?: ItemsApi<Tag> }) {
|
||||||
const setTagData = useSetTagData()
|
const setTagData = useSetTagData()
|
||||||
const setTagApi = useSetTagApi()
|
const setTagApi = useSetTagApi()
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import { ContextWrapper } from '#components/AppShell/ContextWrapper'
|
import { ContextWrapper } from '#components/AppShell/ContextWrapper'
|
||||||
import { UtopiaMapProps } from '#src/types'
|
|
||||||
|
|
||||||
import { UtopiaMapInner } from './UtopiaMapInner'
|
import { UtopiaMapInner } from './UtopiaMapInner'
|
||||||
|
|
||||||
|
import type { UtopiaMapProps } from '#types/UtopiaMapProps'
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-unassigned-import
|
// eslint-disable-next-line import/no-unassigned-import
|
||||||
import 'react-toastify/dist/ReactToastify.css'
|
import 'react-toastify/dist/ReactToastify.css'
|
||||||
|
|
||||||
|
|||||||
@ -23,8 +23,6 @@ import MarkerClusterGroup from 'react-leaflet-cluster'
|
|||||||
import { Outlet } from 'react-router-dom'
|
import { Outlet } from 'react-router-dom'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
import { ItemFormPopupProps, UtopiaMapProps } from '#src/types'
|
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-unassigned-import
|
// eslint-disable-next-line import/no-unassigned-import
|
||||||
import './UtopiaMap.css'
|
import './UtopiaMap.css'
|
||||||
|
|
||||||
@ -46,6 +44,8 @@ import { TagsControl } from './Subcomponents/Controls/TagsControl'
|
|||||||
import { TextView } from './Subcomponents/ItemPopupComponents/TextView'
|
import { TextView } from './Subcomponents/ItemPopupComponents/TextView'
|
||||||
import { SelectPosition } from './Subcomponents/SelectPosition'
|
import { SelectPosition } from './Subcomponents/SelectPosition'
|
||||||
|
|
||||||
|
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
||||||
|
import type { UtopiaMapProps } from '#types/UtopiaMapProps'
|
||||||
import type { Feature, Geometry as GeoJSONGeometry } from 'geojson'
|
import type { Feature, Geometry as GeoJSONGeometry } from 'geojson'
|
||||||
|
|
||||||
const mapDivRef = createRef()
|
const mapDivRef = createRef()
|
||||||
|
|||||||
@ -7,11 +7,12 @@
|
|||||||
import { useCallback, useReducer, createContext, useContext, useState } from 'react'
|
import { useCallback, useReducer, createContext, useContext, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
import { LayerProps, Tag } from '#src/types'
|
|
||||||
|
|
||||||
import { useLayers } from './useLayers'
|
import { useLayers } from './useLayers'
|
||||||
import useWindowDimensions from './useWindowDimension'
|
import useWindowDimensions from './useWindowDimension'
|
||||||
|
|
||||||
|
import type { LayerProps } from '#types/LayerProps'
|
||||||
|
import type { Tag } from '#types/Tag'
|
||||||
|
|
||||||
type ActionType =
|
type ActionType =
|
||||||
| { type: 'ADD_TAG'; tag: Tag }
|
| { type: 'ADD_TAG'; tag: Tag }
|
||||||
| { type: 'REMOVE_TAG'; name: string }
|
| { type: 'REMOVE_TAG'; name: string }
|
||||||
|
|||||||
@ -3,17 +3,16 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||||
import { useCallback, useReducer, createContext, useContext, useState } from 'react'
|
import { useCallback, useReducer, createContext, useContext, useState } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
import { Item, LayerProps } from '#src/types'
|
|
||||||
|
|
||||||
import { useAddLayer } from './useLayers'
|
import { useAddLayer } from './useLayers'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { LayerProps } from '#types/LayerProps'
|
||||||
|
|
||||||
type ActionType =
|
type ActionType =
|
||||||
| { type: 'ADD'; item: Item }
|
| { type: 'ADD'; item: Item }
|
||||||
| { type: 'UPDATE'; item: Item }
|
| { type: 'UPDATE'; item: Item }
|
||||||
@ -82,13 +81,11 @@ function useItemsManager(initialItems: Item[]): {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (result) {
|
result.map((item) => {
|
||||||
result.map((item) => {
|
dispatch({ type: 'ADD', item: { ...item, layer } })
|
||||||
dispatch({ type: 'ADD', item: { ...item, layer } })
|
return null
|
||||||
return null
|
})
|
||||||
})
|
setallItemsLoaded(true)
|
||||||
setallItemsLoaded(true)
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
import { useCallback, useReducer, createContext, useContext } from 'react'
|
import { useCallback, useReducer, createContext, useContext } from 'react'
|
||||||
|
|
||||||
import { LayerProps } from '#src/types'
|
import type { LayerProps } from '#types/LayerProps'
|
||||||
|
|
||||||
interface ActionType {
|
interface ActionType {
|
||||||
type: 'ADD LAYER'
|
type: 'ADD LAYER'
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable react/prop-types */
|
/* eslint-disable react/prop-types */
|
||||||
/* eslint-disable @typescript-eslint/ban-types */
|
/* eslint-disable @typescript-eslint/ban-types */
|
||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
import { Marker, Popup } from 'leaflet'
|
|
||||||
import { useCallback, useReducer, createContext, useContext } from 'react'
|
import { useCallback, useReducer, createContext, useContext } from 'react'
|
||||||
|
|
||||||
import { Item } from '#src/types'
|
import type { Item } from '#types/Item'
|
||||||
|
import type { Marker, Popup } from 'leaflet'
|
||||||
|
|
||||||
interface LeafletRef {
|
interface LeafletRef {
|
||||||
item: Item
|
item: Item
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable react/prop-types */
|
/* eslint-disable react/prop-types */
|
||||||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
@ -10,7 +9,12 @@
|
|||||||
import { useCallback, useReducer, createContext, useContext, useState } from 'react'
|
import { useCallback, useReducer, createContext, useContext, useState } from 'react'
|
||||||
|
|
||||||
import { useAuth } from '#components/Auth/useAuth'
|
import { useAuth } from '#components/Auth/useAuth'
|
||||||
import { Item, ItemsApi, LayerProps, Permission, PermissionAction } from '#src/types'
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { ItemsApi } from '#types/ItemsApi'
|
||||||
|
import type { LayerProps } from '#types/LayerProps'
|
||||||
|
import type { Permission } from '#types/Permission'
|
||||||
|
import type { PermissionAction } from '#types/PermissionAction'
|
||||||
|
|
||||||
type ActionType = { type: 'ADD'; permission: Permission } | { type: 'REMOVE'; id: string }
|
type ActionType = { type: 'ADD'; permission: Permission } | { type: 'REMOVE'; id: string }
|
||||||
|
|
||||||
@ -57,12 +61,10 @@ function usePermissionsManager(initialPermissions: Permission[]): {
|
|||||||
|
|
||||||
const setPermissionApi = useCallback(async (api: ItemsApi<Permission>) => {
|
const setPermissionApi = useCallback(async (api: ItemsApi<Permission>) => {
|
||||||
const result = await api.getItems()
|
const result = await api.getItems()
|
||||||
if (result) {
|
result.map((permission) => {
|
||||||
result.map((permission) => {
|
dispatch({ type: 'ADD', permission })
|
||||||
dispatch({ type: 'ADD', permission })
|
return null
|
||||||
return null
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const setPermissionData = useCallback((data: Permission[]) => {
|
const setPermissionData = useCallback((data: Permission[]) => {
|
||||||
|
|||||||
@ -8,15 +8,18 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
import { LatLng } from 'leaflet'
|
|
||||||
import { createContext, useContext, useEffect, useState } from 'react'
|
import { createContext, useContext, useEffect, useState } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
import { Geometry, Item, LayerProps, ItemFormPopupProps } from '#src/types'
|
|
||||||
|
|
||||||
import { useUpdateItem } from './useItems'
|
import { useUpdateItem } from './useItems'
|
||||||
import { useHasUserPermission } from './usePermissions'
|
import { useHasUserPermission } from './usePermissions'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
||||||
|
import type { LayerProps } from '#types/LayerProps'
|
||||||
|
import type { Point } from 'geojson'
|
||||||
|
import type { LatLng } from 'leaflet'
|
||||||
|
|
||||||
interface PolygonClickedProps {
|
interface PolygonClickedProps {
|
||||||
position: LatLng
|
position: LatLng
|
||||||
setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
||||||
@ -67,7 +70,11 @@ function useSelectPositionManager(): {
|
|||||||
}
|
}
|
||||||
if ('text' in selectPosition) {
|
if ('text' in selectPosition) {
|
||||||
const position =
|
const position =
|
||||||
mapClicked?.position.lng && new Geometry(mapClicked.position.lng, mapClicked.position.lat)
|
mapClicked?.position.lng &&
|
||||||
|
({
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: [mapClicked.position.lng, mapClicked.position.lat],
|
||||||
|
} as Point)
|
||||||
position && itemUpdatePosition({ ...selectPosition, position })
|
position && itemUpdatePosition({ ...selectPosition, position })
|
||||||
setSelectPosition(null)
|
setSelectPosition(null)
|
||||||
}
|
}
|
||||||
@ -89,6 +96,7 @@ function useSelectPositionManager(): {
|
|||||||
position: null,
|
position: null,
|
||||||
})
|
})
|
||||||
success = true
|
success = true
|
||||||
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error.toString())
|
toast.error(error.toString())
|
||||||
}
|
}
|
||||||
@ -113,6 +121,7 @@ function useSelectPositionManager(): {
|
|||||||
position: updatedItem.position,
|
position: updatedItem.position,
|
||||||
})
|
})
|
||||||
success = true
|
success = true
|
||||||
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error.toString())
|
toast.error(error.toString())
|
||||||
}
|
}
|
||||||
@ -134,6 +143,7 @@ function useSelectPositionManager(): {
|
|||||||
try {
|
try {
|
||||||
await markerClicked.layer?.api?.updateItem!(updatedItem)
|
await markerClicked.layer?.api?.updateItem!(updatedItem)
|
||||||
success = true
|
success = true
|
||||||
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error.toString())
|
toast.error(error.toString())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable react/prop-types */
|
/* eslint-disable react/prop-types */
|
||||||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||||
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
||||||
@ -11,10 +10,13 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
import { useCallback, useReducer, createContext, useContext, useState } from 'react'
|
import { useCallback, useReducer, createContext, useContext, useState } from 'react'
|
||||||
|
|
||||||
import { Item, ItemsApi, Tag } from '#src/types'
|
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
import { hashTagRegex } from '#utils/HashTagRegex'
|
import { hashTagRegex } from '#utils/HashTagRegex'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { ItemsApi } from '#types/ItemsApi'
|
||||||
|
import type { Tag } from '#types/Tag'
|
||||||
|
|
||||||
type ActionType = { type: 'ADD'; tag: Tag } | { type: 'REMOVE'; id: string }
|
type ActionType = { type: 'ADD'; tag: Tag } | { type: 'REMOVE'; id: string }
|
||||||
|
|
||||||
type UseTagManagerResult = ReturnType<typeof useTagsManager>
|
type UseTagManagerResult = ReturnType<typeof useTagsManager>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||||
@ -15,7 +14,6 @@ import { useLayers } from '#components/Map/hooks/useLayers'
|
|||||||
import { useHasUserPermission } from '#components/Map/hooks/usePermissions'
|
import { useHasUserPermission } from '#components/Map/hooks/usePermissions'
|
||||||
import { useAddTag, useGetItemTags, useTags } from '#components/Map/hooks/useTags'
|
import { useAddTag, useGetItemTags, useTags } from '#components/Map/hooks/useTags'
|
||||||
import { MapOverlayPage } from '#components/Templates'
|
import { MapOverlayPage } from '#components/Templates'
|
||||||
import { Item, Tag } from '#src/types'
|
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
|
|
||||||
import { linkItem, onUpdateItem, unlinkItem } from './itemFunctions'
|
import { linkItem, onUpdateItem, unlinkItem } from './itemFunctions'
|
||||||
@ -25,6 +23,9 @@ import { OnepagerForm } from './Templates/OnepagerForm'
|
|||||||
import { SimpleForm } from './Templates/SimpleForm'
|
import { SimpleForm } from './Templates/SimpleForm'
|
||||||
import { TabsForm } from './Templates/TabsForm'
|
import { TabsForm } from './Templates/TabsForm'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { Tag } from '#types/Tag'
|
||||||
|
|
||||||
export function ProfileForm() {
|
export function ProfileForm() {
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
color: '',
|
color: '',
|
||||||
@ -78,7 +79,7 @@ export function ProfileForm() {
|
|||||||
const layer = layers.find((l) => l.itemType.name === appState.userType)
|
const layer = layers.find((l) => l.itemType.name === appState.userType)
|
||||||
setItem({
|
setItem({
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
name: user ? user.first_name : '',
|
name: user?.first_name ?? '',
|
||||||
text: '',
|
text: '',
|
||||||
layer,
|
layer,
|
||||||
new: true,
|
new: true,
|
||||||
@ -148,58 +149,64 @@ export function ProfileForm() {
|
|||||||
backdrop
|
backdrop
|
||||||
className='tw-mx-4 tw-mt-4 tw-mb-4 tw-overflow-x-hidden tw-w-[calc(100%-32px)] md:tw-w-[calc(50%-32px)] tw-max-w-3xl !tw-left-auto tw-top-0 tw-bottom-0'
|
className='tw-mx-4 tw-mt-4 tw-mb-4 tw-overflow-x-hidden tw-w-[calc(100%-32px)] md:tw-w-[calc(50%-32px)] tw-max-w-3xl !tw-left-auto tw-top-0 tw-bottom-0'
|
||||||
>
|
>
|
||||||
<div className='tw-flex tw-flex-col tw-h-full'>
|
<form
|
||||||
<FormHeader item={item} state={state} setState={setState} />
|
className='tw-h-full'
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
void onUpdateItem(
|
||||||
|
state,
|
||||||
|
item,
|
||||||
|
tags,
|
||||||
|
addTag,
|
||||||
|
setLoading,
|
||||||
|
navigate,
|
||||||
|
updateItem,
|
||||||
|
addItem,
|
||||||
|
user,
|
||||||
|
urlParams,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='tw-flex tw-flex-col tw-h-full'>
|
||||||
|
<FormHeader item={item} state={state} setState={setState} />
|
||||||
|
|
||||||
{template === 'onepager' && (
|
{template === 'onepager' && (
|
||||||
<OnepagerForm item={item} state={state} setState={setState}></OnepagerForm>
|
<OnepagerForm item={item} state={state} setState={setState}></OnepagerForm>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{template === 'simple' && <SimpleForm state={state} setState={setState}></SimpleForm>}
|
{template === 'simple' && <SimpleForm state={state} setState={setState}></SimpleForm>}
|
||||||
|
|
||||||
{template === 'flex' && (
|
{template === 'flex' && (
|
||||||
<FlexForm item={item} state={state} setState={setState}></FlexForm>
|
<FlexForm item={item} state={state} setState={setState}></FlexForm>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{template === 'tabs' && (
|
{template === 'tabs' && (
|
||||||
<TabsForm
|
<TabsForm
|
||||||
loading={loading}
|
loading={loading}
|
||||||
item={item}
|
item={item}
|
||||||
state={state}
|
state={state}
|
||||||
setState={setState}
|
setState={setState}
|
||||||
updatePermission={updatePermission}
|
updatePermission={updatePermission}
|
||||||
linkItem={(id) => linkItem(id, item, updateItem)}
|
linkItem={(id) => linkItem(id, item, updateItem)}
|
||||||
unlinkItem={(id) => unlinkItem(id, item, updateItem)}
|
unlinkItem={(id) => unlinkItem(id, item, updateItem)}
|
||||||
setUrlParams={setUrlParams}
|
setUrlParams={setUrlParams}
|
||||||
></TabsForm>
|
></TabsForm>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='tw-mt-4'>
|
<div className='tw-mt-4'>
|
||||||
<button
|
<button
|
||||||
className={loading ? ' tw-loading tw-btn tw-float-right' : 'tw-btn tw-float-right'}
|
className={loading ? ' tw-loading tw-btn tw-float-right' : 'tw-btn tw-float-right'}
|
||||||
onClick={() =>
|
type='submit'
|
||||||
onUpdateItem(
|
style={{
|
||||||
state,
|
backgroundColor: `${item.layer?.itemColorField && getValue(item, item.layer?.itemColorField) ? getValue(item, item.layer?.itemColorField) : getItemTags(item) && getItemTags(item)[0] && getItemTags(item)[0].color ? getItemTags(item)[0].color : item?.layer?.markerDefaultColor}`,
|
||||||
item,
|
color: '#fff',
|
||||||
tags,
|
}}
|
||||||
addTag,
|
>
|
||||||
setLoading,
|
Update
|
||||||
navigate,
|
</button>
|
||||||
updateItem,
|
</div>
|
||||||
addItem,
|
|
||||||
user,
|
|
||||||
urlParams,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
style={{
|
|
||||||
backgroundColor: `${item.layer?.itemColorField && getValue(item, item.layer?.itemColorField) ? getValue(item, item.layer?.itemColorField) : getItemTags(item) && getItemTags(item)[0] && getItemTags(item)[0].color ? getItemTags(item)[0].color : item?.layer?.markerDefaultColor}`,
|
|
||||||
color: '#fff',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</MapOverlayPage>
|
</MapOverlayPage>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -21,7 +21,6 @@ import { useSelectPosition, useSetSelectPosition } from '#components/Map/hooks/u
|
|||||||
import { useTags } from '#components/Map/hooks/useTags'
|
import { useTags } from '#components/Map/hooks/useTags'
|
||||||
import { HeaderView } from '#components/Map/Subcomponents/ItemPopupComponents/HeaderView'
|
import { HeaderView } from '#components/Map/Subcomponents/ItemPopupComponents/HeaderView'
|
||||||
import { MapOverlayPage } from '#components/Templates'
|
import { MapOverlayPage } from '#components/Templates'
|
||||||
import { Item, ItemsApi, Tag } from '#src/types'
|
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
|
|
||||||
import { handleDelete, linkItem, unlinkItem } from './itemFunctions'
|
import { handleDelete, linkItem, unlinkItem } from './itemFunctions'
|
||||||
@ -30,6 +29,10 @@ import { OnepagerView } from './Templates/OnepagerView'
|
|||||||
import { SimpleView } from './Templates/SimpleView'
|
import { SimpleView } from './Templates/SimpleView'
|
||||||
import { TabsView } from './Templates/TabsView'
|
import { TabsView } from './Templates/TabsView'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { ItemsApi } from '#types/ItemsApi'
|
||||||
|
import type { Tag } from '#types/Tag'
|
||||||
|
|
||||||
export function ProfileView({ attestationApi }: { attestationApi?: ItemsApi<any> }) {
|
export function ProfileView({ attestationApi }: { attestationApi?: ItemsApi<any> }) {
|
||||||
const [item, setItem] = useState<Item>()
|
const [item, setItem] = useState<Item>()
|
||||||
const [updatePermission, setUpdatePermission] = useState<boolean>(false)
|
const [updatePermission, setUpdatePermission] = useState<boolean>(false)
|
||||||
@ -64,7 +67,9 @@ export function ProfileView({ attestationApi }: { attestationApi?: ItemsApi<any>
|
|||||||
console.log(value)
|
console.log(value)
|
||||||
|
|
||||||
setAttestations(value)
|
setAttestations(value)
|
||||||
|
return null
|
||||||
})
|
})
|
||||||
|
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error('Error fetching items:', error)
|
console.error('Error fetching items:', error)
|
||||||
|
|||||||
@ -10,9 +10,10 @@ import { useHasUserPermission } from '#components/Map/hooks/usePermissions'
|
|||||||
import { useGetItemTags } from '#components/Map/hooks/useTags'
|
import { useGetItemTags } from '#components/Map/hooks/useTags'
|
||||||
import { HeaderView } from '#components/Map/Subcomponents/ItemPopupComponents/HeaderView'
|
import { HeaderView } from '#components/Map/Subcomponents/ItemPopupComponents/HeaderView'
|
||||||
import DialogModal from '#components/Templates/DialogModal'
|
import DialogModal from '#components/Templates/DialogModal'
|
||||||
import { Item } from '#src/types'
|
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export function ActionButton({
|
export function ActionButton({
|
||||||
item,
|
item,
|
||||||
triggerAddButton,
|
triggerAddButton,
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
/* eslint-disable react/prop-types */
|
/* eslint-disable react/prop-types */
|
||||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
||||||
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
||||||
import { useState, useCallback, useRef } from 'react'
|
import { useState, useCallback, useRef } from 'react'
|
||||||
import { ReactCrop, centerCrop, makeAspectCrop } from 'react-image-crop'
|
import { ReactCrop, centerCrop, makeAspectCrop } from 'react-image-crop'
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
import { TextInput } from '#components/Input'
|
import { TextInput } from '#components/Input'
|
||||||
import { FormState } from '#src/types'
|
|
||||||
|
import type { FormState } from '#types/FormState'
|
||||||
|
|
||||||
export const ContactInfoForm = ({
|
export const ContactInfoForm = ({
|
||||||
state,
|
state,
|
||||||
@ -22,6 +23,7 @@ export const ContactInfoForm = ({
|
|||||||
<TextInput
|
<TextInput
|
||||||
placeholder='Email'
|
placeholder='Email'
|
||||||
type='email'
|
type='email'
|
||||||
|
required={false}
|
||||||
defaultValue={state.contact}
|
defaultValue={state.contact}
|
||||||
updateFormValue={(v) =>
|
updateFormValue={(v) =>
|
||||||
setState((prevState) => ({
|
setState((prevState) => ({
|
||||||
@ -41,6 +43,9 @@ export const ContactInfoForm = ({
|
|||||||
</label>
|
</label>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder='Telefonnummer'
|
placeholder='Telefonnummer'
|
||||||
|
type='tel'
|
||||||
|
required={false}
|
||||||
|
pattern='^\+?[0-9\s\-]{7,15}$'
|
||||||
defaultValue={state.telephone}
|
defaultValue={state.telephone}
|
||||||
updateFormValue={(v) =>
|
updateFormValue={(v) =>
|
||||||
setState((prevState) => ({
|
setState((prevState) => ({
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
||||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
@ -8,7 +7,8 @@ import { Link } from 'react-router-dom'
|
|||||||
|
|
||||||
import { useAppState } from '#components/AppShell/hooks/useAppState'
|
import { useAppState } from '#components/AppShell/hooks/useAppState'
|
||||||
import { useItems } from '#components/Map/hooks/useItems'
|
import { useItems } from '#components/Map/hooks/useItems'
|
||||||
import { Item } from '#src/types'
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const ContactInfoView = ({ item, heading }: { item: Item; heading: string }) => {
|
export const ContactInfoView = ({ item, heading }: { item: Item; heading: string }) => {
|
||||||
const appState = useAppState()
|
const appState = useAppState()
|
||||||
|
|||||||
@ -44,6 +44,7 @@ export const FormHeader = ({ item, state, setState }) => {
|
|||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder='Subtitle'
|
placeholder='Subtitle'
|
||||||
|
required={false}
|
||||||
defaultValue={item?.subname ? item.subname : ''}
|
defaultValue={item?.subname ? item.subname : ''}
|
||||||
updateFormValue={(v) =>
|
updateFormValue={(v) =>
|
||||||
setState((prevState) => ({
|
setState((prevState) => ({
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Item } from '#src/types'
|
|
||||||
|
|
||||||
import SocialShareBar from './SocialShareBar'
|
import SocialShareBar from './SocialShareBar'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const GroupSubHeaderView = ({
|
export const GroupSubHeaderView = ({
|
||||||
item,
|
item,
|
||||||
shareBaseUrl,
|
shareBaseUrl,
|
||||||
|
|||||||
@ -4,7 +4,9 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
import ComboBoxInput from '#components/Input/ComboBoxInput'
|
import ComboBoxInput from '#components/Input/ComboBoxInput'
|
||||||
import { Item, FormState } from '#src/types'
|
|
||||||
|
import type { FormState } from '#types/FormState'
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
interface groupType {
|
interface groupType {
|
||||||
groupTypes_id: {
|
groupTypes_id: {
|
||||||
@ -30,13 +32,16 @@ export const GroupSubheaderForm = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (groupTypes && groupStates) {
|
if (groupTypes && groupStates) {
|
||||||
const groupType = groupTypes.find((gt) => gt.groupTypes_id.name === state.group_type)
|
const groupType = groupTypes.find((gt) => gt.groupTypes_id.name === state.group_type)
|
||||||
// eslint-disable-next-line no-console
|
const customImage = !groupTypes.some(
|
||||||
console.log(state.group_type)
|
(gt) => gt.groupTypes_id.image === state.image || !state.image,
|
||||||
|
)
|
||||||
setState((prevState) => ({
|
setState((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
color: groupType?.groupTypes_id.color || groupTypes[0].groupTypes_id.color,
|
color: groupType?.groupTypes_id.color || groupTypes[0].groupTypes_id.color,
|
||||||
marker_icon: groupType?.groupTypes_id.markerIcon || groupTypes[0].groupTypes_id.markerIcon,
|
marker_icon: groupType?.groupTypes_id.markerIcon || groupTypes[0].groupTypes_id.markerIcon,
|
||||||
image: groupType?.groupTypes_id.image || groupTypes[0].groupTypes_id.image,
|
image: customImage
|
||||||
|
? state.image
|
||||||
|
: groupType?.groupTypes_id.image || groupTypes[0].groupTypes_id.image,
|
||||||
status: state.status || groupStates[0],
|
status: state.status || groupStates[0],
|
||||||
group_type: state.group_type || groupTypes[0].groupTypes_id.name,
|
group_type: state.group_type || groupTypes[0].groupTypes_id.name,
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -8,9 +8,10 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
import { useAppState } from '#components/AppShell/hooks/useAppState'
|
import { useAppState } from '#components/AppShell/hooks/useAppState'
|
||||||
import { Item } from '#src/types'
|
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export function LinkedItemsHeaderView({
|
export function LinkedItemsHeaderView({
|
||||||
item,
|
item,
|
||||||
unlinkCallback,
|
unlinkCallback,
|
||||||
|
|||||||
34
src/Components/Profile/Subcomponents/MarkdownHint.tsx
Normal file
34
src/Components/Profile/Subcomponents/MarkdownHint.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
export const MarkdownHint = () => {
|
||||||
|
const [expended, setExpended] = useState<boolean>(false)
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onClick={() => setExpended(true)}
|
||||||
|
title='Markdown is supported'
|
||||||
|
className='flex tw-flex-row tw-text-gray-400 tw-cursor-pointer tw-items-center'
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden='true'
|
||||||
|
height='16'
|
||||||
|
viewBox='0 0 16 16'
|
||||||
|
version='1.1'
|
||||||
|
width='16'
|
||||||
|
data-view-component='true'
|
||||||
|
className='octicon octicon-markdown'
|
||||||
|
fill='rgb(156 163 175 / var(--tw-text-opacity))'
|
||||||
|
>
|
||||||
|
<path d='M14.85 3c.63 0 1.15.52 1.14 1.15v7.7c0 .63-.51 1.15-1.15 1.15H1.15C.52 13 0 12.48 0 11.84V4.15C0 3.52.52 3 1.15 3ZM9 11V5H7L5.5 7 4 5H2v6h2V8l1.5 1.92L7 8v3Zm2.99.5L14.5 8H13V5h-2v3H9.5Z'></path>
|
||||||
|
</svg>
|
||||||
|
{expended && (
|
||||||
|
<a
|
||||||
|
href='https://www.markdownguide.org/cheat-sheet/#basic-syntax'
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
>
|
||||||
|
<span className='Button-label tw-ml-1'>Markdown is support</span>{' '}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,7 +1,8 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
import { useHasUserPermission } from '#components/Map/hooks/usePermissions'
|
import { useHasUserPermission } from '#components/Map/hooks/usePermissions'
|
||||||
import { LayerProps } from '#src/types'
|
|
||||||
|
import type { LayerProps } from '#types/LayerProps'
|
||||||
|
|
||||||
export function PlusButton({
|
export function PlusButton({
|
||||||
layer,
|
layer,
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
import { PopupStartEndInput } from '#components/Map'
|
import { PopupStartEndInput } from '#components/Map'
|
||||||
import { Item } from '#src/types'
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const ProfileStartEndForm = ({
|
export const ProfileStartEndForm = ({
|
||||||
item,
|
item,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { StartEndView } from '#components/Map'
|
import { StartEndView } from '#components/Map'
|
||||||
import { Item } from '#src/types'
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const ProfileStartEndView = ({ item }: { item: Item }) => {
|
export const ProfileStartEndView = ({ item }: { item: Item }) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -5,9 +5,12 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { TextAreaInput } from '#components/Input'
|
import { TextAreaInput } from '#components/Input'
|
||||||
import { FormState } from '#src/types'
|
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
|
|
||||||
|
import { MarkdownHint } from './MarkdownHint'
|
||||||
|
|
||||||
|
import type { FormState } from '#types/FormState'
|
||||||
|
|
||||||
export const ProfileTextForm = ({
|
export const ProfileTextForm = ({
|
||||||
state,
|
state,
|
||||||
setState,
|
setState,
|
||||||
@ -15,6 +18,7 @@ export const ProfileTextForm = ({
|
|||||||
heading,
|
heading,
|
||||||
size,
|
size,
|
||||||
hideInputLabel,
|
hideInputLabel,
|
||||||
|
required,
|
||||||
}: {
|
}: {
|
||||||
state: FormState
|
state: FormState
|
||||||
setState: React.Dispatch<React.SetStateAction<any>>
|
setState: React.Dispatch<React.SetStateAction<any>>
|
||||||
@ -22,6 +26,7 @@ export const ProfileTextForm = ({
|
|||||||
heading: string
|
heading: string
|
||||||
size: string
|
size: string
|
||||||
hideInputLabel: boolean
|
hideInputLabel: boolean
|
||||||
|
required?: boolean
|
||||||
}) => {
|
}) => {
|
||||||
const [field, setField] = useState<string>(dataField || 'text')
|
const [field, setField] = useState<string>(dataField || 'text')
|
||||||
|
|
||||||
@ -33,12 +38,15 @@ export const ProfileTextForm = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='tw-h-full tw-flex tw-flex-col tw-mt-4'>
|
<div className='tw-h-full tw-flex tw-flex-col tw-mt-4'>
|
||||||
<label
|
<div className='tw-flex tw-justify-between tw-items-center'>
|
||||||
htmlFor='nextAppointment'
|
<label
|
||||||
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
htmlFor='nextAppointment'
|
||||||
>
|
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
||||||
{heading || 'Text'}:
|
>
|
||||||
</label>
|
{heading || 'Text'}:
|
||||||
|
</label>
|
||||||
|
<MarkdownHint />
|
||||||
|
</div>
|
||||||
<TextAreaInput
|
<TextAreaInput
|
||||||
placeholder={'...'}
|
placeholder={'...'}
|
||||||
defaultValue={getValue(state, field)}
|
defaultValue={getValue(state, field)}
|
||||||
@ -51,6 +59,7 @@ export const ProfileTextForm = ({
|
|||||||
labelStyle={hideInputLabel ? 'tw-hidden' : ''}
|
labelStyle={hideInputLabel ? 'tw-hidden' : ''}
|
||||||
containerStyle={size === 'full' ? 'tw-grow tw-h-full' : ''}
|
containerStyle={size === 'full' ? 'tw-grow tw-h-full' : ''}
|
||||||
inputStyle={size === 'full' ? 'tw-h-full' : 'tw-h-24'}
|
inputStyle={size === 'full' ? 'tw-h-full' : 'tw-h-24'}
|
||||||
|
required={required}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
import { TextView } from '#components/Map'
|
import { TextView } from '#components/Map'
|
||||||
import { Item } from '#src/types'
|
|
||||||
import { getValue } from '#utils/GetValue'
|
import { getValue } from '#utils/GetValue'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const ProfileTextView = ({
|
export const ProfileTextView = ({
|
||||||
item,
|
item,
|
||||||
dataField,
|
dataField,
|
||||||
|
|||||||
@ -16,7 +16,9 @@ const SocialShareBar = ({
|
|||||||
.writeText(url)
|
.writeText(url)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success('link copied to clipboard')
|
toast.success('link copied to clipboard')
|
||||||
|
return null
|
||||||
})
|
})
|
||||||
|
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||||
.catch((error: never) => {
|
.catch((error: never) => {
|
||||||
toast.error('Fehler beim Kopieren des Links: ', error)
|
toast.error('Fehler beim Kopieren des Links: ', error)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -84,6 +84,7 @@ const SocialShareButton = ({
|
|||||||
url: string
|
url: string
|
||||||
title: string
|
title: string
|
||||||
}) => {
|
}) => {
|
||||||
|
// eslint-disable-next-line security/detect-object-injection
|
||||||
const config = platformConfigs[platform]
|
const config = platformConfigs[platform]
|
||||||
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
|
|||||||
@ -8,10 +8,11 @@ import { useEffect, useState } from 'react'
|
|||||||
|
|
||||||
import { Autocomplete } from '#components/Input/Autocomplete'
|
import { Autocomplete } from '#components/Input/Autocomplete'
|
||||||
import { useTags } from '#components/Map/hooks/useTags'
|
import { useTags } from '#components/Map/hooks/useTags'
|
||||||
import { Tag } from '#src/types'
|
|
||||||
import { decodeTag, encodeTag } from '#utils/FormatTags'
|
import { decodeTag, encodeTag } from '#utils/FormatTags'
|
||||||
import { randomColor } from '#utils/RandomColor'
|
import { randomColor } from '#utils/RandomColor'
|
||||||
|
|
||||||
|
import type { Tag } from '#types/Tag'
|
||||||
|
|
||||||
// eslint-disable-next-line react/prop-types
|
// eslint-disable-next-line react/prop-types
|
||||||
export const TagsWidget = ({ placeholder, containerStyle, defaultTags, onUpdate }) => {
|
export const TagsWidget = ({ placeholder, containerStyle, defaultTags, onUpdate }) => {
|
||||||
const [input, setInput] = useState('')
|
const [input, setInput] = useState('')
|
||||||
|
|||||||
@ -7,7 +7,9 @@ import { ContactInfoForm } from '#components/Profile/Subcomponents/ContactInfoFo
|
|||||||
import { GroupSubheaderForm } from '#components/Profile/Subcomponents/GroupSubheaderForm'
|
import { GroupSubheaderForm } from '#components/Profile/Subcomponents/GroupSubheaderForm'
|
||||||
import { ProfileStartEndForm } from '#components/Profile/Subcomponents/ProfileStartEndForm'
|
import { ProfileStartEndForm } from '#components/Profile/Subcomponents/ProfileStartEndForm'
|
||||||
import { ProfileTextForm } from '#components/Profile/Subcomponents/ProfileTextForm'
|
import { ProfileTextForm } from '#components/Profile/Subcomponents/ProfileTextForm'
|
||||||
import { Item, FormState } from '#src/types'
|
|
||||||
|
import type { FormState } from '#types/FormState'
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
const componentMap = {
|
const componentMap = {
|
||||||
groupSubheaders: GroupSubheaderForm,
|
groupSubheaders: GroupSubheaderForm,
|
||||||
|
|||||||
@ -6,7 +6,8 @@ import { ContactInfoView } from '#components/Profile/Subcomponents/ContactInfoVi
|
|||||||
import { GroupSubHeaderView } from '#components/Profile/Subcomponents/GroupSubHeaderView'
|
import { GroupSubHeaderView } from '#components/Profile/Subcomponents/GroupSubHeaderView'
|
||||||
import { ProfileStartEndView } from '#components/Profile/Subcomponents/ProfileStartEndView'
|
import { ProfileStartEndView } from '#components/Profile/Subcomponents/ProfileStartEndView'
|
||||||
import { ProfileTextView } from '#components/Profile/Subcomponents/ProfileTextView'
|
import { ProfileTextView } from '#components/Profile/Subcomponents/ProfileTextView'
|
||||||
import { Item } from '#src/types'
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
const componentMap = {
|
const componentMap = {
|
||||||
groupSubheaders: GroupSubHeaderView,
|
groupSubheaders: GroupSubHeaderView,
|
||||||
|
|||||||
@ -3,7 +3,9 @@
|
|||||||
import { TextAreaInput } from '#components/Input'
|
import { TextAreaInput } from '#components/Input'
|
||||||
import { ContactInfoForm } from '#components/Profile/Subcomponents/ContactInfoForm'
|
import { ContactInfoForm } from '#components/Profile/Subcomponents/ContactInfoForm'
|
||||||
import { GroupSubheaderForm } from '#components/Profile/Subcomponents/GroupSubheaderForm'
|
import { GroupSubheaderForm } from '#components/Profile/Subcomponents/GroupSubheaderForm'
|
||||||
import { FormState, Item } from '#src/types'
|
|
||||||
|
import type { FormState } from '#types/FormState'
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
export const OnepagerForm = ({
|
export const OnepagerForm = ({
|
||||||
item,
|
item,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user