Update Vaadin and Spring Boot version
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
This commit is contained in:
-43
@@ -1,43 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.views;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.comics.Artist;
|
||||
import de.thpeetz.kontor.views.comics.ArtistForm;
|
||||
import de.thpeetz.kontor.views.comics.ArtistView;
|
||||
|
||||
@SpringBootTest
|
||||
class ArtistViewTest {
|
||||
|
||||
@Autowired
|
||||
private ArtistView artistView;
|
||||
|
||||
@Test
|
||||
void formShownWhenArtistSelected() {
|
||||
Grid<Artist> grid = artistView.getGrid();
|
||||
Artist firstArtist = getFirstItem(grid);
|
||||
|
||||
ArtistForm form = artistView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstArtist);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstArtist.getName(), form.name.getValue());
|
||||
}
|
||||
|
||||
private Artist getFirstItem(Grid<Artist> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Artist> artists = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(5, count);
|
||||
return artists.get(0);
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.views;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import de.thpeetz.kontor.data.comics.Artist;
|
||||
import de.thpeetz.kontor.views.comics.ArtistForm;
|
||||
|
||||
@SpringBootTest
|
||||
class ArtistformTest {
|
||||
|
||||
private Artist artist1;
|
||||
private static final String ARTISTNAME= "Lee, Stan";
|
||||
|
||||
@BeforeEach
|
||||
void setupData() {
|
||||
artist1 = new Artist();
|
||||
artist1.setName(ARTISTNAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
void formFieldsPopulated() {
|
||||
ArtistForm form = new ArtistForm();
|
||||
form.setArtist(artist1);
|
||||
assertEquals(ARTISTNAME, form.name.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveEventHasCorrectValues() {
|
||||
ArtistForm form = new ArtistForm();
|
||||
Artist artist = new Artist();
|
||||
form.setArtist(artist);
|
||||
form.name.setValue(ARTISTNAME);
|
||||
|
||||
AtomicReference<Artist> savedArtistReference = new AtomicReference<>(null);
|
||||
form.addSaveListener(e -> {
|
||||
savedArtistReference.set(e.getArtist());
|
||||
});
|
||||
form.save.click();
|
||||
Artist savedArtist = savedArtistReference.get();
|
||||
assertEquals(ARTISTNAME, savedArtist.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteEventHasCorrectValues() {
|
||||
ArtistForm form = new ArtistForm();
|
||||
Artist artist = new Artist();
|
||||
form.setArtist(artist);
|
||||
form.name.setValue(ARTISTNAME);
|
||||
|
||||
AtomicReference<Artist> deletedArtistReference = new AtomicReference<>(null);
|
||||
form.addDeleteListener(e -> {
|
||||
deletedArtistReference.set(e.getArtist());
|
||||
});
|
||||
form.delete.click();
|
||||
Artist deletedArtist = deletedArtistReference.get();
|
||||
assertEquals(ARTISTNAME, deletedArtist.getName());
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.views;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.comics.Comic;
|
||||
import de.thpeetz.kontor.views.comics.ComicForm;
|
||||
import de.thpeetz.kontor.views.comics.ComicView;
|
||||
|
||||
@SpringBootTest
|
||||
public class ComicViewTest {
|
||||
|
||||
@Autowired
|
||||
private ComicView comicView;
|
||||
|
||||
@Test
|
||||
void formShownWhenComicSelected() {
|
||||
Grid<Comic> grid = comicView.getGrid();
|
||||
Comic firstComic = getFirstItem(grid);
|
||||
|
||||
ComicForm form = comicView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstComic);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstComic.getTitle(), form.title.getValue());
|
||||
}
|
||||
|
||||
private Comic getFirstItem(Grid<Comic> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Comic> comics = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(169, count);
|
||||
return comics.get(0);
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.views;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.comics.ComicWork;
|
||||
import de.thpeetz.kontor.views.comics.ComicWorkForm;
|
||||
import de.thpeetz.kontor.views.comics.ComicWorkView;
|
||||
|
||||
@SpringBootTest
|
||||
class ComicWorkViewTest {
|
||||
|
||||
@Autowired
|
||||
private ComicWorkView comicWorkView;
|
||||
|
||||
@Test
|
||||
void formShownWhenComicSelected() {
|
||||
Grid<ComicWork> grid = comicWorkView.getGrid();
|
||||
ComicWork firstComicWork = getFirstItem(grid);
|
||||
|
||||
ComicWorkForm form = comicWorkView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstComicWork);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstComicWork.getComic(), form.comic.getValue());
|
||||
}
|
||||
|
||||
private ComicWork getFirstItem(Grid<ComicWork> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<ComicWork> comicWorks = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(18, count);
|
||||
return comicWorks.get(0);
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.views;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.comics.Issue;
|
||||
import de.thpeetz.kontor.views.comics.IssueForm;
|
||||
import de.thpeetz.kontor.views.comics.IssueView;
|
||||
|
||||
@SpringBootTest
|
||||
public class IssueViewTest {
|
||||
|
||||
@Autowired
|
||||
private IssueView issueView;
|
||||
|
||||
@Test
|
||||
void formShownWhenIssueSelected() {
|
||||
Grid<Issue> grid = issueView.getGrid();
|
||||
Issue firstIssue = getFirstItem(grid);
|
||||
|
||||
IssueForm form = issueView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstIssue);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstIssue.getIssueNumber(), form.issueNumber.getValue());
|
||||
}
|
||||
|
||||
private Issue getFirstItem(Grid<Issue> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Issue> issues = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(750, count);
|
||||
return issues.get(0);
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.views;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.comics.Publisher;
|
||||
import de.thpeetz.kontor.views.comics.PublisherForm;
|
||||
import de.thpeetz.kontor.views.comics.PublisherView;
|
||||
|
||||
@SpringBootTest
|
||||
class PublisherViewTest {
|
||||
|
||||
@Autowired
|
||||
private PublisherView publisherView;
|
||||
|
||||
@Test
|
||||
void formShownWhenPublisherSelected() {
|
||||
Grid<Publisher> grid = publisherView.getGrid();
|
||||
Publisher firstPublisher = getFirstItem(grid);
|
||||
|
||||
PublisherForm form = publisherView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstPublisher);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstPublisher.getName(), form.name.getValue());
|
||||
}
|
||||
|
||||
private Publisher getFirstItem(Grid<Publisher> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Publisher> publishers = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(18, count);
|
||||
return publishers.get(0);
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.views;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.comics.StoryArc;
|
||||
import de.thpeetz.kontor.views.comics.StoryArcForm;
|
||||
import de.thpeetz.kontor.views.comics.StoryArcView;
|
||||
|
||||
@SpringBootTest
|
||||
class StoryArcViewTest {
|
||||
|
||||
@Autowired
|
||||
private StoryArcView storyArcView;
|
||||
|
||||
@Test
|
||||
void formShownWhenStoryArcSelected() {
|
||||
Grid<StoryArc> grid = storyArcView.getGrid();
|
||||
StoryArc firstStoryArc = getFirstItem(grid);
|
||||
|
||||
StoryArcForm form = storyArcView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstStoryArc);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstStoryArc.getName(), form.name.getValue());
|
||||
}
|
||||
|
||||
private StoryArc getFirstItem(Grid<StoryArc> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<StoryArc> storyArcs = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(3, count);
|
||||
return storyArcs.get(0);
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.views;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.comics.TradePaperback;
|
||||
import de.thpeetz.kontor.views.comics.TradePaperBackForm;
|
||||
import de.thpeetz.kontor.views.comics.TradePaperbackView;
|
||||
|
||||
@SpringBootTest
|
||||
class TradePaperbackViewTest {
|
||||
|
||||
@Autowired
|
||||
private TradePaperbackView tradePaperbackView;
|
||||
|
||||
@Test
|
||||
void formShownWhenVolumeSelected() {
|
||||
Grid<TradePaperback> grid = tradePaperbackView.getGrid();
|
||||
|
||||
TradePaperback firstTradePaperback = getFirstItem(grid);
|
||||
|
||||
TradePaperBackForm form = tradePaperbackView.getForm();
|
||||
assertFalse(form.isVisible());
|
||||
|
||||
if (firstTradePaperback != null) {
|
||||
grid.asSingleSelect().setValue(firstTradePaperback);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstTradePaperback.getName(), form.name.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private TradePaperback getFirstItem(Grid<TradePaperback> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<TradePaperback> tradePaperbacks = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(40, count);
|
||||
return tradePaperbacks.get(0);
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.views;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.comics.Volume;
|
||||
import de.thpeetz.kontor.views.comics.VolumeForm;
|
||||
import de.thpeetz.kontor.views.comics.VolumeView;
|
||||
|
||||
@SpringBootTest
|
||||
class VolumeViewTest {
|
||||
|
||||
@Autowired
|
||||
private VolumeView volumeView;
|
||||
|
||||
@Test
|
||||
void formShownWhenVolumeSelected() {
|
||||
Grid<Volume> grid = volumeView.getGrid();
|
||||
|
||||
Volume firstVolume = getFirstItem(grid);
|
||||
|
||||
VolumeForm form = volumeView.getForm();
|
||||
assertFalse(form.isVisible());
|
||||
|
||||
if (firstVolume != null) {
|
||||
grid.asSingleSelect().setValue(firstVolume);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstVolume.getName(), form.name.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private Volume getFirstItem(Grid<Volume> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Volume> volumes = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(0, count);
|
||||
if (count > 0) {
|
||||
return volumes.get(0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.views;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.comics.Worktype;
|
||||
import de.thpeetz.kontor.views.comics.WorktypeForm;
|
||||
import de.thpeetz.kontor.views.comics.WorktypeView;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
class WorktypeViewTest {
|
||||
|
||||
@Autowired
|
||||
private WorktypeView worktypeView;
|
||||
|
||||
@Test
|
||||
void formShownWhenWorktypeSelected() {
|
||||
Grid<Worktype> grid = worktypeView.getGrid();
|
||||
|
||||
Worktype firstWorktype = getFirstItem(grid);
|
||||
|
||||
WorktypeForm form = worktypeView.getForm();
|
||||
assertFalse(form.isVisible());
|
||||
|
||||
if (firstWorktype != null) {
|
||||
grid.asSingleSelect().setValue(firstWorktype);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstWorktype.getName(), form.name.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private Worktype getFirstItem(Grid<Worktype> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Worktype> worktypes = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
log.info("found worktypes: {}", worktypes);
|
||||
assertEquals(3, count);
|
||||
return worktypes.get(0);
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package de.thpeetz.kontor.views.tysc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.tysc.CardSet;
|
||||
import de.thpeetz.kontor.views.tysc.CardSetForm;
|
||||
import de.thpeetz.kontor.views.tysc.CardSetView;
|
||||
|
||||
@SpringBootTest
|
||||
class CardSetViewTest {
|
||||
|
||||
@Autowired
|
||||
private CardSetView cardSetView;
|
||||
|
||||
@Test
|
||||
void formShownWhenCardSetSelected() {
|
||||
Grid<CardSet> grid = cardSetView.getGrid();
|
||||
CardSet firstCardSet = getFirstItem(grid);
|
||||
|
||||
CardSetForm form = cardSetView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstCardSet);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstCardSet.getName(), form.name.getValue());
|
||||
}
|
||||
|
||||
private CardSet getFirstItem(Grid<CardSet> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<CardSet> cardSets = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(15, count);
|
||||
return cardSets.get(0);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package de.thpeetz.kontor.views.tysc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.tysc.Card;
|
||||
import de.thpeetz.kontor.views.tysc.CardForm;
|
||||
import de.thpeetz.kontor.views.tysc.CardView;
|
||||
|
||||
@SpringBootTest
|
||||
class CardViewTest {
|
||||
|
||||
@Autowired
|
||||
private CardView cardView;
|
||||
|
||||
@Test
|
||||
void formShownWhenCardSelected() {
|
||||
Grid<Card> grid = cardView.getGrid();
|
||||
Card firstCard = getFirstItem(grid);
|
||||
|
||||
CardForm form = cardView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstCard);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(String.valueOf(firstCard.getCardNumber()), form.cardNumber.getValue());
|
||||
}
|
||||
|
||||
private Card getFirstItem(Grid<Card> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Card> cards = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(10, count);
|
||||
return cards.get(0);
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package de.thpeetz.kontor.views.tysc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.tysc.FieldPosition;
|
||||
import de.thpeetz.kontor.views.tysc.PositionForm;
|
||||
import de.thpeetz.kontor.views.tysc.PositionView;
|
||||
|
||||
@SpringBootTest
|
||||
class FieldPositionViewTest {
|
||||
|
||||
@Autowired
|
||||
private PositionView positionView;
|
||||
|
||||
@Test
|
||||
void formShownWhenPositionSelected() {
|
||||
Grid<FieldPosition> grid = positionView.getGrid();
|
||||
FieldPosition firstFieldPosition = getFirstItem(grid);
|
||||
|
||||
PositionForm form = positionView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstFieldPosition);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstFieldPosition.getName(), form.name.getValue());
|
||||
}
|
||||
|
||||
private FieldPosition getFirstItem(Grid<FieldPosition> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<FieldPosition> positions = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(44, count);
|
||||
return positions.get(0);
|
||||
}
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
package de.thpeetz.kontor.views.tysc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.tysc.Player;
|
||||
import de.thpeetz.kontor.views.tysc.PlayerForm;
|
||||
import de.thpeetz.kontor.views.tysc.PlayerView;
|
||||
|
||||
@SpringBootTest
|
||||
class PlayerViewTest {
|
||||
|
||||
@Autowired
|
||||
private PlayerView playerView;
|
||||
|
||||
@Test
|
||||
void formShownWhenPlayerSelected() {
|
||||
Grid<Player> grid = playerView.getGrid();
|
||||
Player firstPlayer = getFirstItem(grid);
|
||||
|
||||
PlayerForm form = playerView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstPlayer);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstPlayer.getLastName(), form.lastName.getValue());
|
||||
assertEquals(firstPlayer.getFirstName(), form.firstName.getValue());
|
||||
}
|
||||
|
||||
private Player getFirstItem(Grid<Player> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Player> players = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(38, count);
|
||||
return players.get(0);
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package de.thpeetz.kontor.views.tysc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.tysc.Rooster;
|
||||
import de.thpeetz.kontor.views.tysc.RoosterForm;
|
||||
import de.thpeetz.kontor.views.tysc.RoosterView;
|
||||
|
||||
@SpringBootTest
|
||||
class RoosterViewTest {
|
||||
|
||||
@Autowired
|
||||
private RoosterView roosterView;
|
||||
|
||||
@Test
|
||||
void formShownWhenRoosterSelected() {
|
||||
Grid<Rooster> grid = roosterView.getGrid();
|
||||
Rooster firstRooster = getFirstItem(grid);
|
||||
|
||||
RoosterForm form = roosterView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstRooster);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstRooster.getYear(), form.year.getValue());
|
||||
}
|
||||
|
||||
private Rooster getFirstItem(Grid<Rooster> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Rooster> roosters = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(11, count);
|
||||
return roosters.get(0);
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package de.thpeetz.kontor.views.tysc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.tysc.Sport;
|
||||
import de.thpeetz.kontor.views.tysc.SportForm;
|
||||
import de.thpeetz.kontor.views.tysc.SportView;
|
||||
|
||||
@SpringBootTest
|
||||
class SportViewTest {
|
||||
|
||||
@Autowired
|
||||
private SportView sportView;
|
||||
|
||||
@Test
|
||||
void formShownWhenSportSelected() {
|
||||
Grid<Sport> grid = sportView.getGrid();
|
||||
Sport firstSport = getFirstItem(grid);
|
||||
|
||||
SportForm form = sportView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstSport);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstSport.getName(), form.name.getValue());
|
||||
}
|
||||
|
||||
private Sport getFirstItem(Grid<Sport> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Sport> sports = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(4, count);
|
||||
return sports.get(0);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package de.thpeetz.kontor.views.tysc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.tysc.Team;
|
||||
import de.thpeetz.kontor.views.tysc.TeamForm;
|
||||
import de.thpeetz.kontor.views.tysc.TeamView;
|
||||
|
||||
@SpringBootTest
|
||||
class TeamViewTest {
|
||||
|
||||
@Autowired
|
||||
private TeamView teamView;
|
||||
|
||||
@Test
|
||||
void formShownWhenTeamSelected() {
|
||||
Grid<Team> grid = teamView.getGrid();
|
||||
Team firstTeam = getFirstItem(grid);
|
||||
|
||||
TeamForm form = teamView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstTeam);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstTeam.getName(), form.name.getValue());
|
||||
}
|
||||
|
||||
private Team getFirstItem(Grid<Team> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Team> teams = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(122, count);
|
||||
return teams.get(0);
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package de.thpeetz.kontor.views.tysc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
|
||||
import de.thpeetz.kontor.data.tysc.Vendor;
|
||||
import de.thpeetz.kontor.views.tysc.VendorForm;
|
||||
import de.thpeetz.kontor.views.tysc.VendorView;
|
||||
|
||||
@SpringBootTest
|
||||
class VendorViewTest {
|
||||
|
||||
@Autowired
|
||||
private VendorView vendorView;
|
||||
|
||||
@Test
|
||||
void formShownWhenVendorSelected() {
|
||||
Grid<Vendor> grid = vendorView.getGrid();
|
||||
Vendor firstVendor = getFirstItem(grid);
|
||||
|
||||
VendorForm form = vendorView.getForm();
|
||||
|
||||
assertFalse(form.isVisible());
|
||||
grid.asSingleSelect().setValue(firstVendor);
|
||||
assertTrue(form.isVisible());
|
||||
assertEquals(firstVendor.getName(), form.name.getValue());
|
||||
}
|
||||
|
||||
private Vendor getFirstItem(Grid<Vendor> grid) {
|
||||
int count = grid.getListDataView().getItemCount();
|
||||
List<Vendor> vendors = grid.getListDataView().getItems().collect(Collectors.toList());
|
||||
assertEquals(9, count);
|
||||
return vendors.get(0);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
server.port=8085
|
||||
|
||||
spring.hibernate.dialect=org.hibernate.dialect.HSQLDialect
|
||||
spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect
|
||||
spring.datasource.driverClassName=org.hsqldb.jdbc.JDBCDriver
|
||||
spring.datasource.url=jdbc:hsqldb:mem:itDb
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=sa
|
||||
|
||||
#spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect
|
||||
#spring.datasource.driverClassName=org.sqlite.JDBC
|
||||
#spring.datasource.url=jdbc:sqlite:file:./kontorITDb?cache=shared
|
||||
#spring.datasource.username=sa
|
||||
#spring.datasource.password=sa
|
||||
|
||||
spring.jpa.defer-datasource-initialization = true
|
||||
#spring.jpa.hibernate.ddl-auto=create-drop
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.show-sql=false
|
||||
spring.sql.init.mode=always
|
||||
|
||||
spring.mustache.check-template-location = false
|
||||
|
||||
logging.level.org.atmosphere=INFO
|
||||
logging.level.org.springframework.web=INFO
|
||||
logging.level.guru.springframework.controllers=DEBUG
|
||||
logging.level.org.hibernate=INFO
|
||||
logging.level.de.thpeetz=DEBUG
|
||||
|
||||
jwt.auth.secret=J6GOtcwC2NJI1l0VkHu20PacPFGTxpirBxWwynoHjsc=
|
||||
@@ -0,0 +1 @@
|
||||
export {}
|
||||
@@ -0,0 +1 @@
|
||||
export declare const applyCss: (target: Node) => void;
|
||||
@@ -0,0 +1,707 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
/// <reference lib="es2018" />
|
||||
import { Flow as _Flow } from 'Frontend/generated/jar-resources/Flow.js';
|
||||
import React, { useCallback, useEffect, useReducer, useRef, useState, type ReactNode } from 'react';
|
||||
import { matchRoutes, useBlocker, useLocation, useNavigate, type NavigateOptions, useHref } from 'react-router';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
const flow = new _Flow({
|
||||
imports: () => import('Frontend/generated/flow/generated-flow-imports.js')
|
||||
});
|
||||
|
||||
const router = {
|
||||
render() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
const flowReact : { active: boolean } = {
|
||||
active: false,
|
||||
}
|
||||
|
||||
// ClickHandler for vaadin-router-go event is copied from vaadin/router click.js
|
||||
// @ts-ignore
|
||||
function getAnchorOrigin(anchor) {
|
||||
// IE11: on HTTP and HTTPS the default port is not included into
|
||||
// window.location.origin, so won't include it here either.
|
||||
const port = anchor.port;
|
||||
const protocol = anchor.protocol;
|
||||
const defaultHttp = protocol === 'http:' && port === '80';
|
||||
const defaultHttps = protocol === 'https:' && port === '443';
|
||||
const host =
|
||||
defaultHttp || defaultHttps
|
||||
? anchor.hostname // does not include the port number (e.g. www.example.org)
|
||||
: anchor.host; // does include the port number (e.g. www.example.org:80)
|
||||
return `${protocol}//${host}`;
|
||||
}
|
||||
|
||||
function normalizeURL(url: URL): void | string {
|
||||
// ignore click if baseURI does not match the document (external)
|
||||
if (!url.href.startsWith(document.baseURI)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize path against baseURI
|
||||
return '/' + url.href.slice(document.baseURI.length);
|
||||
}
|
||||
|
||||
function extractURL(event: MouseEvent): void | URL {
|
||||
// ignore the click if the default action is prevented
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore the click if not with the primary mouse button
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore the click if a modifier key is pressed
|
||||
if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find the <a> element that the click is at (or within)
|
||||
let maybeAnchor = event.target;
|
||||
const path = event.composedPath
|
||||
? event.composedPath()
|
||||
: // @ts-ignore
|
||||
event.path || [];
|
||||
|
||||
// example to check: `for...of` loop here throws the "Not yet implemented" error
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
const target = path[i];
|
||||
if (target.nodeName && target.nodeName.toLowerCase() === 'a') {
|
||||
maybeAnchor = target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
while (maybeAnchor && maybeAnchor.nodeName.toLowerCase() !== 'a') {
|
||||
// @ts-ignore
|
||||
maybeAnchor = maybeAnchor.parentNode;
|
||||
}
|
||||
|
||||
// ignore the click if not at an <a> element
|
||||
// @ts-ignore
|
||||
if (!maybeAnchor || maybeAnchor.nodeName.toLowerCase() !== 'a') {
|
||||
return;
|
||||
}
|
||||
|
||||
const anchor = maybeAnchor as HTMLAnchorElement;
|
||||
|
||||
// ignore the click if the <a> element has a non-default target
|
||||
if (anchor.target && anchor.target.toLowerCase() !== '_self') {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore the click if the <a> element has the 'download' attribute
|
||||
if (anchor.hasAttribute('download')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore the click if the <a> element has the 'router-ignore' attribute
|
||||
if (anchor.hasAttribute('router-ignore')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore the click if the target URL is a fragment on the current page
|
||||
if (anchor.pathname === window.location.pathname && anchor.hash !== '') {
|
||||
// @ts-ignore
|
||||
window.location.hash = anchor.hash;
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore the click if the target is external to the app
|
||||
// In IE11 HTMLAnchorElement does not have the `origin` property
|
||||
// @ts-ignore
|
||||
const origin = anchor.origin || getAnchorOrigin(anchor);
|
||||
if (origin !== window.location.origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
return new URL(anchor.href, anchor.baseURI);
|
||||
}
|
||||
|
||||
function extractPath(event: MouseEvent): void | string {
|
||||
const url = extractURL(event);
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
return normalizeURL(url);
|
||||
}
|
||||
|
||||
export const registerGlobalClickHandler = () => {
|
||||
window.addEventListener('click', (event: MouseEvent) => {
|
||||
if (flowReact.active) {
|
||||
return;
|
||||
}
|
||||
const url = extractURL(event);
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
// ignore click if baseURI does not match the document (external)
|
||||
if (!url.href.startsWith(document.baseURI)) {
|
||||
return;
|
||||
}
|
||||
if (event && event.preventDefault) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// Normalize path against baseURI
|
||||
const path = url.pathname + url.search + url.hash;
|
||||
const state = {...window.history.state}
|
||||
if (state.idx !== undefined) {
|
||||
state.idx = state.idx + 1;
|
||||
}
|
||||
window.history.pushState(state, '', path);
|
||||
window.dispatchEvent(new PopStateEvent('popstate'));
|
||||
}, { capture: false });
|
||||
};
|
||||
|
||||
/**
|
||||
* Fire 'vaadin-navigated' event to inform components of navigation.
|
||||
* @param pathname pathname of navigation
|
||||
* @param search search of navigation
|
||||
*/
|
||||
function fireNavigated(pathname: string, search: string) {
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('vaadin-navigated', {
|
||||
detail: {
|
||||
pathname,
|
||||
search
|
||||
}
|
||||
})
|
||||
);
|
||||
// @ts-ignore
|
||||
delete window.Vaadin.Flow.navigation;
|
||||
});
|
||||
}
|
||||
|
||||
function postpone() {}
|
||||
|
||||
const prevent = () => postpone;
|
||||
|
||||
type RouterContainer = Awaited<ReturnType<(typeof flow.serverSideRoutes)[0]['action']>>;
|
||||
|
||||
type PortalEntry = {
|
||||
readonly children: ReactNode;
|
||||
readonly domNode: HTMLElement;
|
||||
};
|
||||
|
||||
type FlowPortalProps = React.PropsWithChildren<
|
||||
Readonly<{
|
||||
domNode: HTMLElement;
|
||||
onRemove(): void;
|
||||
}>
|
||||
>;
|
||||
|
||||
function FlowPortal({ children, domNode, onRemove }: FlowPortalProps) {
|
||||
useEffect(() => {
|
||||
domNode.addEventListener(
|
||||
'flow-portal-remove',
|
||||
(event: Event) => {
|
||||
event.preventDefault();
|
||||
onRemove();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}, []);
|
||||
|
||||
return createPortal(children, domNode);
|
||||
}
|
||||
|
||||
const ADD_FLOW_PORTAL = 'ADD_FLOW_PORTAL';
|
||||
|
||||
type AddFlowPortalAction = Readonly<{
|
||||
type: typeof ADD_FLOW_PORTAL;
|
||||
portal: React.ReactElement<FlowPortalProps>;
|
||||
}>;
|
||||
|
||||
function addFlowPortal(portal: React.ReactElement<FlowPortalProps>): AddFlowPortalAction {
|
||||
return {
|
||||
type: ADD_FLOW_PORTAL,
|
||||
portal
|
||||
};
|
||||
}
|
||||
|
||||
const REMOVE_FLOW_PORTAL = 'REMOVE_FLOW_PORTAL';
|
||||
|
||||
type RemoveFlowPortalAction = Readonly<{
|
||||
type: typeof REMOVE_FLOW_PORTAL;
|
||||
key: string;
|
||||
}>;
|
||||
|
||||
function removeFlowPortal(key: string): RemoveFlowPortalAction {
|
||||
return {
|
||||
type: REMOVE_FLOW_PORTAL,
|
||||
key
|
||||
};
|
||||
}
|
||||
|
||||
function flowPortalsReducer(
|
||||
portals: readonly React.ReactElement<FlowPortalProps>[],
|
||||
action: AddFlowPortalAction | RemoveFlowPortalAction
|
||||
) {
|
||||
switch (action.type) {
|
||||
case ADD_FLOW_PORTAL:
|
||||
return [...portals, action.portal];
|
||||
case REMOVE_FLOW_PORTAL:
|
||||
return portals.filter(({ key }) => key !== action.key);
|
||||
default:
|
||||
return portals;
|
||||
}
|
||||
}
|
||||
|
||||
type NavigateOpts = {
|
||||
to: string;
|
||||
callback: boolean;
|
||||
opts?: NavigateOptions;
|
||||
};
|
||||
|
||||
type NavigateFn = (to: string, callback: boolean, opts?: NavigateOptions) => void;
|
||||
|
||||
let navigateInProgress = false;
|
||||
/**
|
||||
* A hook providing the `navigate(path: string, opts?: NavigateOptions)` function
|
||||
* with React Router API that has more consistent history updates. Uses internal
|
||||
* queue for processing navigate calls.
|
||||
*/
|
||||
function useQueuedNavigate(
|
||||
waitReference: React.MutableRefObject<Promise<void> | undefined>,
|
||||
navigated: React.MutableRefObject<boolean>
|
||||
): NavigateFn {
|
||||
const navigate = useNavigate();
|
||||
const navigateQueue = useRef<NavigateOpts[]>([]).current;
|
||||
const [navigateQueueLength, setNavigateQueueLength] = useState(0);
|
||||
|
||||
const dequeueNavigation = useCallback(() => {
|
||||
if (navigateInProgress) {
|
||||
dequeueNavigationAfterCurrentTask();
|
||||
return;
|
||||
}
|
||||
|
||||
const navigateArgs = navigateQueue.shift();
|
||||
if (navigateArgs === undefined) {
|
||||
// Empty queue, do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
const blockingNavigate = async () => {
|
||||
if (waitReference.current) {
|
||||
await waitReference.current;
|
||||
waitReference.current = undefined;
|
||||
}
|
||||
navigated.current = !navigateArgs.callback;
|
||||
navigateInProgress = true;
|
||||
navigate(navigateArgs.to, navigateArgs.opts);
|
||||
setNavigateQueueLength(navigateQueue.length);
|
||||
};
|
||||
blockingNavigate();
|
||||
}, [navigate, setNavigateQueueLength]);
|
||||
|
||||
const dequeueNavigationAfterCurrentTask = useCallback(() => {
|
||||
setTimeout(dequeueNavigation, 0);
|
||||
}, [dequeueNavigation]);
|
||||
|
||||
const enqueueNavigation = useCallback(
|
||||
(to: string, callback: boolean, opts?: NavigateOptions) => {
|
||||
navigateQueue.push({ to: to, callback: callback, opts: opts });
|
||||
setNavigateQueueLength(navigateQueue.length);
|
||||
if (navigateQueue.length === 1) {
|
||||
// The first navigation can be started right after any pending sync
|
||||
// jobs, which could add more navigations to the queue.
|
||||
dequeueNavigationAfterCurrentTask();
|
||||
}
|
||||
},
|
||||
[setNavigateQueueLength, dequeueNavigationAfterCurrentTask]
|
||||
);
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
// The Flow component has rendered, but history might not be
|
||||
// updated yet, as React Router does it asynchronously.
|
||||
// Use microtask callback for history consistency.
|
||||
dequeueNavigationAfterCurrentTask();
|
||||
},
|
||||
[navigateQueueLength, dequeueNavigationAfterCurrentTask]
|
||||
);
|
||||
|
||||
return enqueueNavigation;
|
||||
}
|
||||
|
||||
const flowNavigation = () => {
|
||||
// @ts-ignore
|
||||
window.Vaadin.Flow.navigation = true;
|
||||
};
|
||||
|
||||
function Flow() {
|
||||
const ref = useRef<HTMLOutputElement>(null);
|
||||
const navigate = useNavigate();
|
||||
const blocker = useBlocker(({ currentLocation, nextLocation }) => {
|
||||
navigated.current =
|
||||
navigated.current ||
|
||||
(nextLocation.pathname === currentLocation.pathname &&
|
||||
nextLocation.search === currentLocation.search &&
|
||||
nextLocation.hash === currentLocation.hash);
|
||||
return true;
|
||||
});
|
||||
const location = useLocation();
|
||||
const navigated = useRef<boolean>(false);
|
||||
const blockerHandled = useRef<boolean>(false);
|
||||
const fromAnchor = useRef<boolean>(false);
|
||||
const containerRef = useRef<RouterContainer | undefined>(undefined);
|
||||
const roundTrip = useRef<Promise<void> | undefined>(undefined);
|
||||
const queuedNavigate = useQueuedNavigate(roundTrip, navigated);
|
||||
const basename = useHref('/');
|
||||
|
||||
// portalsReducer function is used as state outside the Flow component.
|
||||
const [portals, dispatchPortalAction] = useReducer(flowPortalsReducer, []);
|
||||
|
||||
const addPortalEventHandler = useCallback(
|
||||
(event: CustomEvent<PortalEntry>) => {
|
||||
event.preventDefault();
|
||||
|
||||
const key = Math.random().toString(36).slice(2);
|
||||
dispatchPortalAction(
|
||||
addFlowPortal(
|
||||
<FlowPortal
|
||||
key={key}
|
||||
domNode={event.detail.domNode}
|
||||
onRemove={() => dispatchPortalAction(removeFlowPortal(key))}
|
||||
>
|
||||
{event.detail.children}
|
||||
</FlowPortal>
|
||||
)
|
||||
);
|
||||
},
|
||||
[dispatchPortalAction]
|
||||
);
|
||||
|
||||
const navigateEventHandler = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
const path = extractPath(event);
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event && event.preventDefault) {
|
||||
event.preventDefault();
|
||||
}
|
||||
navigated.current = false;
|
||||
// When navigation is triggered by click on a link, fromAnchor is set to true
|
||||
// in order to get a server round-trip even when navigating to the same URL again
|
||||
fromAnchor.current = true;
|
||||
// @ts-ignore
|
||||
window.Vaadin.Flow.navigation = true;
|
||||
navigate(path);
|
||||
// Dispatch close event for overlay drawer on click navigation.
|
||||
window.dispatchEvent(new CustomEvent('close-overlay-drawer'));
|
||||
},
|
||||
[navigate]
|
||||
);
|
||||
|
||||
const vaadinRouterGoEventHandler = useCallback(
|
||||
(event: CustomEvent<URL>) => {
|
||||
const url = event.detail;
|
||||
const path = normalizeURL(url);
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
navigate(path);
|
||||
},
|
||||
[navigate]
|
||||
);
|
||||
|
||||
const vaadinNavigateEventHandler = useCallback(
|
||||
(event: CustomEvent<{ state: unknown; url: string; replace?: boolean; callback: boolean }>) => {
|
||||
// @ts-ignore
|
||||
window.Vaadin.Flow.navigation = true;
|
||||
// clean base uri away if for instance redirected to http://localhost/path/user?id=10
|
||||
// else the whole http... will be appended to the url see #19580
|
||||
const path = event.detail.url.startsWith(document.baseURI)
|
||||
? '/' + event.detail.url.slice(document.baseURI.length)
|
||||
: '/' + event.detail.url;
|
||||
fromAnchor.current = false;
|
||||
queuedNavigate(path, event.detail.callback, { state: event.detail.state, replace: event.detail.replace });
|
||||
},
|
||||
[navigate]
|
||||
);
|
||||
|
||||
const redirect = useCallback(
|
||||
(path: string) => {
|
||||
return () => {
|
||||
navigate(path, { replace: true });
|
||||
};
|
||||
},
|
||||
[navigate]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// @ts-ignore
|
||||
window.addEventListener('vaadin-router-go', vaadinRouterGoEventHandler);
|
||||
// @ts-ignore
|
||||
window.addEventListener('vaadin-navigate', vaadinNavigateEventHandler);
|
||||
|
||||
return () => {
|
||||
// @ts-ignore
|
||||
window.removeEventListener('vaadin-router-go', vaadinRouterGoEventHandler);
|
||||
// @ts-ignore
|
||||
window.removeEventListener('vaadin-navigate', vaadinNavigateEventHandler);
|
||||
};
|
||||
}, [vaadinRouterGoEventHandler, vaadinNavigateEventHandler]);
|
||||
|
||||
useEffect(() => {
|
||||
// @ts-ignore
|
||||
window.addEventListener("popstate", flowNavigation);
|
||||
window.addEventListener('click', navigateEventHandler);
|
||||
flowReact.active = true;
|
||||
|
||||
return () => {
|
||||
containerRef.current?.parentNode?.removeChild(containerRef.current);
|
||||
containerRef.current?.removeEventListener('flow-portal-add', addPortalEventHandler as EventListener);
|
||||
containerRef.current = undefined;
|
||||
// @ts-ignore
|
||||
window.removeEventListener("popstate", flowNavigation);
|
||||
window.removeEventListener('click', navigateEventHandler);
|
||||
flowReact.active = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (blocker.state === 'blocked') {
|
||||
if (blockerHandled.current) {
|
||||
// Blocker is handled and the new navigation
|
||||
// gets queued to be executed after the current handling ends.
|
||||
const { pathname, state } = blocker.location;
|
||||
// Clear base name to not get /baseName/basename/path
|
||||
const pathNoBase = pathname.substring(basename.length);
|
||||
// path should always start with / else react-router will append to current url
|
||||
queuedNavigate(pathNoBase.startsWith('/') ? pathNoBase : '/' + pathNoBase, true, {
|
||||
state: state,
|
||||
replace: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
blockerHandled.current = true;
|
||||
let blockingPromise: any;
|
||||
roundTrip.current = new Promise<void>(
|
||||
(resolve, reject) => (blockingPromise = { resolve: resolve, reject: reject })
|
||||
);
|
||||
// Release blocker handling after promise is fulfilled
|
||||
roundTrip.current.then(
|
||||
() => (blockerHandled.current = false),
|
||||
() => (blockerHandled.current = false)
|
||||
);
|
||||
|
||||
// Proceed to the blocked location, unless the navigation originates from a click on a link.
|
||||
// In that case continue with function execution and perform a server round-trip
|
||||
if (navigated.current && !fromAnchor.current) {
|
||||
blocker.proceed();
|
||||
blockingPromise.resolve();
|
||||
navigateInProgress = false;
|
||||
return;
|
||||
}
|
||||
fromAnchor.current = false;
|
||||
const { pathname, search } = blocker.location;
|
||||
const routes = ((window as any)?.Vaadin?.routesConfig || []) as any[];
|
||||
let matched = matchRoutes(Array.from(routes), pathname);
|
||||
|
||||
// Navigation between server routes
|
||||
// @ts-ignore
|
||||
if (matched && matched.filter((path) => path.route?.element?.type?.name === Flow.name).length != 0) {
|
||||
containerRef.current?.onBeforeEnter?.call(
|
||||
containerRef?.current,
|
||||
{ pathname, search },
|
||||
{
|
||||
prevent() {
|
||||
blocker.reset();
|
||||
blockingPromise.resolve();
|
||||
navigateInProgress = false;
|
||||
navigated.current = false;
|
||||
},
|
||||
redirect,
|
||||
continue() {
|
||||
blocker.proceed();
|
||||
blockingPromise.resolve();
|
||||
navigateInProgress = false;
|
||||
}
|
||||
},
|
||||
router
|
||||
);
|
||||
navigated.current = true;
|
||||
} else {
|
||||
// For covering the 'server -> client' use case
|
||||
Promise.resolve(
|
||||
containerRef.current?.onBeforeLeave?.call(
|
||||
containerRef?.current,
|
||||
{
|
||||
pathname,
|
||||
search
|
||||
},
|
||||
{ prevent },
|
||||
router
|
||||
)
|
||||
).then((cmd: unknown) => {
|
||||
if (cmd === postpone && containerRef.current) {
|
||||
// postponed navigation: expose existing blocker to Flow
|
||||
containerRef.current.serverConnected = (cancel) => {
|
||||
if (cancel) {
|
||||
blocker.reset();
|
||||
} else {
|
||||
blocker.proceed();
|
||||
}
|
||||
blockingPromise.resolve();
|
||||
navigateInProgress = false;
|
||||
};
|
||||
} else {
|
||||
// permitted navigation: proceed with the blocker
|
||||
blocker.proceed();
|
||||
blockingPromise.resolve();
|
||||
navigateInProgress = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [blocker.state, blocker.location]);
|
||||
|
||||
useEffect(() => {
|
||||
if (blocker.state === 'blocked') {
|
||||
return;
|
||||
}
|
||||
if (navigated.current) {
|
||||
navigated.current = false;
|
||||
fireNavigated(location.pathname, location.search);
|
||||
return;
|
||||
}
|
||||
flow.serverSideRoutes[0]
|
||||
.action({ pathname: location.pathname, search: location.search })
|
||||
.then((container) => {
|
||||
const outlet = ref.current?.parentNode;
|
||||
if (outlet && outlet !== container.parentNode) {
|
||||
outlet.append(container);
|
||||
container.addEventListener('flow-portal-add', addPortalEventHandler as EventListener);
|
||||
containerRef.current = container;
|
||||
}
|
||||
return container.onBeforeEnter?.call(
|
||||
container,
|
||||
// Always add base to path as it is cleaned in getFlowRoutePath and will break a route starting with basename
|
||||
{ pathname: basename + location.pathname, search: location.search },
|
||||
{
|
||||
prevent,
|
||||
redirect,
|
||||
continue() {
|
||||
fireNavigated(location.pathname, location.search);
|
||||
}
|
||||
},
|
||||
router
|
||||
);
|
||||
})
|
||||
.then((result: unknown) => {
|
||||
if (typeof result === 'function') {
|
||||
result();
|
||||
}
|
||||
});
|
||||
}, [location]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<output ref={ref} style={{ display: 'none' }} />
|
||||
{portals}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Flow.type = 'FlowContainer'; // This is for copilot to recognize this
|
||||
|
||||
export const serverSideRoutes = [{ path: '/*', element: <Flow /> }];
|
||||
|
||||
/**
|
||||
* Load the script for an exported WebComponent with the given tag
|
||||
*
|
||||
* @param tag name of the exported web-component to load
|
||||
*
|
||||
* @returns Promise(resolve, reject) that is fulfilled on script load
|
||||
*/
|
||||
export const loadComponentScript = (tag: String): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
useEffect(() => {
|
||||
const script = document.createElement('script');
|
||||
script.src = `/web-component/${tag}.js`;
|
||||
script.onload = function () {
|
||||
resolve();
|
||||
};
|
||||
script.onerror = function (err) {
|
||||
reject(err);
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
|
||||
return () => {
|
||||
document.head.removeChild(script);
|
||||
};
|
||||
}, []);
|
||||
});
|
||||
};
|
||||
|
||||
interface Properties {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load WebComponent script and create a React element for the WebComponent.
|
||||
*
|
||||
* @param tag custom web-component tag name.
|
||||
* @param props optional Properties object to create element attributes with
|
||||
* @param onload optional callback to be called for script onload
|
||||
* @param onerror optional callback for error loading the script
|
||||
*/
|
||||
export const reactElement = (tag: string, props?: Properties, onload?: () => void, onerror?: (err: any) => void) => {
|
||||
loadComponentScript(tag).then(
|
||||
() => onload?.(),
|
||||
(err) => {
|
||||
if (onerror) {
|
||||
onerror(err);
|
||||
} else {
|
||||
console.error(`Failed to load script for ${tag}.`, err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (props) {
|
||||
return React.createElement(tag, props);
|
||||
}
|
||||
return React.createElement(tag);
|
||||
};
|
||||
|
||||
export default Flow;
|
||||
|
||||
// @ts-ignore
|
||||
if (import.meta.hot) {
|
||||
// @ts-ignore
|
||||
import.meta.hot.accept((newModule) => {
|
||||
// A hot module replace for Flow.tsx happens when any JS/TS imported through @JsModule
|
||||
// or similar is updated because this updates generated-flow-imports.js and that in turn
|
||||
// is imported by this file. We have no means of hot replacing those files, e.g. some
|
||||
// custom lit element so we need to reload the page. */
|
||||
if (newModule) {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
import { createRoot, Root } from 'react-dom/client';
|
||||
import { createElement, type Dispatch, type ReactElement, type ReactNode, useEffect, useReducer } from 'react';
|
||||
|
||||
type FlowStateKeyChangedAction<K extends string, V> = Readonly<{
|
||||
type: 'stateKeyChanged';
|
||||
key: K;
|
||||
value: V;
|
||||
}>;
|
||||
|
||||
type FlowStateReducerAction = FlowStateKeyChangedAction<string, unknown>;
|
||||
|
||||
function stateReducer<S extends Readonly<Record<string, unknown>>>(state: S, action: FlowStateReducerAction): S {
|
||||
switch (action.type) {
|
||||
case 'stateKeyChanged':
|
||||
const { value } = action;
|
||||
return {
|
||||
...state,
|
||||
key: value
|
||||
} as S;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
type DispatchEvent<T> = T extends undefined ? () => boolean : (value: T) => boolean;
|
||||
|
||||
const emptyAction: Dispatch<unknown> = () => {};
|
||||
|
||||
/**
|
||||
* An object with APIs exposed for using in the {@link ReactAdapterElement#render}
|
||||
* implementation.
|
||||
*/
|
||||
export type RenderHooks = {
|
||||
/**
|
||||
* A hook API for using stateful JS properties of the Web Component from
|
||||
* the React `render()`.
|
||||
*
|
||||
* @typeParam T - Type of the state value
|
||||
*
|
||||
* @param key - Web Component property name, which is used for two-way
|
||||
* value propagation from the server and back.
|
||||
* @param initialValue - Fallback initial value (optional). Only applies if
|
||||
* the Java component constructor does not invoke `setState`.
|
||||
* @returns A tuple with two values:
|
||||
* 1. The current state.
|
||||
* 2. The `set` function for changing the state and triggering render
|
||||
* @protected
|
||||
*/
|
||||
readonly useState: ReactAdapterElement['useState'];
|
||||
|
||||
/**
|
||||
* A hook helper to simplify dispatching a `CustomEvent` on the Web
|
||||
* Component from React.
|
||||
*
|
||||
* @typeParam T - The type for `event.detail` value (optional).
|
||||
*
|
||||
* @param type - The `CustomEvent` type string.
|
||||
* @param options - The settings for the `CustomEvent`.
|
||||
* @returns The `dispatch` function. The function parameters change
|
||||
* depending on the `T` generic type:
|
||||
* - For `undefined` type (default), has no parameters.
|
||||
* - For other types, has one parameter for the `event.detail` value of that type.
|
||||
* @protected
|
||||
*/
|
||||
readonly useCustomEvent: ReactAdapterElement['useCustomEvent'];
|
||||
|
||||
/**
|
||||
* A hook helper to generate the content element with name attribute to bind
|
||||
* the server-side Flow element for this component.
|
||||
*
|
||||
* This is used together with {@link ReactAdapterComponent::getContentElement}
|
||||
* to have server-side component attach to the correct client element.
|
||||
*
|
||||
* Usage as follows:
|
||||
*
|
||||
* const content = hooks.useContent('content');
|
||||
* return <>
|
||||
* {content}
|
||||
* </>;
|
||||
*
|
||||
* Note! Not adding the 'content' element into the dom will have the
|
||||
* server throw a IllegalStateException for element with tag name not found.
|
||||
*
|
||||
* @param name - The name attribute of the element
|
||||
*/
|
||||
readonly useContent: ReactAdapterElement['useContent'];
|
||||
};
|
||||
|
||||
interface ReadyCallbackFunction {
|
||||
(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A base class for Web Components that render using React. Enables creating
|
||||
* adapters for integrating React components with Flow. Intended for use with
|
||||
* `ReactAdapterComponent` Flow Java class.
|
||||
*/
|
||||
export abstract class ReactAdapterElement extends HTMLElement {
|
||||
#root: Root | undefined = undefined;
|
||||
#rootRendered: boolean = false;
|
||||
#rendering: ReactNode | undefined = undefined;
|
||||
|
||||
#state: Record<string, unknown> = Object.create(null);
|
||||
#stateSetters = new Map<string, Dispatch<unknown>>();
|
||||
#customEvents = new Map<string, DispatchEvent<unknown>>();
|
||||
#dispatchFlowState: Dispatch<FlowStateReducerAction> = emptyAction;
|
||||
|
||||
#readyCallback = new Map<string, ReadyCallbackFunction>();
|
||||
|
||||
readonly #renderHooks: RenderHooks;
|
||||
|
||||
readonly #Wrapper: () => ReactElement | null;
|
||||
|
||||
#unmounting?: Promise<void>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#renderHooks = {
|
||||
useState: this.useState.bind(this),
|
||||
useCustomEvent: this.useCustomEvent.bind(this),
|
||||
useContent: this.useContent.bind(this)
|
||||
};
|
||||
this.#Wrapper = this.#renderWrapper.bind(this);
|
||||
this.#markAsUsed();
|
||||
}
|
||||
|
||||
public async connectedCallback() {
|
||||
this.#rendering = createElement(this.#Wrapper);
|
||||
const createNewRoot = this.dispatchEvent(
|
||||
new CustomEvent('flow-portal-add', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
children: this.#rendering,
|
||||
domNode: this
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
if (!createNewRoot || this.#root) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.#unmounting;
|
||||
|
||||
this.#root = createRoot(this);
|
||||
this.#maybeRenderRoot();
|
||||
this.#root.render(this.#rendering);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback for specified element identifier to be called when
|
||||
* react element is ready.
|
||||
* <p>
|
||||
* For internal use only. May be renamed or removed in a future release.
|
||||
*
|
||||
* @param id element identifier that callback is for
|
||||
* @param readyCallback callback method to be informed on element ready state
|
||||
* @internal
|
||||
*/
|
||||
public addReadyCallback(id: string, readyCallback: ReadyCallbackFunction) {
|
||||
this.#readyCallback.set(id, readyCallback);
|
||||
}
|
||||
|
||||
public async disconnectedCallback() {
|
||||
if (!this.#root) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('flow-portal-remove', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
children: this.#rendering,
|
||||
domNode: this
|
||||
}
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this.#unmounting = Promise.resolve();
|
||||
await this.#unmounting;
|
||||
this.#root.unmount();
|
||||
this.#root = undefined;
|
||||
}
|
||||
this.#rootRendered = false;
|
||||
this.#rendering = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook API for using stateful JS properties of the Web Component from
|
||||
* the React `render()`.
|
||||
*
|
||||
* @typeParam T - Type of the state value
|
||||
*
|
||||
* @param key - Web Component property name, which is used for two-way
|
||||
* value propagation from the server and back.
|
||||
* @param initialValue - Fallback initial value (optional). Only applies if
|
||||
* the Java component constructor does not invoke `setState`.
|
||||
* @returns A tuple with two values:
|
||||
* 1. The current state.
|
||||
* 2. The `set` function for changing the state and triggering render
|
||||
* @protected
|
||||
*/
|
||||
protected useState<T>(key: string, initialValue?: T): [value: T, setValue: Dispatch<T>] {
|
||||
if (this.#stateSetters.has(key)) {
|
||||
return [this.#state[key] as T, this.#stateSetters.get(key)!];
|
||||
}
|
||||
|
||||
const value = ((this as Record<string, unknown>)[key] as T) ?? initialValue!;
|
||||
this.#state[key] = value;
|
||||
Object.defineProperty(this, key, {
|
||||
enumerable: true,
|
||||
get(): T {
|
||||
return this.#state[key];
|
||||
},
|
||||
set(nextValue: T) {
|
||||
this.#state[key] = nextValue;
|
||||
this.#dispatchFlowState({ type: 'stateKeyChanged', key, value });
|
||||
}
|
||||
});
|
||||
|
||||
const dispatchChangedEvent = this.useCustomEvent<{ value: T }>(`${key}-changed`, { detail: { value } });
|
||||
const setValue = (value: T) => {
|
||||
this.#state[key] = value;
|
||||
dispatchChangedEvent({ value });
|
||||
this.#dispatchFlowState({ type: 'stateKeyChanged', key, value });
|
||||
};
|
||||
this.#stateSetters.set(key, setValue as Dispatch<unknown>);
|
||||
return [value, setValue];
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook helper to simplify dispatching a `CustomEvent` on the Web
|
||||
* Component from React.
|
||||
*
|
||||
* @typeParam T - The type for `event.detail` value (optional).
|
||||
*
|
||||
* @param type - The `CustomEvent` type string.
|
||||
* @param options - The settings for the `CustomEvent`.
|
||||
* @returns The `dispatch` function. The function parameters change
|
||||
* depending on the `T` generic type:
|
||||
* - For `undefined` type (default), has no parameters.
|
||||
* - For other types, has one parameter for the `event.detail` value of that type.
|
||||
* @protected
|
||||
*/
|
||||
protected useCustomEvent<T = undefined>(type: string, options: CustomEventInit<T> = {}): DispatchEvent<T> {
|
||||
if (!this.#customEvents.has(type)) {
|
||||
const dispatch = ((detail?: T) => {
|
||||
const eventInitDict =
|
||||
detail === undefined
|
||||
? options
|
||||
: {
|
||||
...options,
|
||||
detail
|
||||
};
|
||||
const event = new CustomEvent(type, eventInitDict);
|
||||
return this.dispatchEvent(event);
|
||||
}) as DispatchEvent<T>;
|
||||
this.#customEvents.set(type, dispatch as DispatchEvent<unknown>);
|
||||
return dispatch;
|
||||
}
|
||||
return this.#customEvents.get(type)! as DispatchEvent<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Web Component render function. To be implemented by users with React.
|
||||
*
|
||||
* @param hooks - the adapter APIs exposed for the implementation.
|
||||
* @protected
|
||||
*/
|
||||
protected abstract render(hooks: RenderHooks): ReactElement | null;
|
||||
|
||||
/**
|
||||
* Prepare content container for Flow to bind server Element to.
|
||||
*
|
||||
* @param name container name attribute matching server name attribute
|
||||
* @protected
|
||||
*/
|
||||
protected useContent(name: string): ReactElement | null {
|
||||
useEffect(() => {
|
||||
this.#readyCallback.get(name)?.();
|
||||
}, []);
|
||||
return createElement('flow-content-container', { name, style: { display: 'contents' } });
|
||||
}
|
||||
|
||||
#maybeRenderRoot() {
|
||||
if (this.#rootRendered || !this.#root) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#root.render(createElement(this.#Wrapper));
|
||||
this.#rootRendered = true;
|
||||
}
|
||||
|
||||
#renderWrapper(): ReactElement | null {
|
||||
const [state, dispatchFlowState] = useReducer(stateReducer, this.#state);
|
||||
this.#state = state;
|
||||
this.#dispatchFlowState = dispatchFlowState;
|
||||
return this.render(this.#renderHooks);
|
||||
}
|
||||
|
||||
#markAsUsed(): void {
|
||||
// @ts-ignore
|
||||
let vaadinObject = window.Vaadin || {};
|
||||
// @ts-ignore
|
||||
if (vaadinObject.developmentMode) {
|
||||
vaadinObject.registrations = vaadinObject.registrations || [];
|
||||
vaadinObject.registrations.push({
|
||||
is: 'ReactAdapterElement',
|
||||
version: '25.2.6'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export {}
|
||||
@@ -0,0 +1,105 @@
|
||||
import '@vaadin/field-highlighter/src/vaadin-field-highlighter.js';
|
||||
import '@vaadin/common-frontend/ConnectionIndicator.js';
|
||||
import '@vaadin/accordion/src/vaadin-accordion.js';
|
||||
import '@vaadin/details/src/vaadin-details.js';
|
||||
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
|
||||
import '@vaadin/app-layout/src/vaadin-app-layout.js';
|
||||
import '@vaadin/button/src/vaadin-button.js';
|
||||
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
|
||||
import '@vaadin/avatar/src/vaadin-avatar.js';
|
||||
import '@vaadin/avatar-group/src/vaadin-avatar-group.js';
|
||||
import '@vaadin/badge/src/vaadin-badge.js';
|
||||
import '@vaadin/breadcrumbs/src/vaadin-breadcrumbs-item.js';
|
||||
import '@vaadin/card/src/vaadin-card.js';
|
||||
import '@vaadin/checkbox/src/vaadin-checkbox.js';
|
||||
import '@vaadin/checkbox-group/src/vaadin-checkbox-group.js';
|
||||
import '@vaadin/combo-box/src/vaadin-combo-box.js';
|
||||
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
|
||||
import 'Frontend/generated/jar-resources/flow-component-directive.js';
|
||||
import 'Frontend/generated/jar-resources/comboBoxConnector.js';
|
||||
import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js';
|
||||
import '@vaadin/confirm-dialog/src/vaadin-confirm-dialog.js';
|
||||
import '@vaadin/context-menu/src/vaadin-context-menu.js';
|
||||
import '@vaadin/tooltip/src/vaadin-tooltip.js';
|
||||
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
|
||||
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
|
||||
import '@vaadin/custom-field/src/vaadin-custom-field.js';
|
||||
import '@vaadin/date-picker/src/vaadin-date-picker.js';
|
||||
import 'Frontend/generated/jar-resources/datepickerConnector.js';
|
||||
import '@vaadin/date-time-picker/src/vaadin-date-time-picker.js';
|
||||
import '@vaadin/time-picker/src/vaadin-time-picker.js';
|
||||
import 'Frontend/generated/jar-resources/vaadin-time-picker/timepickerConnector.js';
|
||||
import 'Frontend/generated/jar-resources/vaadin-time-picker/helpers.js';
|
||||
import '@vaadin/dialog/src/vaadin-dialog.js';
|
||||
import 'Frontend/generated/jar-resources/dndConnector.js';
|
||||
import '@vaadin/form-layout/src/vaadin-form-layout.js';
|
||||
import '@vaadin/form-layout/src/vaadin-form-item.js';
|
||||
import '@vaadin/form-layout/src/vaadin-form-row.js';
|
||||
import '@vaadin/grid/src/vaadin-grid-column-group.js';
|
||||
import '@vaadin/grid/src/vaadin-grid.js';
|
||||
import '@vaadin/grid/src/vaadin-grid-column.js';
|
||||
import '@vaadin/grid/src/vaadin-grid-sorter.js';
|
||||
import 'Frontend/generated/jar-resources/gridConnector.ts';
|
||||
import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js';
|
||||
import '@vaadin/icon/src/vaadin-icon.js';
|
||||
import '@vaadin/icons/vaadin-iconset.js';
|
||||
import '@vaadin/list-box/src/vaadin-list-box.js';
|
||||
import '@vaadin/item/src/vaadin-item.js';
|
||||
import '@vaadin/login/src/vaadin-login-form.js';
|
||||
import '@vaadin/login/src/vaadin-login-overlay.js';
|
||||
import '@vaadin/markdown/src/vaadin-markdown.js';
|
||||
import '@vaadin/master-detail-layout/src/vaadin-master-detail-layout.js';
|
||||
import 'Frontend/generated/jar-resources/menubarConnector.js';
|
||||
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
|
||||
import '@vaadin/message-input/src/vaadin-message-input.js';
|
||||
import 'Frontend/generated/jar-resources/messageListConnector.js';
|
||||
import '@vaadin/message-list/src/vaadin-message-list.js';
|
||||
import '@vaadin/notification/src/vaadin-notification.js';
|
||||
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
|
||||
import '@vaadin/scroller/src/vaadin-scroller.js';
|
||||
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
|
||||
import '@vaadin/popover/src/vaadin-popover.js';
|
||||
import 'Frontend/generated/jar-resources/vaadin-popover/popover.ts';
|
||||
import '@vaadin/progress-bar/src/vaadin-progress-bar.js';
|
||||
import '@vaadin/radio-group/src/vaadin-radio-button.js';
|
||||
import '@vaadin/radio-group/src/vaadin-radio-group.js';
|
||||
import 'Frontend/generated/jar-resources/ReactRouterOutletElement.tsx';
|
||||
import '@vaadin/select/src/vaadin-select.js';
|
||||
import 'Frontend/generated/jar-resources/selectConnector.js';
|
||||
import 'Frontend/generated/jar-resources/tooltip.ts';
|
||||
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
|
||||
import '@vaadin/side-nav/src/vaadin-side-nav.js';
|
||||
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
|
||||
import '@vaadin/slider/src/vaadin-range-slider.js';
|
||||
import '@vaadin/slider/src/vaadin-slider.js';
|
||||
import '@vaadin/split-layout/src/vaadin-split-layout.js';
|
||||
import '@vaadin/tabs/src/vaadin-tab.js';
|
||||
import '@vaadin/tabsheet/src/vaadin-tabsheet.js';
|
||||
import '@vaadin/tabs/src/vaadin-tabs.js';
|
||||
import 'Frontend/generated/jar-resources/vaadin-big-decimal-field.js';
|
||||
import '@vaadin/email-field/src/vaadin-email-field.js';
|
||||
import '@vaadin/integer-field/src/vaadin-integer-field.js';
|
||||
import '@vaadin/number-field/src/vaadin-number-field.js';
|
||||
import '@vaadin/password-field/src/vaadin-password-field.js';
|
||||
import '@vaadin/text-area/src/vaadin-text-area.js';
|
||||
import '@vaadin/text-field/src/vaadin-text-field.js';
|
||||
import 'Frontend/generated/jar-resources/lit-renderer.ts';
|
||||
import '@vaadin/grid/src/vaadin-grid-tree-toggle.js';
|
||||
import 'Frontend/generated/jar-resources/treeGridConnector.ts';
|
||||
import '@vaadin/upload/src/vaadin-upload.js';
|
||||
import '@vaadin/upload/src/vaadin-upload-button.js';
|
||||
import '@vaadin/upload/src/vaadin-upload-drop-zone.js';
|
||||
import '@vaadin/upload/src/vaadin-upload-file-list.js';
|
||||
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
|
||||
import '@vaadin/virtual-list/src/vaadin-virtual-list.js';
|
||||
import 'Frontend/generated/jar-resources/virtualListConnector.js';
|
||||
import '@vaadin/vaadin-lumo-styles/vaadin-iconset.js';
|
||||
const loadOnDemand = (key) => { return Promise.resolve(0); }
|
||||
window.Vaadin = window.Vaadin || {};
|
||||
window.Vaadin.Flow = window.Vaadin.Flow || {};
|
||||
window.Vaadin.Flow.loadOnDemand = loadOnDemand;
|
||||
window.Vaadin.Flow.resetFocus = () => {
|
||||
let ae=document.activeElement;
|
||||
while(ae&&ae.shadowRoot) ae = ae.shadowRoot.activeElement;
|
||||
return !ae || ae.blur() || ae.focus() || true;
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
import { injectGlobalWebcomponentCss } from 'Frontend/generated/jar-resources/theme-util.js';
|
||||
|
||||
import '@vaadin/field-highlighter/src/vaadin-field-highlighter.js';
|
||||
import '@vaadin/common-frontend/ConnectionIndicator.js';
|
||||
import '@vaadin/accordion/src/vaadin-accordion.js';
|
||||
import '@vaadin/details/src/vaadin-details.js';
|
||||
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
|
||||
import '@vaadin/app-layout/src/vaadin-app-layout.js';
|
||||
import '@vaadin/button/src/vaadin-button.js';
|
||||
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
|
||||
import '@vaadin/avatar/src/vaadin-avatar.js';
|
||||
import '@vaadin/avatar-group/src/vaadin-avatar-group.js';
|
||||
import '@vaadin/badge/src/vaadin-badge.js';
|
||||
import '@vaadin/breadcrumbs/src/vaadin-breadcrumbs-item.js';
|
||||
import '@vaadin/card/src/vaadin-card.js';
|
||||
import '@vaadin/checkbox/src/vaadin-checkbox.js';
|
||||
import '@vaadin/checkbox-group/src/vaadin-checkbox-group.js';
|
||||
import '@vaadin/combo-box/src/vaadin-combo-box.js';
|
||||
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
|
||||
import 'Frontend/generated/jar-resources/flow-component-directive.js';
|
||||
import 'Frontend/generated/jar-resources/comboBoxConnector.js';
|
||||
import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js';
|
||||
import '@vaadin/confirm-dialog/src/vaadin-confirm-dialog.js';
|
||||
import '@vaadin/context-menu/src/vaadin-context-menu.js';
|
||||
import '@vaadin/tooltip/src/vaadin-tooltip.js';
|
||||
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
|
||||
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
|
||||
import '@vaadin/custom-field/src/vaadin-custom-field.js';
|
||||
import '@vaadin/date-picker/src/vaadin-date-picker.js';
|
||||
import 'Frontend/generated/jar-resources/datepickerConnector.js';
|
||||
import '@vaadin/date-time-picker/src/vaadin-date-time-picker.js';
|
||||
import '@vaadin/time-picker/src/vaadin-time-picker.js';
|
||||
import 'Frontend/generated/jar-resources/vaadin-time-picker/timepickerConnector.js';
|
||||
import 'Frontend/generated/jar-resources/vaadin-time-picker/helpers.js';
|
||||
import '@vaadin/dialog/src/vaadin-dialog.js';
|
||||
import 'Frontend/generated/jar-resources/dndConnector.js';
|
||||
import '@vaadin/form-layout/src/vaadin-form-layout.js';
|
||||
import '@vaadin/form-layout/src/vaadin-form-item.js';
|
||||
import '@vaadin/form-layout/src/vaadin-form-row.js';
|
||||
import '@vaadin/grid/src/vaadin-grid-column-group.js';
|
||||
import '@vaadin/grid/src/vaadin-grid.js';
|
||||
import '@vaadin/grid/src/vaadin-grid-column.js';
|
||||
import '@vaadin/grid/src/vaadin-grid-sorter.js';
|
||||
import 'Frontend/generated/jar-resources/gridConnector.ts';
|
||||
import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js';
|
||||
import '@vaadin/icon/src/vaadin-icon.js';
|
||||
import '@vaadin/icons/vaadin-iconset.js';
|
||||
import '@vaadin/list-box/src/vaadin-list-box.js';
|
||||
import '@vaadin/item/src/vaadin-item.js';
|
||||
import '@vaadin/login/src/vaadin-login-form.js';
|
||||
import '@vaadin/login/src/vaadin-login-overlay.js';
|
||||
import '@vaadin/markdown/src/vaadin-markdown.js';
|
||||
import '@vaadin/master-detail-layout/src/vaadin-master-detail-layout.js';
|
||||
import 'Frontend/generated/jar-resources/menubarConnector.js';
|
||||
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
|
||||
import '@vaadin/message-input/src/vaadin-message-input.js';
|
||||
import 'Frontend/generated/jar-resources/messageListConnector.js';
|
||||
import '@vaadin/message-list/src/vaadin-message-list.js';
|
||||
import '@vaadin/notification/src/vaadin-notification.js';
|
||||
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
|
||||
import '@vaadin/scroller/src/vaadin-scroller.js';
|
||||
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
|
||||
import '@vaadin/popover/src/vaadin-popover.js';
|
||||
import 'Frontend/generated/jar-resources/vaadin-popover/popover.ts';
|
||||
import '@vaadin/progress-bar/src/vaadin-progress-bar.js';
|
||||
import '@vaadin/radio-group/src/vaadin-radio-button.js';
|
||||
import '@vaadin/radio-group/src/vaadin-radio-group.js';
|
||||
import 'Frontend/generated/jar-resources/ReactRouterOutletElement.tsx';
|
||||
import '@vaadin/select/src/vaadin-select.js';
|
||||
import 'Frontend/generated/jar-resources/selectConnector.js';
|
||||
import 'Frontend/generated/jar-resources/tooltip.ts';
|
||||
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
|
||||
import '@vaadin/side-nav/src/vaadin-side-nav.js';
|
||||
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
|
||||
import '@vaadin/slider/src/vaadin-range-slider.js';
|
||||
import '@vaadin/slider/src/vaadin-slider.js';
|
||||
import '@vaadin/split-layout/src/vaadin-split-layout.js';
|
||||
import '@vaadin/tabs/src/vaadin-tab.js';
|
||||
import '@vaadin/tabsheet/src/vaadin-tabsheet.js';
|
||||
import '@vaadin/tabs/src/vaadin-tabs.js';
|
||||
import 'Frontend/generated/jar-resources/vaadin-big-decimal-field.js';
|
||||
import '@vaadin/email-field/src/vaadin-email-field.js';
|
||||
import '@vaadin/integer-field/src/vaadin-integer-field.js';
|
||||
import '@vaadin/number-field/src/vaadin-number-field.js';
|
||||
import '@vaadin/password-field/src/vaadin-password-field.js';
|
||||
import '@vaadin/text-area/src/vaadin-text-area.js';
|
||||
import '@vaadin/text-field/src/vaadin-text-field.js';
|
||||
import 'Frontend/generated/jar-resources/lit-renderer.ts';
|
||||
import '@vaadin/grid/src/vaadin-grid-tree-toggle.js';
|
||||
import 'Frontend/generated/jar-resources/treeGridConnector.ts';
|
||||
import '@vaadin/upload/src/vaadin-upload.js';
|
||||
import '@vaadin/upload/src/vaadin-upload-button.js';
|
||||
import '@vaadin/upload/src/vaadin-upload-drop-zone.js';
|
||||
import '@vaadin/upload/src/vaadin-upload-file-list.js';
|
||||
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
|
||||
import '@vaadin/virtual-list/src/vaadin-virtual-list.js';
|
||||
import 'Frontend/generated/jar-resources/virtualListConnector.js';
|
||||
import '@vaadin/vaadin-lumo-styles/vaadin-iconset.js';
|
||||
const loadOnDemand = (key) => { return Promise.resolve(0); }
|
||||
window.Vaadin = window.Vaadin || {};
|
||||
window.Vaadin.Flow = window.Vaadin.Flow || {};
|
||||
window.Vaadin.Flow.loadOnDemand = loadOnDemand;
|
||||
window.Vaadin.Flow.resetFocus = () => {
|
||||
let ae=document.activeElement;
|
||||
while(ae&&ae.shadowRoot) ae = ae.shadowRoot.activeElement;
|
||||
return !ae || ae.blur() || ae.focus() || true;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
app-shell-imports.d.ts
|
||||
app-shell-imports.js
|
||||
css.generated.d.ts
|
||||
flow/Flow.tsx
|
||||
flow/ReactAdapter.tsx
|
||||
flow/generated-flow-imports.d.ts
|
||||
flow/generated-flow-imports.js
|
||||
flow/generated-flow-webcomponent-imports.js
|
||||
index.tsx
|
||||
jar-resources/Clipboard.d.ts
|
||||
jar-resources/Clipboard.js
|
||||
jar-resources/Clipboard.js.map
|
||||
jar-resources/Download.d.ts
|
||||
jar-resources/Download.js
|
||||
jar-resources/Download.js.map
|
||||
jar-resources/ElementResize.d.ts
|
||||
jar-resources/ElementResize.js
|
||||
jar-resources/ElementResize.js.map
|
||||
jar-resources/Flow.d.ts
|
||||
jar-resources/Flow.js
|
||||
jar-resources/Flow.js.map
|
||||
jar-resources/FlowBootstrap.d.ts
|
||||
jar-resources/FlowBootstrap.js
|
||||
jar-resources/FlowClient.d.ts
|
||||
jar-resources/FlowClient.js
|
||||
jar-resources/FlowShortcut.js
|
||||
jar-resources/Fullscreen.d.ts
|
||||
jar-resources/Fullscreen.js
|
||||
jar-resources/Fullscreen.js.map
|
||||
jar-resources/Geolocation.d.ts
|
||||
jar-resources/Geolocation.js
|
||||
jar-resources/Geolocation.js.map
|
||||
jar-resources/PageVisibility.d.ts
|
||||
jar-resources/PageVisibility.js
|
||||
jar-resources/PageVisibility.js.map
|
||||
jar-resources/ReactRouterOutletElement.tsx
|
||||
jar-resources/ScreenOrientation.d.ts
|
||||
jar-resources/ScreenOrientation.js
|
||||
jar-resources/ScreenOrientation.js.map
|
||||
jar-resources/WakeLock.d.ts
|
||||
jar-resources/WakeLock.js
|
||||
jar-resources/WakeLock.js.map
|
||||
jar-resources/WebShare.d.ts
|
||||
jar-resources/WebShare.js
|
||||
jar-resources/WebShare.js.map
|
||||
jar-resources/comboBoxConnector.js
|
||||
jar-resources/contextMenuConnector.js
|
||||
jar-resources/contextMenuTargetConnector.js
|
||||
jar-resources/copilot-version.js
|
||||
jar-resources/copilot.d.ts
|
||||
jar-resources/copilot.js
|
||||
jar-resources/copilot/base-panel-Fr0D1ZcU.js
|
||||
jar-resources/copilot/chunk-DiqZc92J.js
|
||||
jar-resources/copilot/consts-CSALuSsm.js
|
||||
jar-resources/copilot/copilot-development-setup-user-guide-Db31eO1T.js
|
||||
jar-resources/copilot/copilot-development-setup-user-guide-utils-DzEVQbWO.js
|
||||
jar-resources/copilot/copilot-devtools-CYwy4U79.js
|
||||
jar-resources/copilot/copilot-error-handler-9OpssAH1.js
|
||||
jar-resources/copilot/copilot-features-plugin-DwQSwtbQ.js
|
||||
jar-resources/copilot/copilot-feedback-plugin-JMYrBCmQ.js
|
||||
jar-resources/copilot/copilot-focus-trap-CaZw1c70.js
|
||||
jar-resources/copilot/copilot-global-vars-later-CWkvR40X.js
|
||||
jar-resources/copilot/copilot-impersonator-plugin-iN25IekB.js
|
||||
jar-resources/copilot/copilot-info-plugin-9l6uSELy.js
|
||||
jar-resources/copilot/copilot-init-step2-tqpZOWcn.js
|
||||
jar-resources/copilot/copilot-log-plugin-CmwIHcBw.js
|
||||
jar-resources/copilot/copilot-message-box-CVAh5PSs.js
|
||||
jar-resources/copilot/copilot-modes-wJyMqHUb.js
|
||||
jar-resources/copilot/copilot-notification-CCNJdNg4.js
|
||||
jar-resources/copilot/copilot-notification-UcomqPI8.js
|
||||
jar-resources/copilot/copilot-server-communicator-impl-B7YDzJpM.js
|
||||
jar-resources/copilot/copilot-settings-panel-qUN2f6RH.js
|
||||
jar-resources/copilot/copilot-shortcuts-BzZuUtjW.js
|
||||
jar-resources/copilot/copilot-stored-machine-state-D6qB_Peh.js
|
||||
jar-resources/copilot/copilot-tree-impl-DxBvMTRa.js
|
||||
jar-resources/copilot/copilot-ui-state-Dc6l_5DA.js
|
||||
jar-resources/copilot/copilot-userinfo-C0s6T_kB.js
|
||||
jar-resources/copilot/copilot-vaadin-versions-CkxDkDmp.js
|
||||
jar-resources/copilot/copy-to-clipboard-4Y12mBRr.js
|
||||
jar-resources/copilot/directive-DWLihZIi.js
|
||||
jar-resources/copilot/directive-helpers-BTt8P8-5.js
|
||||
jar-resources/copilot/dom-utils-Cuv93-tQ.js
|
||||
jar-resources/copilot/early-project-state-LGwavSyI.js
|
||||
jar-resources/copilot/figma-public/figma-api.d.ts
|
||||
jar-resources/copilot/icons-CwakCZgK.js
|
||||
jar-resources/copilot/lit-renderer-fa_B9boC.js
|
||||
jar-resources/copilot/section-panel-ui-state-hOj_RfX_.js
|
||||
jar-resources/copilot/shared/copilot-plugin-support.d.ts
|
||||
jar-resources/copilot/shared/flow-utils.d.ts
|
||||
jar-resources/copilot/stats-CRkPKCLQ.js
|
||||
jar-resources/copilot/track-active-mode-event-DkX0nsC6.js
|
||||
jar-resources/copilot/typescript-BkEBjsia.js
|
||||
jar-resources/datepickerConnector.js
|
||||
jar-resources/disableOnClickFunctions.js
|
||||
jar-resources/dndConnector.js
|
||||
jar-resources/flow-component-directive.js
|
||||
jar-resources/flow-component-renderer.js
|
||||
jar-resources/gridConnector.ts
|
||||
jar-resources/index.d.ts
|
||||
jar-resources/index.js
|
||||
jar-resources/index.js.map
|
||||
jar-resources/lit-renderer.ts
|
||||
jar-resources/menubarConnector.js
|
||||
jar-resources/messageListConnector.js
|
||||
jar-resources/selectConnector.js
|
||||
jar-resources/theme-util.js
|
||||
jar-resources/tooltip.ts
|
||||
jar-resources/treeGridConnector.ts
|
||||
jar-resources/vaadin-big-decimal-field.js
|
||||
jar-resources/vaadin-dev-tools/License.d.ts
|
||||
jar-resources/vaadin-dev-tools/connection.d.ts
|
||||
jar-resources/vaadin-dev-tools/hotswap-scroll.d.ts
|
||||
jar-resources/vaadin-dev-tools/live-reload-connection.d.ts
|
||||
jar-resources/vaadin-dev-tools/pre-trial-splash-screen.d.ts
|
||||
jar-resources/vaadin-dev-tools/vaadin-dev-tools.d.ts
|
||||
jar-resources/vaadin-dev-tools/vaadin-dev-tools.js
|
||||
jar-resources/vaadin-dev-tools/vaadin-dev-tools.js.map
|
||||
jar-resources/vaadin-dev-tools/websocket-connection.d.ts
|
||||
jar-resources/vaadin-grid-flow-selection-column.js
|
||||
jar-resources/vaadin-popover/popover.ts
|
||||
jar-resources/vaadin-time-picker/helpers.js
|
||||
jar-resources/vaadin-time-picker/timepickerConnector.js
|
||||
jar-resources/vaadin-upload-manager-connector.ts
|
||||
jar-resources/virtualListConnector.js
|
||||
jsx-dev-transform/index.ts
|
||||
jsx-dev-transform/jsx-dev-runtime.ts
|
||||
jsx-dev-transform/jsx-runtime.ts
|
||||
layouts.json
|
||||
routes.tsx
|
||||
vaadin-featureflags.js
|
||||
vaadin-react.tsx
|
||||
vaadin.ts
|
||||
@@ -0,0 +1,26 @@
|
||||
/******************************************************************************
|
||||
* This file is auto-generated by Vaadin.
|
||||
* If you want to customize the entry point, you can copy this file or create
|
||||
* your own `index.tsx` in your frontend directory.
|
||||
* By default, the `index.tsx` file should be in `./frontend/` folder.
|
||||
*
|
||||
* NOTE:
|
||||
* - You need to restart the dev-server after adding the new `index.tsx` file.
|
||||
* After that, all modifications to `index.tsx` are recompiled automatically.
|
||||
* - `index.js` is also supported if you don't want to use TypeScript.
|
||||
******************************************************************************/
|
||||
|
||||
import { createElement } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { RouterProvider } from 'react-router';
|
||||
import { router } from 'Frontend/generated/routes.js';
|
||||
|
||||
function App() {
|
||||
return <RouterProvider router={router} />;
|
||||
}
|
||||
|
||||
const outlet = document.getElementById('outlet')!;
|
||||
let root = (outlet as any)._root ?? createRoot(outlet);
|
||||
(outlet as any)._root = root;
|
||||
root.render(createElement(App));
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export {};
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
/**
|
||||
* Reads the first item from the system clipboard and returns its text/plain
|
||||
* and text/html representations. Either field is {@code null} if the
|
||||
* corresponding MIME type is not present.
|
||||
*
|
||||
* The caller is expected to be inside a transient user gesture and to have
|
||||
* been granted the {@code clipboard-read} permission; otherwise
|
||||
* {@code navigator.clipboard.read} rejects and this function propagates the
|
||||
* rejection.
|
||||
*/
|
||||
async function readClipboardPayload() {
|
||||
const items = await navigator.clipboard.read();
|
||||
if (!items.length) {
|
||||
return null;
|
||||
}
|
||||
const item = items[0];
|
||||
const get = async (type) => item.types.includes(type) ? (await item.getType(type)).text() : null;
|
||||
return {
|
||||
text: await get('text/plain'),
|
||||
html: await get('text/html')
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Re-encodes the given {@code <img>} as {@code image/png} via a canvas
|
||||
* round-trip. The source can be any rasterisable format the browser already
|
||||
* decodes ({@code image/png}, {@code image/jpeg}, {@code image/svg+xml}, ...);
|
||||
* the output is always a {@code Promise<Blob>} of {@code image/png}, the only
|
||||
* image MIME type every browser's asynchronous Clipboard API accepts on write.
|
||||
*
|
||||
* Cross-origin images need {@code crossorigin="anonymous"} on the {@code <img>}
|
||||
* plus matching CORS headers, otherwise the canvas is tainted and
|
||||
* {@code toBlob} throws.
|
||||
*/
|
||||
function imageToPngBlob(img) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const draw = () => {
|
||||
try {
|
||||
const width = img.naturalWidth || img.width;
|
||||
const height = img.naturalHeight || img.height;
|
||||
if (!width || !height) {
|
||||
reject(new Error('image has no intrinsic size'));
|
||||
return;
|
||||
}
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) {
|
||||
reject(new Error('2D canvas context not available'));
|
||||
return;
|
||||
}
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
canvas.toBlob((png) => (png ? resolve(png) : reject(new Error('canvas.toBlob returned null'))), 'image/png');
|
||||
}
|
||||
catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
if (img.complete) {
|
||||
// `complete` is also true for an image that already failed to load or has
|
||||
// an empty src; those have naturalWidth === 0 and their load/error events
|
||||
// have already fired and will never fire again, so we must settle here
|
||||
// rather than wait for an event that never comes.
|
||||
if (img.naturalWidth > 0) {
|
||||
draw();
|
||||
}
|
||||
else {
|
||||
reject(new Error('image failed to load or has empty src'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
img.addEventListener('load', draw, { once: true });
|
||||
img.addEventListener('error', () => reject(new Error('image load failed')), { once: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Writes any combination of text/plain, text/html and image/png to the system
|
||||
* clipboard as a single ClipboardItem. Any argument may be {@code null} to omit
|
||||
* that MIME type; at least one is expected to be non-null (the caller enforces
|
||||
* this). The image argument is the source {@code <img>}; it is re-encoded as
|
||||
* {@code image/png} via {@link imageToPngBlob} and the resulting
|
||||
* {@code Promise<Blob>} is fed directly to {@code ClipboardItem} so the
|
||||
* {@code navigator.clipboard.write} call stays synchronous inside the user
|
||||
* gesture (Safari otherwise loses activation on the first await).
|
||||
*
|
||||
* The caller is expected to be inside a transient user gesture; otherwise
|
||||
* {@code navigator.clipboard.write} rejects and this function propagates the
|
||||
* rejection.
|
||||
*
|
||||
* Resolves with the {@code text/plain} value if present, otherwise with the
|
||||
* {@code text/html} value, otherwise with {@code null} (image-only case).
|
||||
*/
|
||||
async function writeClipboardPayload(text, html, image) {
|
||||
const entries = {};
|
||||
if (text !== null) {
|
||||
entries['text/plain'] = text;
|
||||
}
|
||||
if (html !== null) {
|
||||
entries['text/html'] = html;
|
||||
}
|
||||
if (image !== null) {
|
||||
entries['image/png'] = imageToPngBlob(image);
|
||||
}
|
||||
await navigator.clipboard.write([new ClipboardItem(entries)]);
|
||||
return text !== null ? text : html;
|
||||
}
|
||||
/**
|
||||
* Posts each file from a {@code paste} event's {@code clipboardData.files} as
|
||||
* its own XHR to the URL stored as the named attribute on {@code element}. The
|
||||
* wire format matches vaadin-upload: raw body, percent-encoded {@code X-Filename}
|
||||
* header, MIME type in {@code Content-Type}.
|
||||
*
|
||||
* Each upload is processed in its own HTTP request, so the UI changes the
|
||||
* server-side UploadHandler makes through {@code UI.access} are applied to the
|
||||
* state tree but not sent to the client by the upload response itself. Once
|
||||
* every upload of the paste has settled this helper dispatches a
|
||||
* {@code vaadin-paste-upload-finished} event back on {@code element}; a
|
||||
* server-side listener for that event triggers a normal Flow round trip that
|
||||
* flushes those pending UI changes — so the API works without {@code @Push},
|
||||
* exactly like a regular upload completing through the Upload component.
|
||||
*
|
||||
* Editable targets ({@code <input>}, {@code <textarea>}, {@code contentEditable})
|
||||
* are not given any special treatment here: browsers do not paste files into
|
||||
* those elements, so a paste containing a file in a focused text field is
|
||||
* still a "the user tried to drop a file on the page" event from the
|
||||
* application's point of view.
|
||||
*/
|
||||
// Monotonic counter incremented once per paste gesture so server-side
|
||||
// handlers can correlate the parallel fetch POSTs that belong to the same
|
||||
// paste, and order pastes against each other. Scoped to the browser tab —
|
||||
// a different tab gets its own counter, but no server-side state crosses
|
||||
// tabs in this flow.
|
||||
let pasteSequence = 0;
|
||||
function uploadPastedFiles(event, element, urlAttribute) {
|
||||
const files = event.clipboardData?.files;
|
||||
if (!files || files.length === 0) {
|
||||
return;
|
||||
}
|
||||
const url = element.getAttribute(urlAttribute);
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
pasteSequence += 1;
|
||||
const pasteId = String(pasteSequence);
|
||||
// Surface the file count too: the batch server handler needs it to know
|
||||
// when the paste has been fully delivered (one fetch per file means the
|
||||
// server only observes arrivals, not the total).
|
||||
const fileCount = String(files.length);
|
||||
const uploads = [];
|
||||
for (const file of files) {
|
||||
const headers = {
|
||||
'X-Filename': encodeURIComponent(file.name),
|
||||
'X-Paste-Id': pasteId,
|
||||
'X-Paste-File-Count': fileCount
|
||||
};
|
||||
if (file.type) {
|
||||
headers['Content-Type'] = file.type;
|
||||
}
|
||||
// The per-file UploadHandler callback runs as each POST is processed;
|
||||
// log network/connectivity failures the server will never see otherwise.
|
||||
uploads.push(fetch(url, { method: 'POST', headers: headers, body: file }).catch((err) => {
|
||||
console.error('Vaadin clipboard file upload failed', err);
|
||||
}));
|
||||
}
|
||||
// Tell the server the paste's uploads are done so it can flush the queued
|
||||
// UI updates without requiring @Push. The upload response is written only
|
||||
// after the handler's UI.access task has applied its changes to the state
|
||||
// tree, so by the time a fetch settles those changes are guaranteed to be
|
||||
// picked up by this round trip.
|
||||
Promise.allSettled(uploads).then(() => {
|
||||
element.dispatchEvent(new CustomEvent('vaadin-paste-upload-finished'));
|
||||
});
|
||||
}
|
||||
const $wnd = window;
|
||||
$wnd.Vaadin ??= {};
|
||||
$wnd.Vaadin.Flow ??= {};
|
||||
$wnd.Vaadin.Flow.clipboard = {
|
||||
readPayload: readClipboardPayload,
|
||||
writePayload: writeClipboardPayload,
|
||||
uploadPastedFiles: uploadPastedFiles
|
||||
};
|
||||
export {};
|
||||
//# sourceMappingURL=Clipboard.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
export {};
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
/**
|
||||
* Triggers a file download from the given URL using the standard
|
||||
* <a href download> click pattern.
|
||||
*
|
||||
* The anchor is synthesised, clicked synchronously inside the caller's
|
||||
* gesture context, and removed. The browser then either navigates to the
|
||||
* URL (server responds with Content-Disposition: attachment) or saves the
|
||||
* resource directly when the download attribute applies.
|
||||
*
|
||||
* The {@code download} attribute is honoured only for same-origin URLs;
|
||||
* cross-origin responses must set Content-Disposition themselves for the
|
||||
* filename to take effect.
|
||||
*/
|
||||
function startDownload(url, filename) {
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
// Always set `download` so the browser saves the response rather than
|
||||
// navigating to it. Empty value lets the browser pick the filename from
|
||||
// Content-Disposition or the URL pathname; a non-empty value is the
|
||||
// suggested filename (honoured only same-origin). Cross-origin responses
|
||||
// without Content-Disposition: attachment still navigate — that's a
|
||||
// server-side concern this client helper can't override.
|
||||
a.download = filename ?? '';
|
||||
// Opt out of Vaadin's client-side router so the click reaches the
|
||||
// browser's native download handling instead of being intercepted as an
|
||||
// in-app navigation. Matches Anchor.setHref(DownloadHandler).
|
||||
a.setAttribute('router-ignore', '');
|
||||
// Hidden but in the document — some browsers ignore clicks on detached
|
||||
// anchors.
|
||||
a.style.display = 'none';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
}
|
||||
const $wnd = window;
|
||||
$wnd.Vaadin ??= {};
|
||||
$wnd.Vaadin.Flow ??= {};
|
||||
$wnd.Vaadin.Flow.download = {
|
||||
start: startDownload
|
||||
};
|
||||
export {};
|
||||
//# sourceMappingURL=Download.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Download.js","sourceRoot":"","sources":["../../../../src/main/frontend/Download.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;;GAYG;AACH,SAAS,aAAa,CAAC,GAAW,EAAE,QAAiB;IACnD,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC;IACb,sEAAsE;IACtE,wEAAwE;IACxE,oEAAoE;IACpE,yEAAyE;IACzE,oEAAoE;IACpE,yDAAyD;IACzD,CAAC,CAAC,QAAQ,GAAG,QAAQ,IAAI,EAAE,CAAC;IAC5B,kEAAkE;IAClE,wEAAwE;IACxE,8DAA8D;IAC9D,CAAC,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IACpC,uEAAuE;IACvE,WAAW;IACX,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC,KAAK,EAAE,CAAC;IACV,CAAC,CAAC,MAAM,EAAE,CAAC;AACb,CAAC;AAED,MAAM,IAAI,GAAG,MAAa,CAAC;AAC3B,IAAI,CAAC,MAAM,KAAK,EAAE,CAAC;AACnB,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;AACxB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,GAAG;IAC1B,KAAK,EAAE,aAAa;CACrB,CAAC","sourcesContent":["/*\n * Copyright 2000-2026 Vaadin Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\n/**\n * Triggers a file download from the given URL using the standard\n * <a href download> click pattern.\n *\n * The anchor is synthesised, clicked synchronously inside the caller's\n * gesture context, and removed. The browser then either navigates to the\n * URL (server responds with Content-Disposition: attachment) or saves the\n * resource directly when the download attribute applies.\n *\n * The {@code download} attribute is honoured only for same-origin URLs;\n * cross-origin responses must set Content-Disposition themselves for the\n * filename to take effect.\n */\nfunction startDownload(url: string, filename?: string): void {\n const a = document.createElement('a');\n a.href = url;\n // Always set `download` so the browser saves the response rather than\n // navigating to it. Empty value lets the browser pick the filename from\n // Content-Disposition or the URL pathname; a non-empty value is the\n // suggested filename (honoured only same-origin). Cross-origin responses\n // without Content-Disposition: attachment still navigate — that's a\n // server-side concern this client helper can't override.\n a.download = filename ?? '';\n // Opt out of Vaadin's client-side router so the click reaches the\n // browser's native download handling instead of being intercepted as an\n // in-app navigation. Matches Anchor.setHref(DownloadHandler).\n a.setAttribute('router-ignore', '');\n // Hidden but in the document — some browsers ignore clicks on detached\n // anchors.\n a.style.display = 'none';\n document.body.appendChild(a);\n a.click();\n a.remove();\n}\n\nconst $wnd = window as any;\n$wnd.Vaadin ??= {};\n$wnd.Vaadin.Flow ??= {};\n$wnd.Vaadin.Flow.download = {\n start: startDownload\n};\n\n// Empty export to ensure TypeScript emits this as an ES module,\n// which is required for Vite to load it via import.\nexport {};\n"]}
|
||||
@@ -0,0 +1 @@
|
||||
export {};
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
const $wnd = window;
|
||||
$wnd.Vaadin ??= {};
|
||||
$wnd.Vaadin.Flow ??= {};
|
||||
$wnd.Vaadin.Flow.elementResize = {
|
||||
/**
|
||||
* Installs a ResizeObserver on the given element and invokes the callback
|
||||
* with the rounded content-box width and height each time the element
|
||||
* resizes. Returns a function that disconnects the observer.
|
||||
*
|
||||
* Sub-pixel decimals from contentRect are rounded to integers to avoid
|
||||
* spamming equal-after-rounding updates back to the server.
|
||||
*/
|
||||
observe(element, callback) {
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (!entry.target.isConnected) {
|
||||
continue;
|
||||
}
|
||||
callback({
|
||||
width: Math.round(entry.contentRect.width),
|
||||
height: Math.round(entry.contentRect.height)
|
||||
});
|
||||
}
|
||||
});
|
||||
observer.observe(element);
|
||||
return () => observer.disconnect();
|
||||
}
|
||||
};
|
||||
export {};
|
||||
//# sourceMappingURL=ElementResize.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ElementResize.js","sourceRoot":"","sources":["../../../../src/main/frontend/ElementResize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAYH,MAAM,IAAI,GAAG,MAAa,CAAC;AAC3B,IAAI,CAAC,MAAM,KAAK,EAAE,CAAC;AACnB,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;AACxB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,GAAG;IAC/B;;;;;;;OAOG;IACH,OAAO,CAAC,OAAgB,EAAE,QAA8B;QACtD,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,CAAC,OAAO,EAAE,EAAE;YAC9C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;oBAC9B,SAAS;gBACX,CAAC;gBACD,QAAQ,CAAC;oBACP,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC;oBAC1C,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;iBAC7C,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC;CACF,CAAC","sourcesContent":["/*\n * Copyright 2000-2026 Vaadin Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\n/**\n * Size data passed to the observe() callback. Field names match the Java\n * Size record so the value can be Jackson-deserialised on the server when\n * forwarded through a trigger-framework input.\n */\ninterface Size {\n width: number;\n height: number;\n}\n\nconst $wnd = window as any;\n$wnd.Vaadin ??= {};\n$wnd.Vaadin.Flow ??= {};\n$wnd.Vaadin.Flow.elementResize = {\n /**\n * Installs a ResizeObserver on the given element and invokes the callback\n * with the rounded content-box width and height each time the element\n * resizes. Returns a function that disconnects the observer.\n *\n * Sub-pixel decimals from contentRect are rounded to integers to avoid\n * spamming equal-after-rounding updates back to the server.\n */\n observe(element: Element, callback: (size: Size) => void): () => void {\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n if (!entry.target.isConnected) {\n continue;\n }\n callback({\n width: Math.round(entry.contentRect.width),\n height: Math.round(entry.contentRect.height)\n });\n }\n });\n observer.observe(element);\n return () => observer.disconnect();\n }\n};\n\n// Empty export to ensure TypeScript emits this as an ES module,\n// which is required for Vite to load it via import.\nexport {};\n"]}
|
||||
@@ -0,0 +1,83 @@
|
||||
import './Clipboard';
|
||||
import './Download';
|
||||
import './ElementResize';
|
||||
import './Geolocation';
|
||||
import './WakeLock';
|
||||
export interface FlowConfig {
|
||||
imports?: () => Promise<any>;
|
||||
}
|
||||
interface AppConfig {
|
||||
productionMode: boolean;
|
||||
appId: string;
|
||||
uidl: any;
|
||||
}
|
||||
interface AppInitResponse {
|
||||
appConfig: AppConfig;
|
||||
pushScript?: string;
|
||||
}
|
||||
interface Router {
|
||||
render: (ctx: NavigationParameters, shouldUpdateHistory: boolean) => Promise<void>;
|
||||
}
|
||||
interface HTMLRouterContainer extends HTMLElement {
|
||||
onBeforeEnter?: (ctx: NavigationParameters, cmd: PreventAndRedirectCommands, router: Router) => void | Promise<any>;
|
||||
onBeforeLeave?: (ctx: NavigationParameters, cmd: PreventCommands, router: Router) => void | Promise<any>;
|
||||
serverConnected?: (cancel: boolean, url?: NavigationParameters) => void;
|
||||
serverPaused?: () => void;
|
||||
}
|
||||
interface FlowRoute {
|
||||
action: (params: NavigationParameters) => Promise<HTMLRouterContainer>;
|
||||
path: string;
|
||||
}
|
||||
export interface NavigationParameters {
|
||||
pathname: string;
|
||||
search?: string;
|
||||
}
|
||||
export interface PreventCommands {
|
||||
prevent: () => any;
|
||||
continue?: () => any;
|
||||
}
|
||||
export interface PreventAndRedirectCommands extends PreventCommands {
|
||||
redirect: (route: string) => any;
|
||||
}
|
||||
/**
|
||||
* Client API for flow UI operations.
|
||||
*/
|
||||
export declare class Flow {
|
||||
config: FlowConfig;
|
||||
response?: AppInitResponse;
|
||||
pathname: string;
|
||||
container: HTMLRouterContainer;
|
||||
private isActive;
|
||||
private baseRegex;
|
||||
private appShellTitle;
|
||||
private navigation;
|
||||
constructor(config?: FlowConfig);
|
||||
/**
|
||||
* Return a `route` object for vaadin-router in an one-element array.
|
||||
*
|
||||
* The `FlowRoute` object `path` property handles any route,
|
||||
* and the `action` returns the flow container without updating the content,
|
||||
* delaying the actual Flow server call to the `onBeforeEnter` phase.
|
||||
*
|
||||
* This is a specific API for its use with `vaadin-router`.
|
||||
*/
|
||||
get serverSideRoutes(): [FlowRoute];
|
||||
loadingStarted(): void;
|
||||
loadingFinished(): void;
|
||||
private get action();
|
||||
private flowLeave;
|
||||
private flowNavigate;
|
||||
private getFlowRoutePath;
|
||||
private getFlowRouteQuery;
|
||||
private flowInit;
|
||||
private loadScript;
|
||||
private findNonce;
|
||||
private injectAppIdScript;
|
||||
private flowInitClient;
|
||||
private flowInitUi;
|
||||
private collectBrowserDetails;
|
||||
private addConnectionIndicator;
|
||||
private offlineStubAction;
|
||||
private isFlowClientLoaded;
|
||||
}
|
||||
export {};
|
||||
@@ -0,0 +1,538 @@
|
||||
import { ConnectionIndicator, ConnectionState } from '@vaadin/common-frontend';
|
||||
import './Clipboard';
|
||||
import { currentFullscreenState } from './Fullscreen';
|
||||
import './Download';
|
||||
import './ElementResize';
|
||||
import './Geolocation';
|
||||
import { currentVisibility } from './PageVisibility';
|
||||
import { currentScreenOrientationAngle, currentScreenOrientationType } from './ScreenOrientation';
|
||||
import './WakeLock';
|
||||
import { isShareSupported } from './WebShare';
|
||||
class FlowUiInitializationError extends Error {
|
||||
}
|
||||
// flow uses body for keeping references
|
||||
const flowRoot = window.document.body;
|
||||
const $wnd = window;
|
||||
const ROOT_NODE_ID = 1; // See StateTree.java
|
||||
function getClients() {
|
||||
return Object.keys($wnd.Vaadin.Flow.clients)
|
||||
.filter((key) => key !== 'TypeScript')
|
||||
.map((id) => $wnd.Vaadin.Flow.clients[id]);
|
||||
}
|
||||
function sendEvent(eventName, data) {
|
||||
getClients().forEach((client) => client.sendEventMessage(ROOT_NODE_ID, eventName, data));
|
||||
}
|
||||
// In the future could be replaced with RegExp.escape()
|
||||
function escapeRegExp(pattern) {
|
||||
return pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
/**
|
||||
* Client API for flow UI operations.
|
||||
*/
|
||||
export class Flow {
|
||||
config;
|
||||
response = undefined;
|
||||
pathname = '';
|
||||
container;
|
||||
// flag used to inform Testbench whether a server route is in progress
|
||||
isActive = false;
|
||||
baseRegex = /^\//;
|
||||
appShellTitle;
|
||||
navigation = '';
|
||||
constructor(config) {
|
||||
// Set window.name early so @PreserveOnRefresh can use it to identify the browser tab
|
||||
// Only set if not already set to preserve any existing value
|
||||
if (!window.name) {
|
||||
window.name = `v-${Math.random()}`;
|
||||
}
|
||||
flowRoot.$ = flowRoot.$ || [];
|
||||
this.config = config || {};
|
||||
// TB checks for the existence of window.Vaadin.Flow in order
|
||||
// to consider that TB needs to wait for `initFlow()`.
|
||||
$wnd.Vaadin = $wnd.Vaadin || {};
|
||||
$wnd.Vaadin.Flow = $wnd.Vaadin.Flow || {};
|
||||
$wnd.Vaadin.Flow.clients = {
|
||||
TypeScript: {
|
||||
isActive: () => this.isActive
|
||||
}
|
||||
};
|
||||
// Set browser details collection function as global for use by refresh()
|
||||
$wnd.Vaadin.Flow.getBrowserDetailsParameters = this.collectBrowserDetails.bind(this);
|
||||
// Regular expression used to remove the app-context
|
||||
const elm = document.head.querySelector('base');
|
||||
this.baseRegex = new RegExp(`^${
|
||||
// IE11 does not support document.baseURI
|
||||
escapeRegExp((document.baseURI || (elm && elm.href) || '/').replace(/^https?:\/\/[^/]+/i, ''))}`);
|
||||
this.appShellTitle = document.title;
|
||||
// Put a vaadin-connection-indicator in the dom
|
||||
this.addConnectionIndicator();
|
||||
}
|
||||
/**
|
||||
* Return a `route` object for vaadin-router in an one-element array.
|
||||
*
|
||||
* The `FlowRoute` object `path` property handles any route,
|
||||
* and the `action` returns the flow container without updating the content,
|
||||
* delaying the actual Flow server call to the `onBeforeEnter` phase.
|
||||
*
|
||||
* This is a specific API for its use with `vaadin-router`.
|
||||
*/
|
||||
get serverSideRoutes() {
|
||||
return [
|
||||
{
|
||||
path: '(.*)',
|
||||
action: this.action
|
||||
}
|
||||
];
|
||||
}
|
||||
loadingStarted() {
|
||||
// Make Testbench know that server request is in progress
|
||||
this.isActive = true;
|
||||
$wnd.Vaadin.connectionState.loadingStarted();
|
||||
}
|
||||
loadingFinished() {
|
||||
// Make Testbench know that server request has finished
|
||||
this.isActive = false;
|
||||
$wnd.Vaadin.connectionState.loadingFinished();
|
||||
if ($wnd.Vaadin.listener) {
|
||||
// Listeners registered, do not register again.
|
||||
return;
|
||||
}
|
||||
$wnd.Vaadin.listener = {};
|
||||
// Listen for click on router-links -> 'link' navigation trigger
|
||||
// and on <a> nodes -> 'client' navigation trigger.
|
||||
// Use capture phase to detect prevented / stopped events.
|
||||
document.addEventListener('click', (_e) => {
|
||||
if (_e.target) {
|
||||
if (_e.composedPath().some((node) => node instanceof HTMLElement && node.hasAttribute('router-link'))) {
|
||||
this.navigation = 'link';
|
||||
}
|
||||
else if (_e.composedPath().some((node) => node.nodeName === 'A')) {
|
||||
this.navigation = 'client';
|
||||
}
|
||||
}
|
||||
}, {
|
||||
capture: true
|
||||
});
|
||||
}
|
||||
get action() {
|
||||
// Return a function which is bound to the flow instance, thus we can use
|
||||
// the syntax `...serverSideRoutes` in vaadin-router.
|
||||
return async (params) => {
|
||||
// Store last action pathname so as we can check it in events
|
||||
this.pathname = params.pathname;
|
||||
if ($wnd.Vaadin.connectionState.online) {
|
||||
try {
|
||||
await this.flowInit();
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof FlowUiInitializationError) {
|
||||
// error initializing Flow: assume connection lost
|
||||
$wnd.Vaadin.connectionState.state = ConnectionState.CONNECTION_LOST;
|
||||
return this.offlineStubAction();
|
||||
}
|
||||
else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// insert an offline stub
|
||||
return this.offlineStubAction();
|
||||
}
|
||||
// When an action happens, navigation will be resolved `onBeforeEnter`
|
||||
this.container.onBeforeEnter = (ctx, cmd) => this.flowNavigate(ctx, cmd);
|
||||
// For covering the 'server -> client' use case
|
||||
this.container.onBeforeLeave = (ctx, cmd) => this.flowLeave(ctx, cmd);
|
||||
return this.container;
|
||||
};
|
||||
}
|
||||
// Send a remote call to `JavaScriptBootstrapUI` to check
|
||||
// whether navigation has to be cancelled.
|
||||
async flowLeave(ctx, cmd) {
|
||||
// server -> server, viewing offline stub, or browser is offline
|
||||
const { connectionState } = $wnd.Vaadin;
|
||||
if (this.pathname === ctx.pathname || !this.isFlowClientLoaded() || connectionState.offline) {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
// 'server -> client'
|
||||
return new Promise((resolve) => {
|
||||
this.loadingStarted();
|
||||
// The callback to run from server side to cancel navigation
|
||||
this.container.serverConnected = (cancel) => {
|
||||
resolve(cmd && cancel ? cmd.prevent() : cmd?.continue?.());
|
||||
this.loadingFinished();
|
||||
};
|
||||
// Call server side to check whether we can leave the view
|
||||
sendEvent('ui-leave-navigation', { route: this.getFlowRoutePath(ctx), query: this.getFlowRouteQuery(ctx) });
|
||||
});
|
||||
}
|
||||
// Send the remote call to `UI` to render the flow
|
||||
// route specified by the context
|
||||
async flowNavigate(ctx, cmd) {
|
||||
if (this.response) {
|
||||
return new Promise((resolve) => {
|
||||
this.loadingStarted();
|
||||
// The callback to run from server side once the view is ready
|
||||
this.container.serverConnected = (cancel, redirectContext) => {
|
||||
if (cmd && cancel) {
|
||||
resolve(cmd.prevent());
|
||||
}
|
||||
else if (cmd && cmd.redirect && redirectContext) {
|
||||
resolve(cmd.redirect(redirectContext.pathname));
|
||||
}
|
||||
else {
|
||||
cmd?.continue?.();
|
||||
this.container.style.display = '';
|
||||
resolve(this.container);
|
||||
}
|
||||
this.loadingFinished();
|
||||
};
|
||||
this.container.serverPaused = () => {
|
||||
this.loadingFinished();
|
||||
};
|
||||
// Call server side to navigate to the given route
|
||||
sendEvent('ui-navigate', {
|
||||
route: this.getFlowRoutePath(ctx),
|
||||
query: this.getFlowRouteQuery(ctx),
|
||||
appShellTitle: this.appShellTitle,
|
||||
historyState: history.state,
|
||||
trigger: this.navigation
|
||||
});
|
||||
// Default to history navigation trigger.
|
||||
// Link and client cases are handled by click listener in loadingFinished().
|
||||
this.navigation = 'history';
|
||||
});
|
||||
}
|
||||
else {
|
||||
// No server response => offline or erroneous connection
|
||||
return Promise.resolve(this.container);
|
||||
}
|
||||
}
|
||||
getFlowRoutePath(context) {
|
||||
// Don't decode the pathname here - let the server handle decoding
|
||||
// individual path segments. This preserves the distinction between
|
||||
// literal slashes (path separators) and encoded slashes (%2F, data).
|
||||
return context.pathname.replace(this.baseRegex, '');
|
||||
}
|
||||
getFlowRouteQuery(context) {
|
||||
return (context.search && context.search.substring(1)) || '';
|
||||
}
|
||||
// import flow client modules and initialize UI in server side.
|
||||
async flowInit() {
|
||||
// Do not start flow twice
|
||||
if (!this.isFlowClientLoaded()) {
|
||||
$wnd.Vaadin.Flow.nonce = this.findNonce();
|
||||
// show flow progress indicator
|
||||
this.loadingStarted();
|
||||
// Initialize server side UI
|
||||
this.response = await this.flowInitUi();
|
||||
const { pushScript, appConfig } = this.response;
|
||||
if (typeof pushScript === 'string') {
|
||||
await this.loadScript(pushScript);
|
||||
}
|
||||
const { appId } = appConfig;
|
||||
// we use a custom tag for the flow app container
|
||||
// This must be created before bootstrapMod.init is called as that call
|
||||
// can handle a UIDL from the server, which relies on the container being available
|
||||
const tag = `flow-container-${appId.toLowerCase()}`;
|
||||
const serverCreatedContainer = document.querySelector(tag);
|
||||
if (serverCreatedContainer) {
|
||||
this.container = serverCreatedContainer;
|
||||
}
|
||||
else {
|
||||
this.container = document.createElement(tag);
|
||||
this.container.id = appId;
|
||||
}
|
||||
flowRoot.$[appId] = this.container;
|
||||
// Load bootstrap script with server side parameters
|
||||
const bootstrapMod = await import('./FlowBootstrap');
|
||||
bootstrapMod.init(this.response);
|
||||
// Load custom modules defined by user
|
||||
if (typeof this.config.imports === 'function') {
|
||||
this.injectAppIdScript(appId);
|
||||
await this.config.imports();
|
||||
}
|
||||
// Load flow-client module
|
||||
const clientMod = await import('./FlowClient');
|
||||
await this.flowInitClient(clientMod);
|
||||
// hide flow progress indicator
|
||||
this.loadingFinished();
|
||||
}
|
||||
// It might be that components created from server expect that their content has been rendered.
|
||||
// Appending eagerly the container we avoid these kind of errors.
|
||||
// Note that the client router will move this container to the outlet if the navigation succeed
|
||||
if (this.container && !this.container.isConnected) {
|
||||
this.container.style.display = 'none';
|
||||
document.body.appendChild(this.container);
|
||||
}
|
||||
return this.response;
|
||||
}
|
||||
async loadScript(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
script.onload = () => resolve();
|
||||
script.onerror = reject;
|
||||
script.src = url;
|
||||
const { nonce } = $wnd.Vaadin.Flow;
|
||||
if (nonce !== undefined) {
|
||||
script.setAttribute('nonce', nonce);
|
||||
}
|
||||
document.body.appendChild(script);
|
||||
});
|
||||
}
|
||||
findNonce() {
|
||||
let nonce;
|
||||
const scriptTags = document.head.getElementsByTagName('script');
|
||||
for (const scriptTag of scriptTags) {
|
||||
if (scriptTag.nonce) {
|
||||
nonce = scriptTag.nonce;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nonce;
|
||||
}
|
||||
injectAppIdScript(appId) {
|
||||
const appIdWithoutHashCode = appId.substring(0, appId.lastIndexOf('-'));
|
||||
const scriptAppId = document.createElement('script');
|
||||
scriptAppId.type = 'module';
|
||||
scriptAppId.setAttribute('data-app-id', appIdWithoutHashCode);
|
||||
const { nonce } = $wnd.Vaadin.Flow;
|
||||
if (nonce !== undefined) {
|
||||
scriptAppId.setAttribute('nonce', nonce);
|
||||
}
|
||||
document.body.append(scriptAppId);
|
||||
}
|
||||
// After the flow-client javascript module has been loaded, this initializes flow UI
|
||||
// in the browser.
|
||||
async flowInitClient(clientMod) {
|
||||
clientMod.init();
|
||||
// client init is async, we need to loop until initialized
|
||||
return new Promise((resolve) => {
|
||||
const intervalId = setInterval(() => {
|
||||
// client `isActive() == true` while initializing or processing
|
||||
const initializing = getClients().reduce((prev, client) => prev || client.isActive(), false);
|
||||
if (!initializing) {
|
||||
clearInterval(intervalId);
|
||||
resolve();
|
||||
}
|
||||
}, 5);
|
||||
});
|
||||
}
|
||||
// Returns the `appConfig` object
|
||||
async flowInitUi() {
|
||||
// appConfig was sent in the index.html request
|
||||
const initial = $wnd.Vaadin && $wnd.Vaadin.TypeScript && $wnd.Vaadin.TypeScript.initial;
|
||||
if (initial) {
|
||||
$wnd.Vaadin.TypeScript.initial = undefined;
|
||||
return Promise.resolve(initial);
|
||||
}
|
||||
const browserDetails = await this.collectBrowserDetails();
|
||||
// send a request to the `JavaScriptBootstrapHandler`
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const httpRequest = xhr;
|
||||
// Browser details are appended as individual query parameters rather
|
||||
// than as a single JSON-encoded value. A JSON payload in the URL
|
||||
// produces many percent-encoded escape sequences (%7B, %22, %3A, ...)
|
||||
// that some firewalls/WAFs (e.g. Sophos) flag and block, which would
|
||||
// fail the bootstrap on the very first page load. Plain key=value pairs
|
||||
// avoid that pattern entirely.
|
||||
const browserDetailsParams = browserDetails
|
||||
? Object.entries(browserDetails)
|
||||
.map(([key, value]) => `&${key}=${encodeURIComponent(value)}`)
|
||||
.join('')
|
||||
: '';
|
||||
const requestPath = `?v-r=init&location=${encodeURIComponent(this.getFlowRoutePath(location))}&query=${encodeURIComponent(this.getFlowRouteQuery(location))}${browserDetailsParams}`;
|
||||
httpRequest.open('GET', requestPath);
|
||||
httpRequest.onerror = () => reject(new FlowUiInitializationError(`Invalid server response when initializing Flow UI.
|
||||
${httpRequest.status}
|
||||
${httpRequest.responseText}`));
|
||||
httpRequest.onload = () => {
|
||||
const contentType = httpRequest.getResponseHeader('content-type');
|
||||
if (contentType && contentType.indexOf('application/json') !== -1) {
|
||||
resolve(JSON.parse(httpRequest.responseText));
|
||||
}
|
||||
else {
|
||||
httpRequest.onerror();
|
||||
}
|
||||
};
|
||||
httpRequest.send();
|
||||
});
|
||||
}
|
||||
// Collects browser details parameters
|
||||
async collectBrowserDetails() {
|
||||
const params = {};
|
||||
/* Screen height and width */
|
||||
params['v-sh'] = $wnd.screen.height;
|
||||
params['v-sw'] = $wnd.screen.width;
|
||||
/* Browser window dimensions */
|
||||
params['v-wh'] = $wnd.innerHeight;
|
||||
params['v-ww'] = $wnd.innerWidth;
|
||||
/* Body element dimensions */
|
||||
params['v-bh'] = $wnd.document.body.clientHeight;
|
||||
params['v-bw'] = $wnd.document.body.clientWidth;
|
||||
/* Current time */
|
||||
const date = new Date();
|
||||
params['v-curdate'] = date.getTime();
|
||||
/* Current timezone offset (including DST shift) */
|
||||
const tzo1 = date.getTimezoneOffset();
|
||||
/* Compare the current tz offset with the first offset from the end
|
||||
of the year that differs --- if less that, we are in DST, otherwise
|
||||
we are in normal time */
|
||||
let dstDiff = 0;
|
||||
let rawTzo = tzo1;
|
||||
for (let m = 12; m > 0; m -= 1) {
|
||||
date.setUTCMonth(m);
|
||||
const tzo2 = date.getTimezoneOffset();
|
||||
if (tzo1 !== tzo2) {
|
||||
dstDiff = tzo1 > tzo2 ? tzo1 - tzo2 : tzo2 - tzo1;
|
||||
rawTzo = tzo1 > tzo2 ? tzo1 : tzo2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Time zone offset */
|
||||
params['v-tzo'] = tzo1;
|
||||
/* DST difference */
|
||||
params['v-dstd'] = dstDiff;
|
||||
/* Time zone offset without DST */
|
||||
params['v-rtzo'] = rawTzo;
|
||||
/* DST in effect? */
|
||||
params['v-dston'] = tzo1 !== rawTzo;
|
||||
/* Time zone id (if available) */
|
||||
try {
|
||||
params['v-tzid'] = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
}
|
||||
catch (err) {
|
||||
params['v-tzid'] = '';
|
||||
}
|
||||
/* Window name */
|
||||
if ($wnd.name) {
|
||||
params['v-wn'] = $wnd.name;
|
||||
}
|
||||
/* Detect touch device support */
|
||||
let supportsTouch = false;
|
||||
try {
|
||||
$wnd.document.createEvent('TouchEvent');
|
||||
supportsTouch = true;
|
||||
}
|
||||
catch (e) {
|
||||
/* Chrome and IE10 touch detection */
|
||||
supportsTouch = 'ontouchstart' in $wnd || typeof $wnd.navigator.msMaxTouchPoints !== 'undefined';
|
||||
}
|
||||
params['v-td'] = supportsTouch;
|
||||
/* Device Pixel Ratio */
|
||||
params['v-pr'] = $wnd.devicePixelRatio;
|
||||
if ($wnd.navigator.platform) {
|
||||
params['v-np'] = $wnd.navigator.platform;
|
||||
}
|
||||
/* Color scheme from CSS color-scheme property */
|
||||
const colorScheme = getComputedStyle(document.documentElement).colorScheme.trim();
|
||||
// "normal" is the default value and means no color scheme is set
|
||||
params['v-cs'] = colorScheme && colorScheme !== 'normal' ? colorScheme : '';
|
||||
/* Page visibility — initial state of document.hidden / document.hasFocus() */
|
||||
params['v-pv'] = currentVisibility();
|
||||
/* Fullscreen state — initial state of document.fullscreenEnabled / .fullscreenElement */
|
||||
params['v-fs'] = currentFullscreenState();
|
||||
/* Screen orientation — initial state of screen.orientation, empty
|
||||
when the Screen Orientation API is unavailable. */
|
||||
params['v-so'] = currentScreenOrientationType();
|
||||
params['v-soa'] = currentScreenOrientationAngle();
|
||||
/* Theme name - detect which theme is in use */
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
let themeName = '';
|
||||
if (computedStyle.getPropertyValue('--vaadin-lumo-theme').trim()) {
|
||||
themeName = 'lumo';
|
||||
}
|
||||
else if (computedStyle.getPropertyValue('--vaadin-aura-theme').trim()) {
|
||||
themeName = 'aura';
|
||||
}
|
||||
params['v-tn'] = themeName;
|
||||
/* Geolocation availability — guarded because tests may reset
|
||||
window.Vaadin between runs, removing the namespace that
|
||||
Geolocation.ts installs at import time. */
|
||||
const geolocation = $wnd.Vaadin.Flow?.geolocation;
|
||||
if (geolocation) {
|
||||
params['v-ga'] = await geolocation.queryAvailability();
|
||||
}
|
||||
/* Wake-lock availability — same guard rationale as geolocation. */
|
||||
const wakeLock = $wnd.Vaadin.Flow?.wakeLock;
|
||||
if (wakeLock) {
|
||||
params['v-wla'] = wakeLock.queryAvailability();
|
||||
}
|
||||
/* Web Share API support */
|
||||
params['v-ws'] = isShareSupported();
|
||||
/* Stringify each value (they are parsed on the server side) */
|
||||
const stringParams = {};
|
||||
Object.keys(params).forEach((key) => {
|
||||
const value = params[key];
|
||||
if (typeof value !== 'undefined') {
|
||||
stringParams[key] = value.toString();
|
||||
}
|
||||
});
|
||||
return stringParams;
|
||||
}
|
||||
// Create shared connection state store and connection indicator
|
||||
addConnectionIndicator() {
|
||||
// add connection indicator to DOM
|
||||
ConnectionIndicator.create();
|
||||
// Listen to browser online/offline events and update the loading indicator accordingly.
|
||||
// Note: if flow-client is loaded, it instead handles the state transitions.
|
||||
$wnd.addEventListener('online', () => {
|
||||
if (!this.isFlowClientLoaded()) {
|
||||
// Send an HTTP HEAD request for sw.js to verify server reachability.
|
||||
// We do not expect sw.js to be cached, so the request goes to the
|
||||
// server rather than being served from local cache.
|
||||
// Require network-level failure to revert the state to CONNECTION_LOST
|
||||
// (HTTP error code is ok since it still verifies server's presence).
|
||||
$wnd.Vaadin.connectionState.state = ConnectionState.RECONNECTING;
|
||||
const http = new XMLHttpRequest();
|
||||
http.open('HEAD', 'sw.js');
|
||||
http.onload = () => {
|
||||
$wnd.Vaadin.connectionState.state = ConnectionState.CONNECTED;
|
||||
};
|
||||
http.onerror = () => {
|
||||
$wnd.Vaadin.connectionState.state = ConnectionState.CONNECTION_LOST;
|
||||
};
|
||||
// Postpone request to reduce potential net::ERR_INTERNET_DISCONNECTED
|
||||
// errors that sometimes occurs even if browser says it is online
|
||||
setTimeout(() => http.send(), 50);
|
||||
}
|
||||
});
|
||||
$wnd.addEventListener('offline', () => {
|
||||
if (!this.isFlowClientLoaded()) {
|
||||
$wnd.Vaadin.connectionState.state = ConnectionState.CONNECTION_LOST;
|
||||
}
|
||||
});
|
||||
}
|
||||
async offlineStubAction() {
|
||||
const offlineStub = document.createElement('iframe');
|
||||
const offlineStubPath = './offline-stub.html';
|
||||
offlineStub.setAttribute('src', offlineStubPath);
|
||||
offlineStub.setAttribute('style', 'width: 100%; height: 100%; border: 0');
|
||||
this.response = undefined;
|
||||
let onlineListener;
|
||||
const removeOfflineStubAndOnlineListener = () => {
|
||||
if (onlineListener !== undefined) {
|
||||
$wnd.Vaadin.connectionState.removeStateChangeListener(onlineListener);
|
||||
onlineListener = undefined;
|
||||
}
|
||||
};
|
||||
offlineStub.onBeforeEnter = (ctx, _cmds, router) => {
|
||||
onlineListener = () => {
|
||||
if ($wnd.Vaadin.connectionState.online) {
|
||||
removeOfflineStubAndOnlineListener();
|
||||
router.render(ctx, false);
|
||||
}
|
||||
};
|
||||
$wnd.Vaadin.connectionState.addStateChangeListener(onlineListener);
|
||||
};
|
||||
offlineStub.onBeforeLeave = (_ctx, _cmds, _router) => {
|
||||
removeOfflineStubAndOnlineListener();
|
||||
};
|
||||
return offlineStub;
|
||||
}
|
||||
isFlowClientLoaded() {
|
||||
return this.response !== undefined;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=Flow.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
export const init: (appInitResponse: any) => void;
|
||||
@@ -0,0 +1,236 @@
|
||||
/* This is a copy of the regular `BootstrapHandler.js` in the flow-server
|
||||
module, but with the following modifications:
|
||||
- The main function is exported as an ES module for lazy initialization.
|
||||
- Application configuration is passed as a parameter instead of using
|
||||
replacement placeholders as in the regular bootstrapping.
|
||||
- It reuses `Vaadin.Flow.clients` if exists.
|
||||
- Fixed lint errors.
|
||||
*/
|
||||
const init = function (appInitResponse) {
|
||||
window.Vaadin = window.Vaadin || {};
|
||||
window.Vaadin.Flow = window.Vaadin.Flow || {};
|
||||
|
||||
var apps = {};
|
||||
var widgetsets = {};
|
||||
|
||||
var log;
|
||||
if (typeof window.console === undefined || !window.location.search.match(/[&?]debug(&|$)/)) {
|
||||
/* If no console.log present, just use a no-op */
|
||||
log = function () {};
|
||||
} else if (typeof window.console.log === 'function') {
|
||||
/* If it's a function, use it with apply */
|
||||
log = function () {
|
||||
window.console.log.apply(window.console, arguments);
|
||||
};
|
||||
} else {
|
||||
/* In IE, its a native function for which apply is not defined, but it works
|
||||
without a proper 'this' reference */
|
||||
log = window.console.log;
|
||||
}
|
||||
|
||||
var isInitializedInDom = function (appId) {
|
||||
var appDiv = document.getElementById(appId);
|
||||
if (!appDiv) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < appDiv.childElementCount; i++) {
|
||||
var className = appDiv.childNodes[i].className;
|
||||
/* If the app div contains a child with the class
|
||||
'v-app-loading' we have only received the HTML
|
||||
but not yet started the widget set
|
||||
(UIConnector removes the v-app-loading div). */
|
||||
if (className && className.indexOf('v-app-loading') != -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
* Needed for Testbench compatibility, but prevents any Vaadin 7 app from
|
||||
* bootstrapping unless the legacy vaadinBootstrap.js file is loaded before
|
||||
* this script.
|
||||
*/
|
||||
window.Vaadin = window.Vaadin || {};
|
||||
window.Vaadin.Flow = window.Vaadin.Flow || {};
|
||||
|
||||
/**
|
||||
* Triggers a CSS animation on an element by adding a class, then
|
||||
* removes the class when the animation ends.
|
||||
*/
|
||||
window.Vaadin.Flow.flashClass = function (element, className) {
|
||||
element.classList.remove(className);
|
||||
void element.offsetWidth;
|
||||
element.classList.add(className);
|
||||
function onAnimationEnd(e) {
|
||||
if (e.target === element) {
|
||||
element.classList.remove(className);
|
||||
element.removeEventListener('animationend', onAnimationEnd);
|
||||
}
|
||||
}
|
||||
element.addEventListener('animationend', onAnimationEnd);
|
||||
requestAnimationFrame(function () {
|
||||
var style = getComputedStyle(element);
|
||||
var animName = style.animationName;
|
||||
if (!animName || animName === 'none') {
|
||||
element.classList.remove(className);
|
||||
element.removeEventListener('animationend', onAnimationEnd);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Needed for wrapping custom javascript functionality in the components (i.e. connectors)
|
||||
*/
|
||||
window.Vaadin.Flow.tryCatchWrapper = function (originalFunction, component) {
|
||||
return function () {
|
||||
try {
|
||||
// eslint-disable-next-line
|
||||
const result = originalFunction.apply(this, arguments);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`There seems to be an error in ${component}:
|
||||
${error.message}
|
||||
Please submit an issue to https://github.com/vaadin/flow-components/issues/new/choose`
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
if (!window.Vaadin.Flow.initApplication) {
|
||||
window.Vaadin.Flow.clients = window.Vaadin.Flow.clients || {};
|
||||
|
||||
/**
|
||||
* Initializes a Flow application with the given ID and configuration,
|
||||
* and triggers the widgetset callback to start the client engine.
|
||||
*/
|
||||
window.Vaadin.Flow.initApplication = function (appId, config) {
|
||||
var testbenchId = appId.replace(/-\d+$/, '');
|
||||
|
||||
if (apps[appId]) {
|
||||
if (
|
||||
window.Vaadin &&
|
||||
window.Vaadin.Flow &&
|
||||
window.Vaadin.Flow.clients &&
|
||||
window.Vaadin.Flow.clients[testbenchId] &&
|
||||
window.Vaadin.Flow.clients[testbenchId].initializing
|
||||
) {
|
||||
throw new Error('Application ' + appId + ' is already being initialized');
|
||||
}
|
||||
if (isInitializedInDom(appId)) {
|
||||
if (appInitResponse.appConfig.productionMode) {
|
||||
throw new Error('Application ' + appId + ' already initialized');
|
||||
}
|
||||
|
||||
// Remove old contents for Flow
|
||||
var appDiv = document.getElementById(appId);
|
||||
for (var i = 0; i < appDiv.childElementCount; i++) {
|
||||
appDiv.childNodes[i].remove();
|
||||
}
|
||||
|
||||
// For devMode reset app config and restart widgetset as client
|
||||
// is up and running after hrm update.
|
||||
const getConfig = function (name) {
|
||||
return config[name];
|
||||
};
|
||||
|
||||
/* Export public data */
|
||||
const app = {
|
||||
getConfig: getConfig
|
||||
};
|
||||
apps[appId] = app;
|
||||
|
||||
if (widgetsets['client'].callback) {
|
||||
log('Starting from bootstrap', appId);
|
||||
widgetsets['client'].callback(appId);
|
||||
} else {
|
||||
log('Setting pending startup', appId);
|
||||
widgetsets['client'].pendingApps.push(appId);
|
||||
}
|
||||
return apps[appId];
|
||||
}
|
||||
}
|
||||
|
||||
log('init application', appId, config);
|
||||
|
||||
window.Vaadin.Flow.clients[testbenchId] = {
|
||||
isActive: function () {
|
||||
return true;
|
||||
},
|
||||
initializing: true,
|
||||
productionMode: mode
|
||||
};
|
||||
|
||||
var getConfig = function (name) {
|
||||
var value = config[name];
|
||||
return value;
|
||||
};
|
||||
|
||||
/* Export public data */
|
||||
var app = {
|
||||
getConfig: getConfig
|
||||
};
|
||||
apps[appId] = app;
|
||||
|
||||
var widgetset = 'client';
|
||||
widgetsets[widgetset] = {
|
||||
pendingApps: []
|
||||
};
|
||||
if (widgetsets[widgetset].callback) {
|
||||
log('Starting from bootstrap', appId);
|
||||
widgetsets[widgetset].callback(appId);
|
||||
} else {
|
||||
log('Setting pending startup', appId);
|
||||
widgetsets[widgetset].pendingApps.push(appId);
|
||||
}
|
||||
|
||||
return app;
|
||||
};
|
||||
/** Returns an array of all registered application IDs */
|
||||
window.Vaadin.Flow.getAppIds = function () {
|
||||
var ids = [];
|
||||
for (var id in apps) {
|
||||
if (Object.prototype.hasOwnProperty.call(apps, id)) {
|
||||
ids.push(id);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
};
|
||||
/** Returns the application object for the given ID */
|
||||
window.Vaadin.Flow.getApp = function (appId) {
|
||||
return apps[appId];
|
||||
};
|
||||
/**
|
||||
* Registers a widgetset callback and starts any applications
|
||||
* that are waiting for it.
|
||||
*/
|
||||
window.Vaadin.Flow.registerWidgetset = function (widgetset, callback) {
|
||||
log('Widgetset registered', widgetset);
|
||||
var ws = widgetsets[widgetset];
|
||||
if (ws && ws.pendingApps) {
|
||||
ws.callback = callback;
|
||||
for (var i = 0; i < ws.pendingApps.length; i++) {
|
||||
var appId = ws.pendingApps[i];
|
||||
log('Starting from register widgetset', appId);
|
||||
callback(appId);
|
||||
}
|
||||
ws.pendingApps = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
log('Flow bootstrap loaded');
|
||||
if (appInitResponse.appConfig.productionMode && typeof window.__gwtStatsEvent != 'function') {
|
||||
window.Vaadin.Flow.gwtStatsEvents = [];
|
||||
window.__gwtStatsEvent = function (event) {
|
||||
window.Vaadin.Flow.gwtStatsEvents.push(event);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
var config = appInitResponse.appConfig;
|
||||
var mode = appInitResponse.appConfig.productionMode;
|
||||
window.Vaadin.Flow.initApplication(config.appId, config);
|
||||
};
|
||||
|
||||
export { init };
|
||||
@@ -0,0 +1 @@
|
||||
export const init: () => void;
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Client-side helpers for keyboard shortcuts. Loaded on demand by
|
||||
* ShortcutRegistration (see initShortcutClient) the same way FlowWebPush.js is
|
||||
* loaded by WebPush. Provides the popover/modal origin guards (#24974) and the
|
||||
* keydown delegate used when a shortcut listens on a browser-only element.
|
||||
*/
|
||||
window.Vaadin = window.Vaadin || {};
|
||||
window.Vaadin.Flow = window.Vaadin.Flow || {};
|
||||
|
||||
window.Vaadin.Flow.shortcut = window.Vaadin.Flow.shortcut || {
|
||||
// Nearest open popover/modal ancestor of the given node in the flattened
|
||||
// (composed) tree, so slotted light-DOM content resolves to the overlay in a
|
||||
// component's shadow root.
|
||||
_scopeOf: function (node) {
|
||||
while (node) {
|
||||
if (node.nodeType === 1 && node.matches && (node.matches(':popover-open') || node.matches(':modal'))) {
|
||||
return node;
|
||||
}
|
||||
node = node.assignedSlot || node.parentNode || node.host || null;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// Nearest open popover/modal ancestor of the event target.
|
||||
_eventScope: function (event) {
|
||||
const path = event.composedPath();
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
const node = path[i];
|
||||
if (node && node.nodeType === 1 && node.matches && (node.matches(':popover-open') || node.matches(':modal'))) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// Delegate path: suppress when an open popover/modal sits between the event
|
||||
// target and the boundary element the listener is attached to. Returns true
|
||||
// when the shortcut is allowed to fire. Fails open on error.
|
||||
eventWithinBoundary: function (event, boundary) {
|
||||
try {
|
||||
const path = event.composedPath();
|
||||
const boundaryIndex = path.indexOf(boundary);
|
||||
if (boundaryIndex < 0) {
|
||||
return true;
|
||||
}
|
||||
for (let i = 0; i < boundaryIndex; i++) {
|
||||
const node = path[i];
|
||||
if (node && node.nodeType === 1 && node.matches && (node.matches(':popover-open') || node.matches(':modal'))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
// Normal path: fire only when the event and the shortcut owner (located via
|
||||
// the given attribute selector) share the same popover/modal scope. Returns
|
||||
// true when the shortcut is allowed to fire. Fails open on error.
|
||||
//
|
||||
// A relayed clone (see registerKeydownDelegate) carries the real origin scope
|
||||
// in _vaadinShortcutOriginScope, because its own composedPath points at the
|
||||
// listenOn element and no longer reflects where the keydown happened.
|
||||
eventInOwnerScope: function (event, ownerSelector) {
|
||||
try {
|
||||
const owner = document.querySelector(ownerSelector);
|
||||
if (!owner) {
|
||||
return true;
|
||||
}
|
||||
const eventScope =
|
||||
'_vaadinShortcutOriginScope' in event
|
||||
? event._vaadinShortcutOriginScope
|
||||
: window.Vaadin.Flow.shortcut._eventScope(event);
|
||||
return eventScope === window.Vaadin.Flow.shortcut._scopeOf(owner);
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
// Relays keydown events from a browser-only element (found by the JS locator)
|
||||
// to the listenOn component. When the given matcher accepts the event a clone
|
||||
// is re-dispatched to listenOn so the server-side shortcut listener fires.
|
||||
// (Previously the inline ELEMENT_LOCATOR_JS in ShortcutRegistration.)
|
||||
registerKeydownDelegate: function (listenOn, delegate, matches, resetFocus, allowDefault) {
|
||||
if (!delegate) {
|
||||
throw 'Shortcut listenOn element not found with the given JS locator';
|
||||
}
|
||||
delegate.addEventListener('keydown', function (event) {
|
||||
if (matches(event, delegate)) {
|
||||
if (resetFocus) {
|
||||
window.Vaadin.Flow.resetFocus();
|
||||
}
|
||||
const clone = new event.constructor(event.type, event);
|
||||
// Remember where the keydown actually originated: the clone is
|
||||
// re-targeted at listenOn, so its composedPath can no longer tell a
|
||||
// downstream owner-scope guard that the event came from this overlay.
|
||||
clone._vaadinShortcutOriginScope = window.Vaadin.Flow.shortcut._eventScope(event);
|
||||
listenOn.dispatchEvent(clone);
|
||||
if (!allowDefault) {
|
||||
event.preventDefault();
|
||||
}
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
type VaadinFullscreenState = 'UNSUPPORTED' | 'NOT_FULLSCREEN' | 'FULLSCREEN';
|
||||
/**
|
||||
* Returns the current fullscreen state synchronously. Used by the bootstrap
|
||||
* path to seed the server-side signal without waiting for a DOM event.
|
||||
*/
|
||||
export declare function currentFullscreenState(): VaadinFullscreenState;
|
||||
export {};
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
/**
|
||||
* Returns the current fullscreen state synchronously. Used by the bootstrap
|
||||
* path to seed the server-side signal without waiting for a DOM event.
|
||||
*/
|
||||
export function currentFullscreenState() {
|
||||
if (document.fullscreenEnabled !== true) {
|
||||
return 'UNSUPPORTED';
|
||||
}
|
||||
return document.fullscreenElement ? 'FULLSCREEN' : 'NOT_FULLSCREEN';
|
||||
}
|
||||
// Dispatch on document.body so the server-side Page facade (listening on
|
||||
// the UI element, which is body) can update its signal.
|
||||
function dispatch(state) {
|
||||
document.body.dispatchEvent(new CustomEvent('vaadin-fullscreen-change', { detail: state }));
|
||||
}
|
||||
// Tracks the most recent component-fullscreen setup so the wrapper can be
|
||||
// torn down when fullscreen exits (programmatically or via Escape) or when
|
||||
// a new fullscreen request supersedes it.
|
||||
let activeComponentReset;
|
||||
function resetComponentIfActive() {
|
||||
if (activeComponentReset) {
|
||||
const fn = activeComponentReset;
|
||||
activeComponentReset = undefined;
|
||||
fn();
|
||||
}
|
||||
}
|
||||
document.addEventListener('fullscreenchange', () => {
|
||||
if (!document.fullscreenElement) {
|
||||
resetComponentIfActive();
|
||||
}
|
||||
dispatch(currentFullscreenState());
|
||||
});
|
||||
const $wnd = window;
|
||||
$wnd.Vaadin ??= {};
|
||||
$wnd.Vaadin.Flow ??= {};
|
||||
$wnd.Vaadin.Flow.fullscreen = {
|
||||
/**
|
||||
* Requests fullscreen for the entire page (document.documentElement).
|
||||
* Resolves once the browser has entered fullscreen; rejects with the
|
||||
* browser's error if the request is refused (no user activation,
|
||||
* permissions policy, etc.) or with a custom error if fullscreen is not
|
||||
* supported.
|
||||
*/
|
||||
async requestPageFullscreen() {
|
||||
resetComponentIfActive();
|
||||
if (document.fullscreenEnabled !== true) {
|
||||
throw new Error('Fullscreen is not supported');
|
||||
}
|
||||
await document.documentElement.requestFullscreen();
|
||||
},
|
||||
/**
|
||||
* Requests fullscreen for a specific component by moving it into the
|
||||
* given wrapper element and hiding the rest of the view. Fullscreens
|
||||
* document.documentElement so that Vaadin theming and overlay
|
||||
* components keep working. The component is restored to its original
|
||||
* position on exit (programmatic, Escape, or a superseding request).
|
||||
* If the browser rejects the request, the DOM is rolled back before the
|
||||
* promise rejects with the browser's error.
|
||||
*/
|
||||
async requestComponentFullscreen(element, wrapper) {
|
||||
resetComponentIfActive();
|
||||
if (document.fullscreenEnabled !== true) {
|
||||
throw new Error('Fullscreen is not supported');
|
||||
}
|
||||
const originalParent = element.parentNode;
|
||||
if (!originalParent) {
|
||||
throw new Error('Component is not attached to the DOM');
|
||||
}
|
||||
// The view root is the wrapper's current element child (the route
|
||||
// content). Capture it before touching the DOM, because the steps below
|
||||
// insert a placeholder comment and move the element into the wrapper —
|
||||
// after that, the wrapper's first node may be the placeholder rather than
|
||||
// the view root. Use firstElementChild so comment/text nodes are skipped.
|
||||
const viewRoot = wrapper.firstElementChild;
|
||||
const placeholder = document.createComment('vaadin-fullscreen-placeholder');
|
||||
originalParent.insertBefore(placeholder, element);
|
||||
wrapper.appendChild(element);
|
||||
// When the fullscreened component *is* the view root there is nothing
|
||||
// else to hide; hiding it would blank the fullscreen. Otherwise hide the
|
||||
// view root so only the fullscreened component shows.
|
||||
const hidden = viewRoot === element ? null : viewRoot;
|
||||
const previousDisplay = hidden?.style.display ?? '';
|
||||
if (hidden) {
|
||||
hidden.style.display = 'none';
|
||||
}
|
||||
activeComponentReset = () => {
|
||||
placeholder.parentNode?.insertBefore(element, placeholder);
|
||||
placeholder.remove();
|
||||
if (hidden) {
|
||||
hidden.style.display = previousDisplay;
|
||||
}
|
||||
};
|
||||
try {
|
||||
await document.documentElement.requestFullscreen();
|
||||
}
|
||||
catch (e) {
|
||||
// Browser rejected the request — undo the DOM changes so the page
|
||||
// does not end up looking fullscreened without actually being so.
|
||||
resetComponentIfActive();
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Exits fullscreen mode if the page is currently in fullscreen.
|
||||
*/
|
||||
exitFullscreen() {
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=Fullscreen.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
export {};
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
function copyCoords(c) {
|
||||
return {
|
||||
latitude: c.latitude,
|
||||
longitude: c.longitude,
|
||||
accuracy: c.accuracy,
|
||||
altitude: c.altitude,
|
||||
altitudeAccuracy: c.altitudeAccuracy,
|
||||
heading: c.heading,
|
||||
speed: c.speed
|
||||
};
|
||||
}
|
||||
const watches = new Map();
|
||||
// The cached availability for the current page. Populated on first
|
||||
// queryAvailability() call, refreshed from each get()/watch() outcome, and
|
||||
// kept current by a permissionchange listener (where supported).
|
||||
let cachedAvailability = null;
|
||||
let permissionChangeListenerInstalled = false;
|
||||
function publishAvailability(next) {
|
||||
if (cachedAvailability === next) {
|
||||
return;
|
||||
}
|
||||
cachedAvailability = next;
|
||||
// Dispatch on document.body so the server-side Geolocation facade (listening
|
||||
// on the UI element, which is body) can update its cached value.
|
||||
document.body.dispatchEvent(new CustomEvent('vaadin-geolocation-availability-change', {
|
||||
detail: { availability: next }
|
||||
}));
|
||||
}
|
||||
// Applies a single get()/watch() outcome to the cached availability and
|
||||
// returns the value to report in the response. Never overwrites
|
||||
// UNSUPPORTED, which is session-stable. TIMEOUT and POSITION_UNAVAILABLE
|
||||
// don't reveal the permission state, so the previous cached value is
|
||||
// returned unchanged.
|
||||
function getAndCacheAvailabilityFromResult(position, error) {
|
||||
if (cachedAvailability !== 'UNSUPPORTED') {
|
||||
if (position) {
|
||||
publishAvailability('GRANTED');
|
||||
}
|
||||
else if (error?.code === 1) {
|
||||
publishAvailability('DENIED');
|
||||
}
|
||||
}
|
||||
return cachedAvailability ?? 'UNKNOWN';
|
||||
}
|
||||
async function resolveAvailability() {
|
||||
if (!window.isSecureContext) {
|
||||
return 'UNSUPPORTED';
|
||||
}
|
||||
// Chromium exposes document.featurePolicy; Firefox and Safari do not
|
||||
// expose any feature-policy introspection API, so the check is only
|
||||
// possible on Chromium. When absent, assume geolocation is allowed.
|
||||
const doc = document;
|
||||
if (doc.featurePolicy && typeof doc.featurePolicy.allowsFeature === 'function') {
|
||||
try {
|
||||
if (!doc.featurePolicy.allowsFeature('geolocation')) {
|
||||
return 'UNSUPPORTED';
|
||||
}
|
||||
}
|
||||
catch (_e) {
|
||||
// Ignore and assume allowed
|
||||
}
|
||||
}
|
||||
try {
|
||||
const status = await navigator.permissions.query({ name: 'geolocation' });
|
||||
if (!permissionChangeListenerInstalled) {
|
||||
permissionChangeListenerInstalled = true;
|
||||
status.addEventListener('change', () => {
|
||||
publishAvailability(stateToAvailability(status.state));
|
||||
});
|
||||
}
|
||||
return stateToAvailability(status.state);
|
||||
}
|
||||
catch (_e) {
|
||||
// Safari rejects the 'geolocation' permission name with a TypeError
|
||||
return 'UNKNOWN';
|
||||
}
|
||||
}
|
||||
function stateToAvailability(state) {
|
||||
switch (state) {
|
||||
case 'granted':
|
||||
return 'GRANTED';
|
||||
case 'denied':
|
||||
return 'DENIED';
|
||||
case 'prompt':
|
||||
return 'PROMPT';
|
||||
default:
|
||||
return 'UNKNOWN';
|
||||
}
|
||||
}
|
||||
const $wnd = window;
|
||||
$wnd.Vaadin ??= {};
|
||||
$wnd.Vaadin.Flow ??= {};
|
||||
$wnd.Vaadin.Flow.geolocation = {
|
||||
get(options) {
|
||||
return new Promise((resolve) => {
|
||||
navigator.geolocation.getCurrentPosition((p) => {
|
||||
const position = { coords: copyCoords(p.coords), timestamp: p.timestamp };
|
||||
resolve({ position, availability: getAndCacheAvailabilityFromResult(position, undefined) });
|
||||
}, (e) => {
|
||||
const error = { code: e.code, message: e.message };
|
||||
resolve({ error, availability: getAndCacheAvailabilityFromResult(undefined, error) });
|
||||
}, options || undefined);
|
||||
});
|
||||
},
|
||||
watch(element, options, watchKey) {
|
||||
if (watches.has(watchKey)) {
|
||||
navigator.geolocation.clearWatch(watches.get(watchKey));
|
||||
}
|
||||
watches.set(watchKey, navigator.geolocation.watchPosition((p) => {
|
||||
const position = { coords: copyCoords(p.coords), timestamp: p.timestamp };
|
||||
getAndCacheAvailabilityFromResult(position, undefined);
|
||||
element.dispatchEvent(new CustomEvent('vaadin-geolocation-position', {
|
||||
detail: position
|
||||
}));
|
||||
}, (e) => {
|
||||
const error = { code: e.code, message: e.message };
|
||||
getAndCacheAvailabilityFromResult(undefined, error);
|
||||
element.dispatchEvent(new CustomEvent('vaadin-geolocation-error', {
|
||||
detail: error
|
||||
}));
|
||||
}, options || undefined));
|
||||
},
|
||||
clearWatch(watchKey) {
|
||||
if (watches.has(watchKey)) {
|
||||
navigator.geolocation.clearWatch(watches.get(watchKey));
|
||||
watches.delete(watchKey);
|
||||
}
|
||||
},
|
||||
async queryAvailability() {
|
||||
const value = await resolveAvailability();
|
||||
// publish without dispatching a change event — there is no previous
|
||||
// cached value to compare against when cachedAvailability is null and
|
||||
// the bootstrap consumer just wants the initial answer.
|
||||
cachedAvailability = value;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
export {};
|
||||
//# sourceMappingURL=Geolocation.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
||||
type VaadinPageVisibility = 'VISIBLE' | 'VISIBLE_NOT_FOCUSED' | 'HIDDEN';
|
||||
/**
|
||||
* Returns the current visibility state synchronously. Used by the bootstrap
|
||||
* path to seed the server-side signal without waiting for a DOM event.
|
||||
*/
|
||||
export declare function currentVisibility(): VaadinPageVisibility;
|
||||
export {};
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
// Firefox defers the visibilitychange event while the window is blurred, so
|
||||
// a blur handler needs to wait long enough for that delivery to land before
|
||||
// concluding the state is really "visible but not focused".
|
||||
const FIREFOX_BLUR_SETTLE_MS = 500;
|
||||
const DEFAULT_BLUR_SETTLE_MS = 10;
|
||||
/**
|
||||
* Returns the current visibility state synchronously. Used by the bootstrap
|
||||
* path to seed the server-side signal without waiting for a DOM event.
|
||||
*/
|
||||
export function currentVisibility() {
|
||||
if (document.hidden) {
|
||||
return 'HIDDEN';
|
||||
}
|
||||
return document.hasFocus() ? 'VISIBLE' : 'VISIBLE_NOT_FOCUSED';
|
||||
}
|
||||
function isFirefox() {
|
||||
// Firefox is the only supported browser that reorders visibilitychange
|
||||
// relative to blur; UA sniffing is acceptable here because the alternative
|
||||
// is waiting the longer interval on every browser.
|
||||
return navigator.userAgent.indexOf('Firefox') > -1;
|
||||
}
|
||||
let blurTimer;
|
||||
// Dispatch on document.body so the server-side Page facade (listening on
|
||||
// the UI element, which is body) can update its signal.
|
||||
function dispatch(state) {
|
||||
document.body.dispatchEvent(new CustomEvent('vaadin-page-visibility-change', { detail: state }));
|
||||
}
|
||||
function clearBlurTimer() {
|
||||
if (blurTimer !== undefined) {
|
||||
clearTimeout(blurTimer);
|
||||
blurTimer = undefined;
|
||||
}
|
||||
}
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
clearBlurTimer();
|
||||
dispatch(document.hidden ? 'HIDDEN' : 'VISIBLE');
|
||||
});
|
||||
window.addEventListener('blur', () => {
|
||||
clearBlurTimer();
|
||||
const delay = isFirefox() ? FIREFOX_BLUR_SETTLE_MS : DEFAULT_BLUR_SETTLE_MS;
|
||||
blurTimer = setTimeout(() => {
|
||||
blurTimer = undefined;
|
||||
if (!document.hidden) {
|
||||
dispatch('VISIBLE_NOT_FOCUSED');
|
||||
}
|
||||
}, delay);
|
||||
});
|
||||
window.addEventListener('focus', () => {
|
||||
clearBlurTimer();
|
||||
if (!document.hidden) {
|
||||
dispatch('VISIBLE');
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=PageVisibility.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"PageVisibility.js","sourceRoot":"","sources":["../../../../src/main/frontend/PageVisibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,4EAA4E;AAC5E,4EAA4E;AAC5E,4DAA4D;AAC5D,MAAM,sBAAsB,GAAG,GAAG,CAAC;AACnC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAElC;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC;AACjE,CAAC;AAED,SAAS,SAAS;IAChB,uEAAuE;IACvE,2EAA2E;IAC3E,mDAAmD;IACnD,OAAO,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,IAAI,SAAoD,CAAC;AAEzD,yEAAyE;AACzE,wDAAwD;AACxD,SAAS,QAAQ,CAAC,KAA2B;IAC3C,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,+BAA+B,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,SAAS,cAAc;IACrB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,YAAY,CAAC,SAAS,CAAC,CAAC;QACxB,SAAS,GAAG,SAAS,CAAC;IACxB,CAAC;AACH,CAAC;AAED,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;IACjD,cAAc,EAAE,CAAC;IACjB,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE;IACnC,cAAc,EAAE,CAAC;IACjB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,sBAAsB,CAAC;IAC5E,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;QAC1B,SAAS,GAAG,SAAS,CAAC;QACtB,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrB,QAAQ,CAAC,qBAAqB,CAAC,CAAC;QAClC,CAAC;IACH,CAAC,EAAE,KAAK,CAAC,CAAC;AACZ,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;IACpC,cAAc,EAAE,CAAC;IACjB,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACrB,QAAQ,CAAC,SAAS,CAAC,CAAC;IACtB,CAAC;AACH,CAAC,CAAC,CAAC","sourcesContent":["/*\n * Copyright 2000-2026 Vaadin Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\ntype VaadinPageVisibility = 'VISIBLE' | 'VISIBLE_NOT_FOCUSED' | 'HIDDEN';\n\n// Firefox defers the visibilitychange event while the window is blurred, so\n// a blur handler needs to wait long enough for that delivery to land before\n// concluding the state is really \"visible but not focused\".\nconst FIREFOX_BLUR_SETTLE_MS = 500;\nconst DEFAULT_BLUR_SETTLE_MS = 10;\n\n/**\n * Returns the current visibility state synchronously. Used by the bootstrap\n * path to seed the server-side signal without waiting for a DOM event.\n */\nexport function currentVisibility(): VaadinPageVisibility {\n if (document.hidden) {\n return 'HIDDEN';\n }\n return document.hasFocus() ? 'VISIBLE' : 'VISIBLE_NOT_FOCUSED';\n}\n\nfunction isFirefox(): boolean {\n // Firefox is the only supported browser that reorders visibilitychange\n // relative to blur; UA sniffing is acceptable here because the alternative\n // is waiting the longer interval on every browser.\n return navigator.userAgent.indexOf('Firefox') > -1;\n}\n\nlet blurTimer: ReturnType<typeof setTimeout> | undefined;\n\n// Dispatch on document.body so the server-side Page facade (listening on\n// the UI element, which is body) can update its signal.\nfunction dispatch(state: VaadinPageVisibility): void {\n document.body.dispatchEvent(new CustomEvent('vaadin-page-visibility-change', { detail: state }));\n}\n\nfunction clearBlurTimer(): void {\n if (blurTimer !== undefined) {\n clearTimeout(blurTimer);\n blurTimer = undefined;\n }\n}\n\ndocument.addEventListener('visibilitychange', () => {\n clearBlurTimer();\n dispatch(document.hidden ? 'HIDDEN' : 'VISIBLE');\n});\n\nwindow.addEventListener('blur', () => {\n clearBlurTimer();\n const delay = isFirefox() ? FIREFOX_BLUR_SETTLE_MS : DEFAULT_BLUR_SETTLE_MS;\n blurTimer = setTimeout(() => {\n blurTimer = undefined;\n if (!document.hidden) {\n dispatch('VISIBLE_NOT_FOCUSED');\n }\n }, delay);\n});\n\nwindow.addEventListener('focus', () => {\n clearBlurTimer();\n if (!document.hidden) {\n dispatch('VISIBLE');\n }\n});\n"]}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Outlet } from 'react-router';
|
||||
import { ReactAdapterElement } from "Frontend/generated/flow/ReactAdapter.js";
|
||||
import React from "react";
|
||||
|
||||
class ReactRouterOutletElement extends ReactAdapterElement {
|
||||
public async connectedCallback() {
|
||||
await super.connectedCallback();
|
||||
this.style.display = 'contents';
|
||||
}
|
||||
|
||||
protected render(): React.ReactElement | null {
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
customElements.define('react-router-outlet', ReactRouterOutletElement);
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Returns the current screen orientation type synchronously, or
|
||||
* {@code 'unsupported'} if the Screen Orientation API is unavailable. Used by
|
||||
* the bootstrap path to seed the server-side signal without waiting for a DOM
|
||||
* event.
|
||||
*/
|
||||
export declare function currentScreenOrientationType(): string;
|
||||
/**
|
||||
* Returns the current screen orientation angle synchronously, or 0 if the
|
||||
* Screen Orientation API is unavailable.
|
||||
*/
|
||||
export declare function currentScreenOrientationAngle(): number;
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
/**
|
||||
* Returns the current screen orientation type synchronously, or
|
||||
* {@code 'unsupported'} if the Screen Orientation API is unavailable. Used by
|
||||
* the bootstrap path to seed the server-side signal without waiting for a DOM
|
||||
* event.
|
||||
*/
|
||||
export function currentScreenOrientationType() {
|
||||
return screen.orientation?.type ?? 'unsupported';
|
||||
}
|
||||
/**
|
||||
* Returns the current screen orientation angle synchronously, or 0 if the
|
||||
* Screen Orientation API is unavailable.
|
||||
*/
|
||||
export function currentScreenOrientationAngle() {
|
||||
return screen.orientation?.angle ?? 0;
|
||||
}
|
||||
// Dispatch on document.body so the server-side ScreenOrientation facade
|
||||
// (listening on the UI element, which is body) can update its signal.
|
||||
function dispatch(detail) {
|
||||
document.body.dispatchEvent(new CustomEvent('vaadin-screen-orientation-change', { detail }));
|
||||
}
|
||||
if (screen.orientation) {
|
||||
screen.orientation.addEventListener('change', () => {
|
||||
dispatch({
|
||||
type: screen.orientation.type,
|
||||
angle: screen.orientation.angle
|
||||
});
|
||||
});
|
||||
}
|
||||
const $wnd = window;
|
||||
$wnd.Vaadin ??= {};
|
||||
$wnd.Vaadin.Flow ??= {};
|
||||
function lockErrorCode(domExceptionName) {
|
||||
switch (domExceptionName) {
|
||||
case 'NotSupportedError':
|
||||
return 'NOT_SUPPORTED';
|
||||
case 'SecurityError':
|
||||
return 'SECURITY';
|
||||
case 'AbortError':
|
||||
return 'ABORT';
|
||||
default:
|
||||
return 'UNKNOWN';
|
||||
}
|
||||
}
|
||||
$wnd.Vaadin.Flow.screenOrientation = {
|
||||
// Always resolves so the server-side .then(success, error) chain only
|
||||
// receives the "error" branch on a bridge failure (lost connection, etc.).
|
||||
// Rejected DOMExceptions are folded into the resolved result so the server
|
||||
// can decode them as a record without forfeiting the JS-bridge error arm.
|
||||
lock(type) {
|
||||
if (!screen.orientation || typeof screen.orientation.lock !== 'function') {
|
||||
return Promise.resolve({
|
||||
success: false,
|
||||
code: 'NOT_SUPPORTED',
|
||||
message: 'Screen Orientation API is not supported in this browser.'
|
||||
});
|
||||
}
|
||||
return screen.orientation
|
||||
.lock(type)
|
||||
.then(() => ({ success: true }))
|
||||
.catch((e) => {
|
||||
const code = lockErrorCode(e.name);
|
||||
const message = e.message ?? '';
|
||||
return {
|
||||
success: false,
|
||||
code,
|
||||
// The DOMException name is dropped once mapped to a typed code;
|
||||
// keep it in the message for diagnostics when no code matches.
|
||||
message: code === 'UNKNOWN' && e.name ? `${e.name}: ${message}` : message
|
||||
};
|
||||
});
|
||||
},
|
||||
unlock() {
|
||||
screen.orientation?.unlock();
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=ScreenOrientation.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
export {};
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
// Whether the server-side has asked us to hold the lock. The browser releases
|
||||
// the lock whenever the tab is hidden; this flag is what lets the
|
||||
// visibilitychange handler re-acquire silently when the tab returns.
|
||||
let wanted = false;
|
||||
let sentinel = null;
|
||||
let visibilityListenerInstalled = false;
|
||||
function dispatch(element, state) {
|
||||
element.dispatchEvent(new CustomEvent('vaadin-wake-lock-change', { detail: state }));
|
||||
}
|
||||
async function acquire(element) {
|
||||
if (sentinel) {
|
||||
return { state: 'granted' };
|
||||
}
|
||||
if (!window.isSecureContext || !('wakeLock' in navigator)) {
|
||||
return {
|
||||
state: 'error',
|
||||
errorCode: 'UNSUPPORTED',
|
||||
message: window.isSecureContext
|
||||
? 'Screen Wake Lock API not implemented in this browser'
|
||||
: 'Screen Wake Lock API requires a secure context (HTTPS or localhost)'
|
||||
};
|
||||
}
|
||||
try {
|
||||
const next = await navigator.wakeLock.request('screen');
|
||||
// The user (or the browser) may have released the lock or the tab may have
|
||||
// been hidden again while the request was in flight.
|
||||
if (!wanted || document.visibilityState !== 'visible') {
|
||||
try {
|
||||
await next.release();
|
||||
}
|
||||
catch (_e) {
|
||||
// Ignore; releasing an already-released sentinel throws on some
|
||||
// browsers and there is nothing meaningful to do here.
|
||||
}
|
||||
return { state: 'deferred' };
|
||||
}
|
||||
sentinel = next;
|
||||
next.addEventListener('release', () => {
|
||||
sentinel = null;
|
||||
dispatch(element, 'RELEASED');
|
||||
});
|
||||
dispatch(element, 'ACTIVE');
|
||||
return { state: 'granted' };
|
||||
}
|
||||
catch (e) {
|
||||
const name = e?.name;
|
||||
const errorCode = name === 'NotAllowedError' ? 'NOT_ALLOWED' : 'UNKNOWN';
|
||||
return {
|
||||
state: 'error',
|
||||
errorCode,
|
||||
message: e?.message ? String(e.message) : String(e)
|
||||
};
|
||||
}
|
||||
}
|
||||
function installVisibilityListener(element) {
|
||||
if (visibilityListenerInstalled) {
|
||||
return;
|
||||
}
|
||||
visibilityListenerInstalled = true;
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (wanted && !sentinel && document.visibilityState === 'visible') {
|
||||
acquire(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
const $wnd = window;
|
||||
$wnd.Vaadin ??= {};
|
||||
$wnd.Vaadin.Flow ??= {};
|
||||
$wnd.Vaadin.Flow.wakeLock = {
|
||||
request(element) {
|
||||
wanted = true;
|
||||
installVisibilityListener(element);
|
||||
if (document.visibilityState !== 'visible') {
|
||||
// The browser will not grant a lock while the page is hidden; the
|
||||
// visibilitychange listener will pick it up on the next 'visible'.
|
||||
return Promise.resolve({ state: 'deferred' });
|
||||
}
|
||||
return acquire(element);
|
||||
},
|
||||
async release(element) {
|
||||
wanted = false;
|
||||
if (!sentinel) {
|
||||
return;
|
||||
}
|
||||
const current = sentinel;
|
||||
sentinel = null;
|
||||
try {
|
||||
await current.release();
|
||||
}
|
||||
catch (_e) {
|
||||
// Ignore; the 'release' event listener installed in acquire() also
|
||||
// dispatches RELEASED, so the state still reaches the server even when
|
||||
// the explicit release() call rejects.
|
||||
}
|
||||
dispatch(element, 'RELEASED');
|
||||
},
|
||||
queryAvailability() {
|
||||
if (!window.isSecureContext) {
|
||||
return 'UNSUPPORTED';
|
||||
}
|
||||
return 'wakeLock' in navigator ? 'SUPPORTED' : 'UNSUPPORTED';
|
||||
}
|
||||
};
|
||||
export {};
|
||||
//# sourceMappingURL=WakeLock.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Returns whether the current browser exposes the Web Share API
|
||||
* (`navigator.share`). Used by the bootstrap path to seed the server-side
|
||||
* support signal without waiting for a DOM event.
|
||||
*/
|
||||
export declare function isShareSupported(): boolean;
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2000-2026 Vaadin Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
/**
|
||||
* Returns whether the current browser exposes the Web Share API
|
||||
* (`navigator.share`). Used by the bootstrap path to seed the server-side
|
||||
* support signal without waiting for a DOM event.
|
||||
*/
|
||||
export function isShareSupported() {
|
||||
return typeof navigator.share === 'function';
|
||||
}
|
||||
//# sourceMappingURL=WebShare.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"WebShare.js","sourceRoot":"","sources":["../../../../src/main/frontend/WebShare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,SAAS,CAAC,KAAK,KAAK,UAAU,CAAC;AAC/C,CAAC","sourcesContent":["/*\n * Copyright 2000-2026 Vaadin Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\n/**\n * Returns whether the current browser exposes the Web Share API\n * (`navigator.share`). Used by the bootstrap path to seed the server-side\n * support signal without waiting for a DOM event.\n */\nexport function isShareSupported(): boolean {\n return typeof navigator.share === 'function';\n}\n"]}
|
||||
@@ -0,0 +1,244 @@
|
||||
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
||||
import { timeOut } from '@vaadin/component-base/src/async.js';
|
||||
import { ComboBoxPlaceholder } from '@vaadin/combo-box/src/vaadin-combo-box-placeholder.js';
|
||||
|
||||
window.Vaadin.Flow.comboBoxConnector = {};
|
||||
window.Vaadin.Flow.comboBoxConnector.initLazy = (comboBox) => {
|
||||
// Check whether the connector was already initialized for the ComboBox
|
||||
if (comboBox.$connector) {
|
||||
return;
|
||||
}
|
||||
|
||||
comboBox.$connector = {};
|
||||
|
||||
let cache = {};
|
||||
const placeHolder = new window.Vaadin.ComboBoxPlaceholder();
|
||||
|
||||
let lastTypedFilter = '';
|
||||
let lastRequestedRange = [-1, -1];
|
||||
let lastRequestedFilter = '';
|
||||
let needsDataCommunicatorReset = false;
|
||||
|
||||
const dataProvider = function (params, callback) {
|
||||
if (params.pageSize != comboBox.pageSize) {
|
||||
throw 'Invalid pageSize';
|
||||
}
|
||||
|
||||
if (comboBox._clientSideFilter) {
|
||||
if (cache[0]) {
|
||||
performClientSideFilter(cache[0], params.filter, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
// First fetch: ignore the typed filter so we get the full dataset
|
||||
params = { ...params, filter: '' };
|
||||
}
|
||||
|
||||
if (lastTypedFilter !== params.filter) {
|
||||
cache = {};
|
||||
lastTypedFilter = params.filter;
|
||||
lastRequestedRange = [-1, -1];
|
||||
|
||||
comboBox._filterDebouncer = Debouncer.debounce(
|
||||
comboBox._filterDebouncer,
|
||||
timeOut.after(comboBox._filterTimeout ?? 500),
|
||||
() => {
|
||||
// Filter cycled back to what server last received — force re-emit.
|
||||
if (params.filter === lastRequestedFilter) {
|
||||
needsDataCommunicatorReset = true;
|
||||
}
|
||||
|
||||
comboBox.clearCache();
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comboBox._filterDebouncer?.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If buffer-prefetch already cached this page, commit it without a server
|
||||
// round-trip; otherwise ask the server.
|
||||
if (cache[params.page]) {
|
||||
callback(cache[params.page], comboBox.size);
|
||||
return;
|
||||
}
|
||||
|
||||
comboBox.$connector.requestPage(params.page, params.filter);
|
||||
};
|
||||
|
||||
comboBox.$connector.getViewportRange = function () {
|
||||
const indices = Array.from(comboBox._scroller?.children ?? [])
|
||||
.map((child) => child.index)
|
||||
.filter((index) => Number.isFinite(index))
|
||||
.sort((a, b) => a - b);
|
||||
if (indices.length === 0) {
|
||||
return [0, 0];
|
||||
}
|
||||
return [indices[0], indices[indices.length - 1]];
|
||||
};
|
||||
|
||||
comboBox.$connector.requestPage = function (page, filter) {
|
||||
let viewportRange = comboBox.$connector.getViewportRange();
|
||||
const buffer = viewportRange[1] - viewportRange[0];
|
||||
const sizeLimit = Number.isFinite(comboBox.size) ? comboBox.size : Number.POSITIVE_INFINITY;
|
||||
viewportRange[0] = Math.max(viewportRange[0] - buffer, 0);
|
||||
viewportRange[1] = Math.min(viewportRange[1] + buffer, sizeLimit - 1);
|
||||
|
||||
let viewportPageRange = [
|
||||
Math.floor(viewportRange[0] / comboBox.pageSize),
|
||||
Math.floor(viewportRange[1] / comboBox.pageSize)
|
||||
];
|
||||
|
||||
// Collapse to the requested page when it's outside the current viewport,
|
||||
// so confirm() can resolve callbacks left behind by fast scrolling.
|
||||
if (page < viewportPageRange[0] || page > viewportPageRange[1]) {
|
||||
viewportPageRange = [page, page];
|
||||
}
|
||||
|
||||
if (lastRequestedRange[0] != viewportPageRange[0] || lastRequestedRange[1] != viewportPageRange[1]) {
|
||||
const startIndex = viewportPageRange[0] * comboBox.pageSize;
|
||||
const endIndex = (viewportPageRange[1] + 1) * comboBox.pageSize;
|
||||
comboBox.$server.setViewportRange(startIndex, endIndex - startIndex, filter);
|
||||
}
|
||||
|
||||
if (needsDataCommunicatorReset) {
|
||||
comboBox.$server.resetDataCommunicator();
|
||||
needsDataCommunicatorReset = false;
|
||||
}
|
||||
|
||||
lastRequestedRange = viewportPageRange;
|
||||
lastRequestedFilter = filter;
|
||||
};
|
||||
|
||||
comboBox.$connector.clear = (start, length) => {
|
||||
const { pageSize } = comboBox;
|
||||
const firstPage = Math.floor(start / pageSize);
|
||||
const lastPage = firstPage + Math.ceil(length / pageSize);
|
||||
|
||||
for (let page = firstPage; page < lastPage; page++) {
|
||||
delete cache[page];
|
||||
}
|
||||
|
||||
for (let index = firstPage * pageSize; index < lastPage * pageSize; index++) {
|
||||
if (comboBox.filteredItems[index]) {
|
||||
comboBox.filteredItems[index] = placeHolder;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
comboBox.$connector.filter = (item, filter) => {
|
||||
filter = filter ? filter.toString().toLowerCase() : '';
|
||||
return comboBox._getItemLabel(item, comboBox.itemLabelPath).toString().toLowerCase().indexOf(filter) > -1;
|
||||
};
|
||||
|
||||
comboBox.$connector.set = (index, items, filter) => {
|
||||
if (filter !== lastTypedFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index % comboBox.pageSize != 0) {
|
||||
throw 'Got new data to index ' + index + ' which is not aligned with the page size of ' + comboBox.pageSize;
|
||||
}
|
||||
|
||||
const { pendingRequests } = comboBox.__dataProviderController.rootCache;
|
||||
if (index === 0 && items.length === 0 && pendingRequests[0]) {
|
||||
// Makes sure that the dataProvider callback is called even when server
|
||||
// returns empty data set (no items match the filter).
|
||||
cache[0] = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const firstPageToSet = index / comboBox.pageSize;
|
||||
const updatedPageCount = Math.ceil(items.length / comboBox.pageSize);
|
||||
|
||||
for (let i = 0; i < updatedPageCount; i++) {
|
||||
let page = firstPageToSet + i;
|
||||
let slice = items.slice(i * comboBox.pageSize, (i + 1) * comboBox.pageSize);
|
||||
|
||||
cache[page] = slice;
|
||||
}
|
||||
};
|
||||
|
||||
comboBox.$connector.updateData = (items) => {
|
||||
const itemsMap = new Map(items.map((item) => [item.key, item]));
|
||||
|
||||
comboBox.filteredItems = comboBox.filteredItems.map((item) => {
|
||||
return itemsMap.get(item.key) || item;
|
||||
});
|
||||
};
|
||||
|
||||
comboBox.$connector.updateSize = function (newSize) {
|
||||
if (!comboBox._clientSideFilter) {
|
||||
// FIXME: It may be that this size set is unnecessary, since when
|
||||
// providing data to combobox via callback we may use data's size.
|
||||
// However, if this size reflect the whole data size, including
|
||||
// data not fetched yet into client side, and combobox expect it
|
||||
// to be set as such, the at least, we don't need it in case the
|
||||
// filter is clientSide only, since it'll increase the height of
|
||||
// the popup at only at first user filter to this size, while the
|
||||
// filtered items count are less.
|
||||
comboBox.size = newSize;
|
||||
}
|
||||
};
|
||||
|
||||
comboBox.$connector.reset = function () {
|
||||
comboBox._filterDebouncer?.cancel();
|
||||
comboBox._filterDebouncer = null;
|
||||
cache = {};
|
||||
lastRequestedRange = [-1, -1];
|
||||
lastTypedFilter = '';
|
||||
comboBox.clearCache();
|
||||
};
|
||||
|
||||
comboBox.$connector.confirm = function (id, filter) {
|
||||
if (filter !== lastTypedFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We're done applying changes from this batch, resolve pending
|
||||
// callbacks
|
||||
const { pendingRequests } = comboBox.__dataProviderController.rootCache;
|
||||
Object.entries(pendingRequests).forEach(([page, callback]) => {
|
||||
const items = cache[page];
|
||||
|
||||
if (comboBox._clientSideFilter && items) {
|
||||
performClientSideFilter(items, comboBox.filter, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(items ?? [], comboBox.size);
|
||||
delete cache[page];
|
||||
});
|
||||
|
||||
// Let server know we're done
|
||||
comboBox.$server.confirmUpdate(id);
|
||||
};
|
||||
|
||||
// Perform filter on client side (here) using the items from specified page
|
||||
// and submitting the filtered items to specified callback.
|
||||
// The filter used is the one from combobox, not the lastFilter stored since
|
||||
// that may not reflect user's input.
|
||||
const performClientSideFilter = function (page, filter, callback) {
|
||||
let filteredItems = page;
|
||||
|
||||
if (filter) {
|
||||
filteredItems = page.filter((item) => comboBox.$connector.filter(item, filter));
|
||||
}
|
||||
|
||||
callback(filteredItems, filteredItems.length);
|
||||
};
|
||||
|
||||
// Prevent setting the custom value as the 'value'-prop automatically
|
||||
comboBox.addEventListener('custom-value-set', (e) => e.preventDefault());
|
||||
|
||||
comboBox.itemClassNameGenerator = function (item) {
|
||||
return item.className || '';
|
||||
};
|
||||
|
||||
// Assign last, after all `$connector` functions are defined.
|
||||
comboBox.dataProvider = dataProvider;
|
||||
};
|
||||
|
||||
window.Vaadin.ComboBoxPlaceholder = ComboBoxPlaceholder;
|
||||
@@ -0,0 +1,124 @@
|
||||
function getContainer(appId, nodeId) {
|
||||
try {
|
||||
return window.Vaadin.Flow.clients[appId].getByNodeId(nodeId);
|
||||
} catch (error) {
|
||||
console.error('Could not get node %s from app %s', nodeId, appId);
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the connector for a context menu element.
|
||||
*
|
||||
* @param {HTMLElement} contextMenu
|
||||
* @param {string} appId
|
||||
*/
|
||||
function initLazy(contextMenu, appId) {
|
||||
if (contextMenu.$connector) {
|
||||
return;
|
||||
}
|
||||
|
||||
contextMenu.$connector = {
|
||||
/**
|
||||
* Generates and assigns the items to the context menu.
|
||||
*
|
||||
* @param {number} nodeId
|
||||
*/
|
||||
generateItems(nodeId) {
|
||||
const items = generateItemsTree(appId, nodeId);
|
||||
|
||||
contextMenu.items = items;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an items tree compatible with the context-menu web component
|
||||
* by traversing the given Flow DOM tree of context menu item nodes
|
||||
* whose root node is identified by the `nodeId` argument.
|
||||
*
|
||||
* The app id is required to access the store of Flow DOM nodes.
|
||||
*
|
||||
* @param {string} appId
|
||||
* @param {number} nodeId
|
||||
*/
|
||||
function generateItemsTree(appId, nodeId) {
|
||||
const container = getContainer(appId, nodeId);
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Array.from(container.children).map((child) => {
|
||||
const item = {
|
||||
component: child,
|
||||
checked: child._checked,
|
||||
keepOpen: child._keepOpen,
|
||||
className: child.className,
|
||||
theme: child.__theme,
|
||||
tooltip: child.tooltip,
|
||||
tooltipPosition: child.tooltipPosition
|
||||
};
|
||||
// Do not hardcode tag name to allow `vaadin-menu-bar-item`
|
||||
if (child._hasVaadinItemMixin && child._containerNodeId) {
|
||||
item.children = generateItemsTree(appId, child._containerNodeId);
|
||||
}
|
||||
child._item = item;
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the checked state for a context menu item.
|
||||
*
|
||||
* This method is supposed to be called when the context menu item is closed,
|
||||
* so there is no need for triggering a re-render eagarly.
|
||||
*
|
||||
* @param {HTMLElement} component
|
||||
* @param {boolean} checked
|
||||
*/
|
||||
function setChecked(component, checked) {
|
||||
if (component._item) {
|
||||
component._item.checked = checked;
|
||||
|
||||
// Set the attribute in the connector to show the checkmark
|
||||
// without having to re-render the whole menu while opened.
|
||||
if (component._item.keepOpen) {
|
||||
component.toggleAttribute('menu-item-checked', checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the keep open state for a context menu item.
|
||||
*
|
||||
* @param {HTMLElement} component
|
||||
* @param {boolean} keepOpen
|
||||
*/
|
||||
function setKeepOpen(component, keepOpen) {
|
||||
if (component._item) {
|
||||
component._item.keepOpen = keepOpen;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the theme for a context menu item.
|
||||
*
|
||||
* This method is supposed to be called when the context menu item is closed,
|
||||
* so there is no need for triggering a re-render eagarly.
|
||||
*
|
||||
* @param {HTMLElement} component
|
||||
* @param {string | undefined | null} theme
|
||||
*/
|
||||
function setTheme(component, theme) {
|
||||
if (component._item) {
|
||||
component._item.theme = theme;
|
||||
}
|
||||
}
|
||||
|
||||
window.Vaadin.Flow.contextMenuConnector = {
|
||||
initLazy,
|
||||
generateItemsTree,
|
||||
setChecked,
|
||||
setKeepOpen,
|
||||
setTheme
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
import * as Gestures from '@vaadin/component-base/src/gestures.js';
|
||||
|
||||
function init(target) {
|
||||
if (target.$contextMenuTargetConnector) {
|
||||
return;
|
||||
}
|
||||
|
||||
target.$contextMenuTargetConnector = {
|
||||
openOnHandler(e) {
|
||||
// used by Grid to prevent context menu on selection column click
|
||||
if (target.preventContextMenu && target.preventContextMenu(e)) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// The menu is opened later, after a server round-trip, when the event has
|
||||
// finished dispatching and `composedPath()` returns an empty array. Capture
|
||||
// the composed path now so the menu can resolve the target inside a shadow
|
||||
// root (e.g. a grid cell) instead of the retargeted host.
|
||||
e.__composedPath = e.composedPath();
|
||||
this.$contextMenuTargetConnector.openEvent = e;
|
||||
let detail = {};
|
||||
if (target.getContextMenuBeforeOpenDetail) {
|
||||
detail = target.getContextMenuBeforeOpenDetail(e);
|
||||
}
|
||||
target.dispatchEvent(
|
||||
new CustomEvent('vaadin-context-menu-before-open', {
|
||||
detail: detail
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
updateOpenOn(eventType) {
|
||||
this.removeListener();
|
||||
this.openOnEventType = eventType;
|
||||
|
||||
customElements.whenDefined('vaadin-context-menu').then(() => {
|
||||
if (Gestures.gestures[eventType]) {
|
||||
Gestures.addListener(target, eventType, this.openOnHandler);
|
||||
} else {
|
||||
target.addEventListener(eventType, this.openOnHandler);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeListener() {
|
||||
if (this.openOnEventType) {
|
||||
if (Gestures.gestures[this.openOnEventType]) {
|
||||
Gestures.removeListener(target, this.openOnEventType, this.openOnHandler);
|
||||
} else {
|
||||
target.removeEventListener(this.openOnEventType, this.openOnHandler);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
openMenu(contextMenu) {
|
||||
contextMenu.open(this.openEvent);
|
||||
},
|
||||
|
||||
removeConnector() {
|
||||
this.removeListener();
|
||||
target.$contextMenuTargetConnector = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
window.Vaadin.Flow.contextMenuTargetConnector = { init };
|
||||
@@ -0,0 +1 @@
|
||||
// Full cdn version: 25.2.5-undefined
|
||||
@@ -0,0 +1,3 @@
|
||||
export { registerImporter, createChildrenDefinitions } from './copilot/figma-public/figma-api';
|
||||
export type { FigmaNode, ImportMetadata } from './copilot/figma-public/figma-api';
|
||||
export type { ComponentDefinition, ComponentDefinitionProperties } from './copilot/shared/flow-utils';
|
||||
File diff suppressed because one or more lines are too long
+99
@@ -0,0 +1,99 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { c as t, n, t as r } from "./dom-utils-Cuv93-tQ.js";
|
||||
import { i, n as a, r as o, t as s } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { a as c, i as l } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { i as u, n as d } from "./copilot-modes-wJyMqHUb.js";
|
||||
//#region frontend/copilot/shared/section-panels/base-panel.ts
|
||||
var f, p = e((() => {
|
||||
i(), c(), s(), t(), u(), f = class extends o {
|
||||
constructor(...e) {
|
||||
super(...e), this.eventBusRemovers = [], this.messageHandlers = {}, this.handleESC = (e) => {
|
||||
let t = a.getPanelByTag(this.tagName);
|
||||
d().appInteractable && t && !t.individual || e.key === "Escape" && r(this);
|
||||
};
|
||||
}
|
||||
getPreferredWidth() {
|
||||
return 400;
|
||||
}
|
||||
getPreferredHeight() {
|
||||
return 400;
|
||||
}
|
||||
getPreferredMaxWidth() {
|
||||
return 500;
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
onEventBus(e, t) {
|
||||
this.eventBusRemovers.push(l.on(e, t));
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback(), this.addESCListener();
|
||||
}
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback(), this.eventBusRemovers.forEach((e) => e()), this.removeESCListener();
|
||||
}
|
||||
addESCListener() {
|
||||
document.addEventListener("keydown", this.handleESC);
|
||||
}
|
||||
removeESCListener() {
|
||||
document.removeEventListener("keydown", this.handleESC);
|
||||
}
|
||||
onCommand(e, t) {
|
||||
this.messageHandlers[e] = t;
|
||||
}
|
||||
handleMessage(e) {
|
||||
return this.messageHandlers[e.command] ? (this.messageHandlers[e.command].call(this, e), !0) : !1;
|
||||
}
|
||||
repositionInPopover(e) {
|
||||
let t = Math.max(this.scrollHeight, this.offsetHeight);
|
||||
if (t === 0) return;
|
||||
let n = e.getAttribute("for");
|
||||
if (!n) return;
|
||||
let r = e.parentElement?.querySelector(`#${n}`);
|
||||
if (!r) return;
|
||||
let i = r.getBoundingClientRect(), a = i.top - 16, o = window.innerHeight - 16 - i.bottom, s = (e.position ?? e.getAttribute("position") ?? "bottom").startsWith("top"), c = s ? a : o;
|
||||
c >= t || (s ? o : a) <= c || (e.position = s ? "bottom" : "top", e._overlayElement?._updatePosition?.());
|
||||
}
|
||||
requestLayoutUpdate() {
|
||||
let e = this.localName;
|
||||
if (a.positionUpdatedManually(e)) return Promise.resolve();
|
||||
let t = a.getPanelByTag(e);
|
||||
return t ? new Promise((r) => {
|
||||
requestAnimationFrame(() => {
|
||||
let i = n(this, "vaadin-dialog");
|
||||
if (!i) {
|
||||
let e = n(this, "vaadin-popover");
|
||||
e && this.repositionInPopover(e), r();
|
||||
return;
|
||||
}
|
||||
let o = this.parentElement?.getBoundingClientRect();
|
||||
if (!o || o.width === 0 && o.height === 0) {
|
||||
r();
|
||||
return;
|
||||
}
|
||||
let s = i._overlayElement, c = s?.shadowRoot?.querySelector("[part=\"overlay\"]"), l = s?.shadowRoot?.querySelector("[part=\"content\"]"), u = s?.shadowRoot?.querySelector("[part=\"footer\"]");
|
||||
if (!c || !l) {
|
||||
r();
|
||||
return;
|
||||
}
|
||||
let d = Math.max(this.scrollWidth, this.offsetWidth, o.width), f = Math.max(this.scrollHeight, this.offsetHeight, o.height), p = c.getBoundingClientRect(), m = l.getBoundingClientRect(), h = Math.max(0, p.width - m.width), g = Math.max(0, p.height - m.height), _ = u?.getBoundingClientRect(), v = !!_ && _.width > 0 && _.height > 0, y = v ? Math.max(0, p.left - _.left) + Math.max(0, _.right - p.right) : 0, b = v ? Math.max(0, p.top - _.top) + Math.max(0, _.bottom - p.bottom) : 0, x = this.getPreferredMaxWidth(), S = Math.floor(window.innerHeight * 2 / 3), C = Math.max(0, this.getPreferredWidth()), w = Math.max(0, this.getPreferredHeight()), T = Math.min(x, Math.max(120, window.innerWidth - 32)), E = Math.min(S, Math.max(120, window.innerHeight - 32)), D = Math.max(120, Math.min(T, Math.max(C, Math.ceil(d + h + y)))), O = Math.max(120, Math.min(E, Math.max(w, Math.ceil(f + g + b)))), k = `${D}px`, A = `${O}px`;
|
||||
i.setAttribute("width", k), i.setAttribute("height", A), i.width = k, i.height = A;
|
||||
let j = t.position, M = Number.parseFloat(i.getAttribute("top") ?? ""), N = Number.parseFloat(i.getAttribute("left") ?? ""), P = j?.top ?? (Number.isNaN(M) ? 0 : M), F = j?.left ?? (Number.isNaN(N) ? 0 : N), I = document.querySelector("copilot-main")?.shadowRoot?.querySelector(`copilot-toolbar #${e}-toolbar-btn`), L = P, R = F;
|
||||
if (I) {
|
||||
let e = I.getBoundingClientRect(), t = e.top - 16 - 16, n = window.innerHeight - 16 - e.bottom - 16;
|
||||
L = t >= O || t >= n ? e.top - O - 16 : e.bottom + 16, R = e.left + e.width / 2 - D / 2;
|
||||
}
|
||||
L = Math.max(16, Math.min(L, window.innerHeight - 16 - O)), R = Math.max(16, Math.min(R, window.innerWidth - 16 - D)), a.updatePanel(e, { position: {
|
||||
top: L,
|
||||
left: R,
|
||||
width: D,
|
||||
height: O
|
||||
} }, !1), r();
|
||||
});
|
||||
}) : Promise.resolve();
|
||||
}
|
||||
};
|
||||
}));
|
||||
//#endregion
|
||||
export { p as n, f as t };
|
||||
@@ -0,0 +1,16 @@
|
||||
//#region \0rolldown/runtime.js
|
||||
var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescriptor, r = Object.getOwnPropertyNames, i = Object.getPrototypeOf, a = Object.prototype.hasOwnProperty, o = (e, t) => () => (e && (t = e(e = 0)), t), s = (e, t) => () => (t || e((t = { exports: {} }).exports, t), t.exports), c = (e, i, o, s) => {
|
||||
if (i && typeof i == "object" || typeof i == "function") for (var c = r(i), l = 0, u = c.length, d; l < u; l++) d = c[l], !a.call(e, d) && d !== o && t(e, d, {
|
||||
get: ((e) => i[e]).bind(null, d),
|
||||
enumerable: !(s = n(i, d)) || s.enumerable
|
||||
});
|
||||
return e;
|
||||
}, l = (n, r, a) => (a = n == null ? {} : e(i(n)), c(r || !n || !n.__esModule ? t(a, "default", {
|
||||
value: n,
|
||||
enumerable: !0
|
||||
}) : a, n)), u = /* @__PURE__ */ ((e) => typeof require < "u" ? require : typeof Proxy < "u" ? new Proxy(e, { get: (e, t) => (typeof require < "u" ? require : e)[t] }) : e)(function(e) {
|
||||
if (typeof require < "u") return require.apply(this, arguments);
|
||||
throw Error("Calling `require` for \"" + e + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
|
||||
});
|
||||
//#endregion
|
||||
export { l as i, o as n, u as r, s as t };
|
||||
@@ -0,0 +1,10 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
//#region frontend/copilot/shared/consts.ts
|
||||
var t, n, r, i, a, o, s, c, l, u = e((() => {
|
||||
t = "copilot-", n = "25.2.5", r = "undefined", i = r === "undefined" ? "" : r, a = "attention-required", o = "https://plugins.jetbrains.com/plugin/23758-vaadin", s = "https://marketplace.visualstudio.com/items?itemName=vaadin.vaadin-vscode", c = "https://marketplace.eclipse.org/content/vaadin-tools", l = {
|
||||
sectionId: "custom-components",
|
||||
sectionName: "Custom Components"
|
||||
};
|
||||
}));
|
||||
//#endregion
|
||||
export { o as a, s as c, c as i, u as l, i as n, t as o, l as r, n as s, a as t };
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { L as t, R as n, at as r, dt as i, n as a, o, r as s, t as c, u as l } from "./icons-CwakCZgK.js";
|
||||
import { a as u, c as d, i as f, l as p, o as m } from "./consts-CSALuSsm.js";
|
||||
import { a as h, d as g, i as _, l as v, n as y, o as b, r as x, s as S, t as C } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { a as w, i as T, n as E, r as D } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { i as O, n as k } from "./copilot-modes-wJyMqHUb.js";
|
||||
import { i as A, o as j } from "./copilot-error-handler-9OpssAH1.js";
|
||||
import { n as M, t as N } from "./early-project-state-LGwavSyI.js";
|
||||
import { i as P, o as F, s as I, t as L } from "./copilot-development-setup-user-guide-utils-DzEVQbWO.js";
|
||||
import { n as R, t as z } from "./base-panel-Fr0D1ZcU.js";
|
||||
//#region frontend/copilot/copilot-development-setup-user-guide.ts
|
||||
function B(e, t) {
|
||||
if (!t) return !0;
|
||||
let [n, r, i] = t.split(".").map((e) => Number.parseInt(e)), [a, o, s] = e.split(".").map((e) => Number.parseInt(e));
|
||||
if (n < a) return !0;
|
||||
if (n === a) {
|
||||
if (r < o) return !0;
|
||||
if (r === o) return i < s;
|
||||
}
|
||||
return !1;
|
||||
}
|
||||
var V, H, U, W, G, K;
|
||||
//#endregion
|
||||
e((() => {
|
||||
a(), S(), C(), s(), r(), p(), D(), I(), w(), j(), t(), M(), O(), R(), _(), b(), H = "https://github.com/JetBrains/JetBrainsRuntime/releases", U = "Download complete", W = (V = class extends z {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
constructor() {
|
||||
super(), this.javaPluginSectionOpened = !1, this.hotswapSectionOpened = !1, this.hotswapTab = "hotswapagent", this.downloadStatusMessages = [], this.downloadProgress = 0, this.onDownloadStatusUpdate = this.downloadStatusUpdate.bind(this), this.handleESC = (e) => {
|
||||
k().appInteractable || e.key === "Escape" && y.openPanel(K.tag);
|
||||
}, this.reaction(() => [N.jdkInfo, E.idePluginState], () => {
|
||||
E.idePluginState && (!E.idePluginState.ide || !E.idePluginState.active ? this.javaPluginSectionOpened = !0 : (!new Set(["vscode", "intellij"]).has(E.idePluginState.ide) || !E.idePluginState.active) && (this.javaPluginSectionOpened = !1)), N.jdkInfo && P() !== "success" && (this.hotswapSectionOpened = !0);
|
||||
}, { fireImmediately: !0 });
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback(), this.classList.add("contents"), T.on("set-up-vs-code-hotswap-status", this.onDownloadStatusUpdate);
|
||||
}
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback(), T.off("set-up-vs-code-hotswap-status", this.onDownloadStatusUpdate);
|
||||
}
|
||||
render() {
|
||||
let e = {
|
||||
intellij: E.idePluginState?.ide === "intellij",
|
||||
vscode: E.idePluginState?.ide === "vscode",
|
||||
eclipse: E.idePluginState?.ide === "eclipse",
|
||||
idePluginInstalled: !!E.idePluginState?.active
|
||||
};
|
||||
return l`
|
||||
${this.renderPluginSection(e)}
|
||||
<hr class="border-b border-e-0 border-s-0 border-t-0 mx-4 my-0" />
|
||||
${this.renderHotswapSection(e)}
|
||||
`;
|
||||
}
|
||||
renderPluginSection(e) {
|
||||
let t = "";
|
||||
e.intellij ? t = "IntelliJ" : e.vscode ? t = "VS Code" : e.eclipse && (t = "Eclipse");
|
||||
let n, r;
|
||||
e.vscode || e.intellij ? e.idePluginInstalled ? (n = `Plugin for ${t} installed`, r = this.renderPluginInstalledContent()) : (n = `Plugin for ${t} not installed`, r = this.renderPluginIsNotInstalledContent(e)) : e.eclipse ? (n = e.idePluginInstalled ? "Eclipse plugin installed" : "Eclipse plugin not installed", r = e.idePluginInstalled ? this.renderPluginInstalledContent() : this.renderEclipsePluginContent()) : (n = "No IDE found", r = this.renderNoIdePluginContent());
|
||||
let a = e.idePluginInstalled ? c.checkCircle : c.warning;
|
||||
return l`
|
||||
<vaadin-details
|
||||
theme="reverse"
|
||||
.opened=${this.javaPluginSectionOpened}
|
||||
@opened-changed=${(e) => {
|
||||
i(() => {
|
||||
this.javaPluginSectionOpened = e.detail.value;
|
||||
}), this.requestLayoutUpdate();
|
||||
}}>
|
||||
<vaadin-details-summary class="px-4 py-3.5" slot="summary">
|
||||
<div class="flex gap-1.5">
|
||||
<vaadin-icon
|
||||
class="${e.idePluginInstalled ? "text-teal-11" : "text-ruby-11"}"
|
||||
.svg=${a}></vaadin-icon>
|
||||
<span>${n}</span>
|
||||
</div>
|
||||
</vaadin-details-summary>
|
||||
<div>${r}</div>
|
||||
</vaadin-details>
|
||||
`;
|
||||
}
|
||||
renderNoIdePluginContent() {
|
||||
return l`
|
||||
<div class="flex flex-col gap-2 pb-4 px-4">
|
||||
<p class="m-0 text-secondary">
|
||||
For the best development experience, use
|
||||
<a class="gap-1 inline-flex items-center" href="https://code.visualstudio.com"
|
||||
><vaadin-icon class="icon-sm" .svg=${c.vsCode}></vaadin-icon>Visual Studio Code</a
|
||||
>
|
||||
or
|
||||
<a class="gap-1 inline-flex items-center" href="https://www.jetbrains.com/idea"
|
||||
><vaadin-icon class="icon-sm" .svg=${c.intelliJ}></vaadin-icon>IntelliJ IDEA</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
renderEclipsePluginContent() {
|
||||
return l`
|
||||
<div class="flex flex-col gap-2 items-start pb-4 px-4">
|
||||
<p class="m-0 text-secondary">Install the Vaadin Eclipse Plugin to ensure a smooth development workflow</p>
|
||||
<p class="m-0 text-secondary">
|
||||
Installing the plugin is not required, but strongly recommended. Some Vaadin Copilot functionality, such as
|
||||
undo, will not function optimally without the plugin.
|
||||
</p>
|
||||
<vaadin-button
|
||||
class="mt-2"
|
||||
@click="${() => {
|
||||
window.open(f, "_blank");
|
||||
}}"
|
||||
>Install from Eclipse Marketplace
|
||||
<vaadin-icon slot="suffix" .svg="${c.arrowOutward}"></vaadin-icon>
|
||||
</vaadin-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
renderPluginInstalledContent() {
|
||||
return l`
|
||||
<p class="m-0 pb-4 px-4 text-secondary">You have a running plugin. Enjoy your awesome development workflow!</p>
|
||||
`;
|
||||
}
|
||||
renderPluginIsNotInstalledContent(e) {
|
||||
let t = null, n = "Install from Marketplace";
|
||||
return e.intellij ? (t = u, n = "Install from JetBrains Marketplace") : e.vscode ? (t = d, n = "Install from VSCode Marketplace") : e.eclipse && (t = f, n = "Install from Eclipse Marketplace"), l`
|
||||
<div class="flex flex-col gap-2 items-start pb-4 px-4">
|
||||
<p class="m-0 text-secondary">Install the Vaadin IDE Plugin to ensure a smooth development workflow</p>
|
||||
<p class="m-0 text-secondary">
|
||||
Installing the plugin is not required, but strongly recommended. Some Vaadin Copilot functionality, such as
|
||||
undo, will not function optimally without the plugin.
|
||||
</p>
|
||||
${t ? l` <vaadin-button
|
||||
class="mt-2"
|
||||
@click="${() => {
|
||||
window.open(t, "_blank");
|
||||
}}"
|
||||
>${n}
|
||||
<vaadin-icon slot="suffix" .svg="${c.arrowOutward}"></vaadin-icon>
|
||||
</vaadin-button>` : o}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
getActiveTabContent(e, t) {
|
||||
return this.hotswapTab === "jrebel" ? t.jrebel ? this.renderJRebelInstalledContent() : this.renderJRebelNotInstalledContent() : e.intellij ? this.renderIntelliJHotswapHint() : e.vscode ? this.renderVSCodeHotswapHint() : this.renderHotswapAgentNotInstalledContent(e);
|
||||
}
|
||||
renderHotswapSection(e) {
|
||||
let { jdkInfo: t } = N;
|
||||
if (!t) return o;
|
||||
let n = P(), r = F(), a, s;
|
||||
n === "success" ? (a = c.checkCircle, s = "Java Hotswap is enabled") : n === "warning" ? (a = c.warning, s = "Java Hotswap is not enabled") : n === "error" && (a = c.warning, s = "Java Hotswap is partially enabled");
|
||||
let u = this.getActiveTabContent(e, t), d = r === "jrebel" ? this.renderJRebelInstalledContent() : this.renderHotswapAgentInstalledContent(), f = this.hotswapTab === "hotswapagent" ? 0 : 1;
|
||||
return l` <vaadin-details
|
||||
theme="reverse"
|
||||
.opened=${this.hotswapSectionOpened}
|
||||
@opened-changed=${(e) => {
|
||||
i(() => {
|
||||
this.hotswapSectionOpened = e.detail.value;
|
||||
}), this.requestLayoutUpdate();
|
||||
}}>
|
||||
<vaadin-details-summary class="px-4 py-3.5" slot="summary">
|
||||
<div class="flex gap-1.5">
|
||||
<vaadin-icon
|
||||
class="${n === "success" ? "text-teal-11" : "text-ruby-11"}"
|
||||
.svg=${a}></vaadin-icon>
|
||||
<span>${s}</span>
|
||||
</div>
|
||||
</vaadin-details-summary>
|
||||
<div>
|
||||
${r === "none" ? l`
|
||||
<vaadin-tabs
|
||||
.selected=${f}
|
||||
@selected-changed=${(e) => {
|
||||
this.hotswapTab = e.detail.value === 0 ? "hotswapagent" : "jrebel";
|
||||
}}>
|
||||
<vaadin-tab>Hotswap Agent</vaadin-tab>
|
||||
<vaadin-tab>JRebel</vaadin-tab>
|
||||
</vaadin-tabs>
|
||||
${u}
|
||||
` : l`${d}`}
|
||||
</div>
|
||||
</vaadin-details>`;
|
||||
}
|
||||
renderJRebelNotInstalledContent() {
|
||||
return l`
|
||||
<div class="flex flex-col gap-2 p-4">
|
||||
<p class="m-0 text-secondary">
|
||||
<a class="inline-flex items-center" href="https://www.jrebel.com"
|
||||
>JRebel <vaadin-icon class="icon-sm" .svg=${c.arrowOutward}></vaadin-icon
|
||||
></a>
|
||||
is a commercial hotswap solution. Vaadin detects the JRebel Agent and automatically reloads the application in
|
||||
the browser after the Java changes have been hotpatched.
|
||||
</p>
|
||||
<p class="m-0 text-secondary">
|
||||
Go to
|
||||
<a
|
||||
class="inline-flex items-center"
|
||||
href="https://www.jrebel.com/products/jrebel/learn"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
https://www.jrebel.com/products/jrebel/learn
|
||||
<vaadin-icon class="icon-sm" .svg=${c.arrowOutward}></vaadin-icon
|
||||
></a>
|
||||
to get started.
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
renderHotswapAgentNotInstalledContent(e) {
|
||||
return l` <div class="p-2">${[
|
||||
this.renderJavaRunningInDebugModeSection(),
|
||||
this.renderHotswapAgentJdkSection(e),
|
||||
this.renderInstallHotswapAgentJdkSection(e),
|
||||
this.renderHotswapAgentVersionSection(),
|
||||
this.renderHotswapAgentMissingArgParam(e)
|
||||
]}</div> `;
|
||||
}
|
||||
renderIntelliJHotswapHint() {
|
||||
return l` <div class="flex flex-col gap-2 p-4">
|
||||
<h3 class="font-semibold my-0 text-sm">Use 'Debug using Hotswap Agent' launch configuration</h3>
|
||||
<p class="m-0 text-secondary">
|
||||
Vaadin IntelliJ plugin offers launch mode that does not require any manual configuration!
|
||||
</p>
|
||||
<p class="m-0 text-secondary">
|
||||
In order to run recommended launch configuration, you should click three dots right next to Debug button and
|
||||
select
|
||||
<code class="bg-gray-3 dark:bg-gray-7 font-mono inline-flex px-1.5 py-px rounded-md text-body text-xs"
|
||||
>Debug using Hotswap Agent</code
|
||||
>
|
||||
option.
|
||||
</p>
|
||||
</div>`;
|
||||
}
|
||||
renderVSCodeHotswapHint() {
|
||||
return l` <div>
|
||||
<h3 class="font-semibold my-0 text-sm">Use 'Debug (hotswap)'</h3>
|
||||
With Vaadin Visual Studio Code extension you can run Hotswap Agent without any manual configuration required!
|
||||
<p class="m-0">
|
||||
Click
|
||||
<code class="bg-gray-3 dark:bg-gray-7 font-mono inline-flex px-1.5 py-px rounded-md text-body text-xs"
|
||||
>Debug (hotswap)</code
|
||||
>
|
||||
within your main class to debug application using Hotswap Agent.
|
||||
</p>
|
||||
</div>`;
|
||||
}
|
||||
renderJavaRunningInDebugModeSection() {
|
||||
return l`
|
||||
<vaadin-details theme="reverse" .opened="${!N.jdkInfo?.runningInJavaDebugMode}">
|
||||
<vaadin-details-summary class="p-2" slot="summary">Run Java in debug mode</vaadin-details-summary>
|
||||
<p class="m-0 pb-2 px-2 text-secondary">Start the application in debug mode in the IDE.</p>
|
||||
</vaadin-details>
|
||||
`;
|
||||
}
|
||||
renderHotswapAgentMissingArgParam(e) {
|
||||
return l`
|
||||
<vaadin-details theme="reverse" .opened="${!(N.jdkInfo?.runningWitHotswap && N.jdkInfo?.runningWithExtendClassDef)}">
|
||||
<vaadin-details-summary class="p-2" slot="summary">Enable HotswapAgent</vaadin-details-summary>
|
||||
<div class="flex flex-col gap-2 pb-2 px-2 text-secondary">
|
||||
<ul class="m-0 ps-4">
|
||||
${e.intellij ? l`<li>Launch as mentioned in the previous step</li>` : o}
|
||||
${e.intellij ? l`<li>
|
||||
To manually configure IntelliJ, add the
|
||||
<code
|
||||
class="bg-gray-3 dark:bg-gray-7 break-all font-mono inline-flex px-1.5 py-px rounded-md text-body text-xs"
|
||||
>-XX:HotswapAgent=fatjar -XX:+AllowEnhancedClassRedefinition -XX:+UpdateClasses</code
|
||||
>
|
||||
JVM arguments when launching the application.
|
||||
</li>` : l`<li>
|
||||
Add the
|
||||
<code
|
||||
class="bg-gray-3 dark:bg-gray-7 break-all font-mono inline-flex px-1.5 py-px rounded-md text-body text-xs"
|
||||
>-XX:HotswapAgent=fatjar -XX:+AllowEnhancedClassRedefinition -XX:+UpdateClasses</code
|
||||
>
|
||||
JVM arguments when launching the application.
|
||||
</li>`}
|
||||
</ul>
|
||||
</div>
|
||||
</vaadin-details>
|
||||
`;
|
||||
}
|
||||
renderHotswapAgentJdkSection(e) {
|
||||
let t = N.jdkInfo?.extendedClassDefCapable, n = this.downloadStatusMessages?.[this.downloadStatusMessages.length - 1] === U, r = this.downloadProgress > 0 ? l`<vaadin-progress-bar .value="${this.downloadProgress}" min="0" max="1"></vaadin-progress-bar>` : o, i = n ? l`<h3 class="font-semibold my-0 text-sm">
|
||||
Go to VS Code and launch the 'Debug using Hotswap Agent' configuration
|
||||
</h3>` : o;
|
||||
return l`
|
||||
<vaadin-details theme="reverse" .opened="${!t}">
|
||||
<vaadin-details-summary class="p-2" slot="summary">Run using JetBrains Runtime JDK</vaadin-details-summary>
|
||||
<div class="flex flex-col gap-2 pb-2 px-2 text-secondary">
|
||||
<p class="m-0">JetBrains Runtime provides much better hotswapping compared to other JDKs.</p>
|
||||
<ul class="m-0 ps-4">
|
||||
${e.intellij && B("1.3.0", E.idePluginState?.version) ? l` <li>Upgrade to the latest IntelliJ plugin</li>` : o}
|
||||
${e.intellij ? l` <li>Launch the application in IntelliJ using "Debug using Hotswap Agent"</li>` : o}
|
||||
${e.vscode ? l` <li>
|
||||
<a href @click="${(e) => this.downloadJetbrainsRuntime(e)}"
|
||||
>Let Copilot download and set up JetBrains Runtime for VS Code</a
|
||||
>
|
||||
${r}
|
||||
<ul>
|
||||
${this.downloadStatusMessages.map((e) => l`<li>${e}</li>`)} ${i}
|
||||
</ul>
|
||||
</li>` : o}
|
||||
<li>
|
||||
${e.intellij || e.vscode ? l`If there is a problem, you can manually
|
||||
<a target="_blank" href="${H}">download JetBrains Runtime JDK</a> and set up your
|
||||
debug configuration to use it.` : l`<a target="_blank" href="${H}">Download JetBrains Runtime JDK</a> and set up
|
||||
your debug configuration to use it.`}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</vaadin-details>
|
||||
`;
|
||||
}
|
||||
renderInstallHotswapAgentJdkSection(e) {
|
||||
let t = N.jdkInfo?.hotswapAgentFound, n = N.jdkInfo?.extendedClassDefCapable;
|
||||
return l`
|
||||
<vaadin-details theme="reverse" .opened="${!t}">
|
||||
<vaadin-details-summary class="p-2" slot="summary"> Install HotswapAgent </vaadin-details-summary>
|
||||
<div class="flex flex-col gap-2 pb-2 px-2 text-secondary">
|
||||
<p class="m-0">
|
||||
Hotswap Agent provides application level support for hot reloading, such as reinitalizing Vaadin @Route or
|
||||
@BrowserCallable classes when they are updated.
|
||||
</p>
|
||||
<ul class="m-0 ps-4">
|
||||
${e.intellij ? l`<li>Launch as mentioned in the previous step</li>` : o}
|
||||
${!e.intellij && !n ? l`<li>First install JetBrains Runtime as mentioned in the step above.</li>` : o}
|
||||
${e.intellij ? l`<li>
|
||||
To manually configure IntelliJ, download HotswapAgent and install the jar file as
|
||||
<code class="bg-gray-3 dark:bg-gray-7 font-mono inline-flex px-1.5 py-px rounded-md text-body text-xs"
|
||||
>[JAVA_HOME]/lib/hotswap/hotswap-agent.jar</code
|
||||
>
|
||||
in the JetBrains Runtime JDK. Note that the file must be renamed to exactly match this path.
|
||||
</li>` : l`<li>
|
||||
Download HotswapAgent and install the jar file as
|
||||
<code class="bg-gray-3 dark:bg-gray-7 font-mono inline-flex px-1.5 py-px rounded-md text-body text-xs"
|
||||
>[JAVA_HOME]/lib/hotswap/hotswap-agent.jar</code
|
||||
>
|
||||
in the JetBrains Runtime JDK. Note that the file must be renamed to exactly match this path.
|
||||
</li>`}
|
||||
</ul>
|
||||
</div>
|
||||
</vaadin-details>
|
||||
`;
|
||||
}
|
||||
renderHotswapAgentVersionSection() {
|
||||
if (!N.jdkInfo?.hotswapAgentFound) return o;
|
||||
let e = N.jdkInfo?.hotswapVersionOk, t = N.jdkInfo?.hotswapVersion, n = N.jdkInfo?.hotswapAgentLocation;
|
||||
return l`
|
||||
<vaadin-details theme="reverse" .opened="${!e}">
|
||||
<vaadin-details-summary class="p-2" slot="summary">Hotswap version requires update</vaadin-details-summary>
|
||||
<div>
|
||||
HotswapAgent version ${t} is in use
|
||||
<a target="_blank" href="https://github.com/HotswapProjects/HotswapAgent/releases"
|
||||
>Download the latest HotswapAgent</a
|
||||
>
|
||||
and place it in
|
||||
<code class="bg-gray-3 dark:bg-gray-7 font-mono inline-flex px-1.5 py-px rounded-md text-body text-xs"
|
||||
>${n}</code
|
||||
>
|
||||
</div>
|
||||
</vaadin-details>
|
||||
`;
|
||||
}
|
||||
renderJRebelInstalledContent() {
|
||||
return l` <p class="m-0 pb-2 px-2">JRebel is in use. Enjoy your awesome development workflow!</p> `;
|
||||
}
|
||||
renderHotswapAgentInstalledContent() {
|
||||
return l`
|
||||
<p class="m-0 pb-4 px-4 text-secondary">Hotswap agent is in use. Enjoy your awesome development workflow!</p>
|
||||
`;
|
||||
}
|
||||
async downloadJetbrainsRuntime(e) {
|
||||
return e.target.disabled = !0, e.preventDefault(), this.downloadStatusMessages = [], n(`${m}set-up-vs-code-hotswap`, {}, (e) => {
|
||||
e.data.error ? (A("Error downloading JetBrains runtime", e.data.error), this.downloadStatusMessages = [...this.downloadStatusMessages, "Download failed"]) : this.downloadStatusMessages = [...this.downloadStatusMessages, U];
|
||||
});
|
||||
}
|
||||
downloadStatusUpdate(e) {
|
||||
let t = e.detail.progress;
|
||||
t ? this.downloadProgress = t : this.downloadStatusMessages = [...this.downloadStatusMessages, e.detail.message];
|
||||
}
|
||||
}, V.NAME = "copilot-development-setup-user-guide", V), h([v()], W.prototype, "javaPluginSectionOpened", void 0), h([v()], W.prototype, "hotswapSectionOpened", void 0), h([v()], W.prototype, "hotswapTab", void 0), h([v()], W.prototype, "downloadStatusMessages", void 0), h([v()], W.prototype, "downloadProgress", void 0), W = h([g(W.NAME)], W), G = class extends x {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback(), this.classList.add("contents");
|
||||
}
|
||||
render() {
|
||||
return l`<vaadin-button
|
||||
id="close"
|
||||
@click="${() => y.closePanel(K.tag)}"
|
||||
>Close
|
||||
</vaadin-button>`;
|
||||
}
|
||||
}, G = h([g("copilot-development-setup-footer-actions")], G), K = {
|
||||
header: "Development Workflow",
|
||||
tag: L,
|
||||
footerActionsTag: "copilot-development-setup-footer-actions",
|
||||
individual: !0
|
||||
}, globalThis.Vaadin.copilot.plugins.push({ init(e) {
|
||||
e.addPanel(K);
|
||||
} }), y.addPanel(K);
|
||||
}))();
|
||||
export { G as CopilotDevelopmentSetupFooterActions, W as CopilotDevelopmentSetupUserGuide, K as copilotDevelopmentSetupPanelConfig };
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { _ as t, g as n } from "./icons-CwakCZgK.js";
|
||||
import { l as r, o as i } from "./consts-CSALuSsm.js";
|
||||
import { n as a, t as o } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { n as s, r as c } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { r as l, t as u } from "./stats-CRkPKCLQ.js";
|
||||
import { n as d, t as f } from "./early-project-state-LGwavSyI.js";
|
||||
//#region frontend/copilot/shared/copilot-development-setup-user-guide-utils.ts
|
||||
function p() {
|
||||
l("use-dev-workflow-guide"), a.openPanel(y);
|
||||
}
|
||||
function m() {
|
||||
let e = f.jdkInfo;
|
||||
return e ? e.jrebel ? "success" : e.hotswapAgentFound ? !e.hotswapVersionOk || !e.runningWithExtendClassDef || !e.runningWitHotswap || !e.runningInJavaDebugMode ? "error" : "success" : "warning" : null;
|
||||
}
|
||||
function h() {
|
||||
let e = f.jdkInfo;
|
||||
return !e || m() !== "success" ? "none" : e.jrebel ? "jrebel" : e.runningWitHotswap ? "hotswap" : "none";
|
||||
}
|
||||
function g() {
|
||||
return s.idePluginState !== void 0 && !s.idePluginState.active ? "warning" : "success";
|
||||
}
|
||||
function _() {
|
||||
if (!f.jdkInfo) return { status: "success" };
|
||||
let e = m(), t = g();
|
||||
return e === "warning" ? t === "warning" ? {
|
||||
status: "warning",
|
||||
message: "IDE Plugin, Hotswap"
|
||||
} : {
|
||||
status: "warning",
|
||||
message: "Hotswap is not enabled"
|
||||
} : t === "warning" ? {
|
||||
status: "warning",
|
||||
message: "IDE Plugin is not active"
|
||||
} : e === "error" ? {
|
||||
status: "error",
|
||||
message: "Hotswap is partially enabled"
|
||||
} : { status: "success" };
|
||||
}
|
||||
function v() {
|
||||
t(`${i}get-dev-setup-info`, {}), window.Vaadin.copilot.eventbus.on("copilot-get-dev-setup-info-response", (e) => {
|
||||
if (e.detail.content) {
|
||||
let t = JSON.parse(e.detail.content);
|
||||
s.setIdePluginState(t.ideInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
var y, b = e((() => {
|
||||
c(), n(), r(), o(), u(), d(), y = "copilot-development-setup-user-guide";
|
||||
}));
|
||||
//#endregion
|
||||
export { g as a, p as c, m as i, v as n, h as o, _ as r, b as s, y as t };
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { $ as t, C as n, Q as r, et as i, n as a, o, r as s, t as c, u as l, y as u } from "./icons-CwakCZgK.js";
|
||||
import { a as d, d as f, i as p, l as m, n as h, o as g, r as _, s as v, t as y } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { n as b, r as x } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { r as S, t as C } from "./stats-CRkPKCLQ.js";
|
||||
import { n as w, t as T } from "./copilot-stored-machine-state-D6qB_Peh.js";
|
||||
import { n as E, r as D } from "./copilot-notification-CCNJdNg4.js";
|
||||
import { n as O, t as k } from "./early-project-state-LGwavSyI.js";
|
||||
import { a as A, i as j, s as M, t as N } from "./copilot-development-setup-user-guide-utils-DzEVQbWO.js";
|
||||
//#region frontend/copilot/copilot-devtools/copilot-devtools.ts
|
||||
var P, F, I, L, R;
|
||||
//#endregion
|
||||
e((() => {
|
||||
v(), s(), p(), x(), a(), M(), i(), y(), C(), O(), T(), E(), n(), g(), P = "bg-[linear-gradient(to_right,var(--amber-3),var(--amber-5),var(--amber-3),var(--amber-6))] dark:bg-[linear-gradient(to_right,var(--amber-5),var(--amber-7),var(--amber-5),var(--amber-8))]", F = "bg-[linear-gradient(to_right,var(--blue-3),var(--blue-5),var(--blue-3),var(--blue-6))] dark:bg-[linear-gradient(to_right,var(--blue-4),var(--blue-6),var(--blue-4),var(--blue-7))]", I = "bg-[linear-gradient(to_right,var(--ruby-3),var(--ruby-5),var(--ruby-3),var(--ruby-6))] dark:bg-[linear-gradient(to_right,var(--ruby-4),var(--ruby-6),var(--ruby-4),var(--ruby-7))]", L = "bg-[linear-gradient(to_right,var(--teal-3),var(--teal-5),var(--teal-3),var(--teal-6))] dark:bg-[linear-gradient(to_right,var(--teal-4),var(--teal-6),var(--teal-4),var(--teal-7))]", R = class extends _ {
|
||||
constructor(...e) {
|
||||
super(...e), this._helpExpanded = !1;
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback(), this.classList.add("flex", "flex-col");
|
||||
}
|
||||
render() {
|
||||
return l`
|
||||
<header class="flex items-center pe-2 ps-4 py-2">
|
||||
<h2 class="font-bold gap-1 me-auto my-0 text-xs uppercase">Vaadin Copilot</h2>
|
||||
<vaadin-button
|
||||
aria-label="Close"
|
||||
theme="icon tertiary"
|
||||
@click=${() => {
|
||||
this.closePopover();
|
||||
}}>
|
||||
<vaadin-icon .svg="${c.close}"></vaadin-icon>
|
||||
<vaadin-tooltip slot="tooltip" text="Close"></vaadin-tooltip>
|
||||
</vaadin-button>
|
||||
</header>
|
||||
<div class="flex flex-col gap-4 pb-4 px-4">
|
||||
${this.renderCopilotServerWarning()} ${this.renderUserButton()} ${this.renderDevelopmentWorkflow()}
|
||||
${this.renderWelcomeToVersion()}
|
||||
<div class="bg-gray-3 dark:bg-gray-6 flex flex-col rounded-md">
|
||||
<vaadin-button
|
||||
@click="${this.handleAppInfoClick}"
|
||||
class="border-0 h-auto justify-start py-2"
|
||||
theme="tertiary">
|
||||
<vaadin-icon slot="prefix" .svg="${c.info}"></vaadin-icon>
|
||||
App Info
|
||||
</vaadin-button>
|
||||
<vaadin-button @click="${this.handleAppLogClick}" class="border-0 h-auto justify-start py-2" theme="tertiary">
|
||||
<vaadin-icon slot="prefix" .svg="${c.terminal}"></vaadin-icon>
|
||||
App Log
|
||||
</vaadin-button>
|
||||
<vaadin-button
|
||||
@click="${this.handleFeaturesClick}"
|
||||
class="border-0 h-auto justify-start py-2"
|
||||
theme="tertiary">
|
||||
<vaadin-icon slot="prefix" .svg="${c.listAlt}"></vaadin-icon>
|
||||
Features
|
||||
</vaadin-button>
|
||||
${k.springSecurityEnabled ? l`
|
||||
<vaadin-button
|
||||
@click="${this.handleImpersonateAppUserClick}"
|
||||
class="border-0 h-auto justify-start py-2"
|
||||
theme="tertiary">
|
||||
<vaadin-icon slot="prefix" .svg="${c.accountCircle}"></vaadin-icon>
|
||||
Impersonate App User
|
||||
</vaadin-button>
|
||||
` : o}
|
||||
</div>
|
||||
<div class="bg-gray-3 dark:bg-gray-6 flex flex-col rounded-md">
|
||||
<vaadin-button
|
||||
@click="${this.handleFeedbackClick}"
|
||||
class="border-0 h-auto justify-start py-2"
|
||||
theme="tertiary">
|
||||
<vaadin-icon slot="prefix" .svg="${c.feedback}"></vaadin-icon>
|
||||
Feedback
|
||||
</vaadin-button>
|
||||
<vaadin-button
|
||||
@click="${this.toggleHelpAndSupport}"
|
||||
class="border-0 h-auto justify-start py-2"
|
||||
theme="tertiary">
|
||||
<vaadin-icon slot="prefix" .svg="${c.help}"></vaadin-icon>
|
||||
Help & Support
|
||||
<vaadin-icon
|
||||
slot="suffix"
|
||||
.svg="${this._helpExpanded ? c.keyboardArrowUp : c.keyboardArrowDown}"></vaadin-icon>
|
||||
</vaadin-button>
|
||||
${this._helpExpanded ? this.renderHelpLinks() : o}
|
||||
<vaadin-button
|
||||
@click="${this.handleSettingsClick}"
|
||||
class="border-0 h-auto justify-start py-2"
|
||||
theme="tertiary">
|
||||
<vaadin-icon slot="prefix" .svg="${c.settings}"></vaadin-icon>
|
||||
Settings
|
||||
</vaadin-button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
renderUserButton() {
|
||||
let e = b.userInfo?.validLicense, t = e ? P : F, n = e ? "text-amber-12 dark:text-amber-11" : "text-blue-12 dark:text-blue-11", r = this.getUserName() !== "Log in";
|
||||
return l`
|
||||
<vaadin-button
|
||||
@click=${this.handleUserLoginClick}
|
||||
class="animate-gradient ${t} border-0 h-auto justify-start py-2 text-start ${r ? "gap-3 px-3" : "items-start"}">
|
||||
${r ? this.renderUserImage() : l`<vaadin-icon
|
||||
class="text-blue-12 dark:text-blue-11"
|
||||
slot="prefix"
|
||||
.svg="${c.login}"></vaadin-icon>`}
|
||||
<span class="flex flex-col">
|
||||
<span>${this.getUserName()}</span>
|
||||
<span class="${n} text-xs">${this.getLicenseType()}</span>
|
||||
</span>
|
||||
</vaadin-button>
|
||||
`;
|
||||
}
|
||||
renderCopilotServerWarning() {
|
||||
return b.userInfo?.copilotServerReached === !1 ? l`
|
||||
<vaadin-button
|
||||
@click=${this.showCopilotServerTroubleshooting}
|
||||
class="animate-gradient ${I} border-0 h-auto items-start justify-start py-2 text-start"
|
||||
data-test-id="copilot-server-unreachable">
|
||||
<vaadin-tooltip slot="tooltip" text="Click here for troubleshooting"></vaadin-tooltip>
|
||||
<vaadin-icon class="text-ruby-12 dark:text-ruby-11" slot="prefix" .svg="${c.warning}"></vaadin-icon>
|
||||
<span class="flex flex-col">
|
||||
<span>Copilot server is unreachable</span>
|
||||
<span class="text-ruby-12 dark:text-ruby-11 text-xs">Check your proxy or firewall settings.</span>
|
||||
</span>
|
||||
</vaadin-button>
|
||||
` : o;
|
||||
}
|
||||
showCopilotServerTroubleshooting() {
|
||||
D({
|
||||
type: t.WARNING,
|
||||
message: "Copilot server is unreachable",
|
||||
details: u(l`
|
||||
<p class="m-0">Copilot could not connect to the Copilot server.</p>
|
||||
<p class="mb-0 mt-2">To troubleshoot:</p>
|
||||
<ol class="mb-0 mt-1 ps-4">
|
||||
<li>Verify that this machine can reach the Copilot server and complete its TLS handshake:</li>
|
||||
<li class="list-none mt-1">
|
||||
<code
|
||||
class="bg-gray-3 dark:bg-gray-6 box-border inline-block pe-8 ps-3 py-1.75 relative rounded-md text-xs w-full"
|
||||
><copilot-copy></copilot-copy>curl -Iv https://copilot.vaadin.com</code
|
||||
>
|
||||
</li>
|
||||
<li>Check that your firewall or network policy allows access to <code>copilot.vaadin.com</code>.</li>
|
||||
<li>If you use a proxy, verify its settings and that it trusts the server's SSL certificate.</li>
|
||||
</ol>
|
||||
`),
|
||||
delay: 3e4
|
||||
});
|
||||
}
|
||||
renderWelcomeToVersion() {
|
||||
let e = b.projectVersionReleaseNoteInfo;
|
||||
return e === null || w.getMostRecentReleaseNoteDismissed() || !e.mostRecentVersion || !e.url ? o : l`
|
||||
<div class="flex relative">
|
||||
<vaadin-button
|
||||
id="release-note-btn"
|
||||
data-test-id="release-note-btn"
|
||||
class="border-0 h-auto items-start justify-start px-3 py-2 text-start w-full"
|
||||
@click="${(t) => {
|
||||
window.open(e.url, "_blank");
|
||||
}}">
|
||||
<vaadin-icon class="text-blue-11" slot="prefix" .svg="${c.info}"></vaadin-icon>
|
||||
<span class="flex flex-col">
|
||||
<span>Welcome to Vaadin ${e.vaadinVersion}</span>
|
||||
<span class="text-blue-11 text-xs">Click for release notes</span>
|
||||
</span>
|
||||
</vaadin-button>
|
||||
<vaadin-button
|
||||
class="absolute end-0 top-0"
|
||||
id="dismiss-release-note-item"
|
||||
theme="icon tertiary"
|
||||
@click="${(e) => {
|
||||
e.stopPropagation(), w.setMostRecentReleaseNoteDismissed(!0);
|
||||
}}"
|
||||
><vaadin-icon .svg="${c.close}"></vaadin-icon
|
||||
<vaadin-tooltip slot="tooltip" text="Dismiss"></vaadin-tooltip>
|
||||
</vaadin-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
renderUserImage() {
|
||||
return b.userInfo?.portraitUrl ? l`<img
|
||||
alt="${this.getUserName()}"
|
||||
class="rounded-full size-8 object-cover"
|
||||
slot="prefix"
|
||||
src="https://vaadin.com${b.userInfo.portraitUrl}" />` : o;
|
||||
}
|
||||
renderDevelopmentWorkflow() {
|
||||
let e = j(), t = A(), n = this.getDevelopmentWorkflowConfig(e, t), r = n?.bgClass ?? "", i = n?.colorClass ?? "", a = this.resolveIcon(n), o = n?.rotateIcon ? `rotate-180 ${i}` : i, s = this.resolveTitle(n), c = n?.displayMessage ?? "";
|
||||
return l`
|
||||
<vaadin-button
|
||||
data-test-id="development-workflow-btn"
|
||||
@click="${this.handleDevelopmentWorkflowClick}"
|
||||
class="animation-delay-4000 animate-gradient ${r} border-0 h-auto items-start justify-start py-2 text-start">
|
||||
<vaadin-icon class="${o}" slot="prefix" .svg="${a}"></vaadin-icon>
|
||||
<span class="flex flex-col">
|
||||
<span>${s}</span>
|
||||
<span class="text-xs ${i}">${c}</span>
|
||||
</span>
|
||||
</vaadin-button>
|
||||
`;
|
||||
}
|
||||
getDevelopmentWorkflowConfig(e, t) {
|
||||
let n = {
|
||||
bgClass: L,
|
||||
colorClass: "text-teal-11"
|
||||
};
|
||||
if (e === "warning" && t === "warning") return {
|
||||
...n,
|
||||
icon: c.wbIncandescent,
|
||||
rotateIcon: !0,
|
||||
title: "IDE plugin & Hotswap recommended",
|
||||
combinedTitle: !0,
|
||||
displayMessage: "Enable both for optimal development workflow"
|
||||
};
|
||||
if (e === "warning") return {
|
||||
...n,
|
||||
icon: c.wbIncandescent,
|
||||
rotateIcon: !0,
|
||||
title: "Hotswap recommended",
|
||||
displayMessage: "Applies changes without restarting"
|
||||
};
|
||||
if (t === "warning") return {
|
||||
...n,
|
||||
icon: c.code,
|
||||
getIcon: !0,
|
||||
title: "IDE plugin recommended",
|
||||
getTitle: !0,
|
||||
displayMessage: "Simplifies Hotswap setup & config"
|
||||
};
|
||||
if (e === "error") return {
|
||||
bgClass: I,
|
||||
colorClass: "text-ruby-11",
|
||||
icon: c.error,
|
||||
title: "Hotswap partially enabled",
|
||||
displayMessage: "View details"
|
||||
};
|
||||
}
|
||||
resolveIcon(e) {
|
||||
return e ? e.getIcon ? this.getIdeIcon() : e.icon : c.bolt;
|
||||
}
|
||||
resolveTitle(e) {
|
||||
return e ? e.combinedTitle ? this.getCombinedTitle() : e.getTitle ? this.getIdePluginName() : e.title : "Development Workflow";
|
||||
}
|
||||
getUserName() {
|
||||
return [b.userInfo?.firstName, b.userInfo?.lastName].filter(Boolean).join(" ") || "Log in";
|
||||
}
|
||||
getLicenseType() {
|
||||
return b.userInfo?.validLicense ? "" : "Unlock all Copilot features, including AI";
|
||||
}
|
||||
getIdeIcon() {
|
||||
switch (b.idePluginState?.ide) {
|
||||
case "intellij": return c.intelliJ;
|
||||
case "vscode": return c.vsCode;
|
||||
case "eclipse": return c.eclipse;
|
||||
default: return c.code;
|
||||
}
|
||||
}
|
||||
getIdePluginName() {
|
||||
switch (b.idePluginState?.ide) {
|
||||
case "intellij": return "Vaadin plugin for IntelliJ";
|
||||
case "vscode": return "Vaadin extension for VS Code";
|
||||
case "eclipse": return "Vaadin plugin for Eclipse";
|
||||
default: return "IDE plugin";
|
||||
}
|
||||
}
|
||||
getCombinedTitle() {
|
||||
switch (b.idePluginState?.ide) {
|
||||
case "intellij": return "IntelliJ plugin & Hotswap recommended";
|
||||
case "vscode": return "VS Code extension & Hotswap recommended";
|
||||
case "eclipse": return "Eclipse plugin & Hotswap recommended";
|
||||
default: return "IDE plugin & Hotswap recommended";
|
||||
}
|
||||
}
|
||||
closePopover() {
|
||||
let e = this.closest("vaadin-popover");
|
||||
e && (e.opened = !1);
|
||||
}
|
||||
handleUserLoginClick() {
|
||||
if (b.userInfo?.validLicense) {
|
||||
window.open("https://vaadin.com/myaccount", "_blank", "noopener");
|
||||
return;
|
||||
}
|
||||
b.setLoginCheckActive(!0);
|
||||
}
|
||||
handleDevelopmentWorkflowClick() {
|
||||
S("use-dev-workflow-guide"), h.openPanel(N), this.closePopover();
|
||||
}
|
||||
handleAppInfoClick() {
|
||||
h.openPanel(r.INFO), this.closePopover();
|
||||
}
|
||||
handleAppLogClick() {
|
||||
h.openPanel(r.LOG), this.closePopover();
|
||||
}
|
||||
handleFeaturesClick() {
|
||||
h.openPanel(r.FEATURES), this.closePopover();
|
||||
}
|
||||
handleImpersonateAppUserClick() {
|
||||
h.openPanel(r.IMPERSONATOR), this.closePopover();
|
||||
}
|
||||
handleSettingsClick() {
|
||||
h.openPanel(r.SETTINGS), this.closePopover();
|
||||
}
|
||||
handleFeedbackClick() {
|
||||
h.openPanel(r.FEEDBACK), this.closePopover();
|
||||
}
|
||||
toggleHelpAndSupport() {
|
||||
this._helpExpanded = !this._helpExpanded;
|
||||
}
|
||||
renderHelpLinks() {
|
||||
return l`
|
||||
<div class="flex flex-col ps-4">
|
||||
${[
|
||||
{
|
||||
label: "Forum",
|
||||
icon: "forum",
|
||||
url: "https://vaadin.com/forum"
|
||||
},
|
||||
{
|
||||
label: "Docs",
|
||||
icon: "article",
|
||||
url: "https://vaadin.com/docs/latest/tools/copilot"
|
||||
},
|
||||
{
|
||||
label: "GitHub Issues",
|
||||
icon: "github",
|
||||
url: "https://github.com/vaadin/copilot/issues"
|
||||
}
|
||||
].map(({ label: e, icon: t, url: n }) => l`
|
||||
<vaadin-button
|
||||
@click="${() => window.open(n, "_blank", "noopener")}"
|
||||
class="border-0 h-auto justify-start py-2"
|
||||
theme="tertiary">
|
||||
<vaadin-icon slot="prefix" .svg="${c[t]}"></vaadin-icon>
|
||||
${e}
|
||||
</vaadin-button>
|
||||
`)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}, d([m()], R.prototype, "_helpExpanded", void 0), R = d([f("copilot-devtools")], R);
|
||||
}))();
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { $ as t, C as n, L as r, R as i, T as a, at as o, et as s, l as c, lt as l, n as u, o as d, r as f, s as ee, t as p, u as m, y as h } from "./icons-CwakCZgK.js";
|
||||
import { l as te, o as g } from "./consts-CSALuSsm.js";
|
||||
import { a as _, i as v, n as y, r as b } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { t as ne } from "./stats-CRkPKCLQ.js";
|
||||
import { i as x, n as S, r as C, t as w } from "./directive-DWLihZIi.js";
|
||||
import { n as T, t as E } from "./copilot-stored-machine-state-D6qB_Peh.js";
|
||||
import { n as D } from "./copilot-notification-CCNJdNg4.js";
|
||||
import { n as O } from "./early-project-state-LGwavSyI.js";
|
||||
//#region node_modules/lit-html/directives/unsafe-html.js
|
||||
var k, A, j = e((() => {
|
||||
c(), C(), k = class extends S {
|
||||
constructor(e) {
|
||||
if (super(e), this.it = d, e.type !== x.CHILD) throw Error(this.constructor.directiveName + "() can only be used in child bindings");
|
||||
}
|
||||
render(e) {
|
||||
if (e === d || e == null) return this._t = void 0, this.it = e;
|
||||
if (e === ee) return e;
|
||||
if (typeof e != "string") throw Error(this.constructor.directiveName + "() called with a non-string value");
|
||||
if (e === this.it) return this._t;
|
||||
this.it = e;
|
||||
let t = [e];
|
||||
return t.raw = t, this._t = {
|
||||
_$litType$: this.constructor.resultType,
|
||||
strings: t,
|
||||
values: []
|
||||
};
|
||||
}
|
||||
}, k.directiveName = "unsafeHTML", k.resultType = 1, A = w(k);
|
||||
})), M = e((() => {
|
||||
j();
|
||||
}));
|
||||
//#endregion
|
||||
//#region frontend/copilot/shared/copilot-userinfo-util.ts
|
||||
function re() {
|
||||
let e = y.userInfo;
|
||||
return !e || e.copilotProjectCannotLeaveLocalhost ? !1 : T.isSendErrorReportsAllowed();
|
||||
}
|
||||
var ie = e((() => {
|
||||
ne(), b(), $(), E(), n(), D(), s();
|
||||
}));
|
||||
//#endregion
|
||||
//#region frontend/copilot/shared/hotswap-utils.ts
|
||||
function N() {
|
||||
return y.idePluginState?.supportedActions?.find((e) => e === "restartApplication");
|
||||
}
|
||||
function P() {
|
||||
i(`${g}plugin-restart-application`, {}, () => {}).catch((e) => {
|
||||
z("Error restarting server", e);
|
||||
});
|
||||
}
|
||||
var F = e((() => {
|
||||
r(), te(), $(), _(), D(), s(), b(), O();
|
||||
}));
|
||||
//#endregion
|
||||
//#region frontend/copilot/shared/copilot-error-handler.ts
|
||||
function I(e) {
|
||||
if (e === void 0) return !1;
|
||||
let t = Object.keys(e);
|
||||
return t.length === 1 && t.includes("message") || t.length >= 3 && t.includes("message") && t.includes("exceptionMessage") && t.includes("exceptionStacktrace");
|
||||
}
|
||||
function L() {
|
||||
let e = "A server restart is required";
|
||||
return N() ? h(m`${e}${R()}`) : h(m`${e}`);
|
||||
}
|
||||
function R() {
|
||||
return N() ? m`<vaadin-button
|
||||
class="mt-2"
|
||||
theme="primary"
|
||||
@click=${(e) => {
|
||||
let t = e.target;
|
||||
t.disabled = !0, t.innerText = "Restarting...", P();
|
||||
}}>
|
||||
Restart Now
|
||||
</vaadin-button>` : d;
|
||||
}
|
||||
function z(e, n) {
|
||||
let r = I(n) ? n.exceptionMessage ?? n.message : n, i = {
|
||||
type: t.ERROR,
|
||||
message: "Copilot internal error",
|
||||
details: e + (r ? `\n${r}` : "")
|
||||
};
|
||||
I(n) && n.suggestRestart && N() && (i.details = h(m`${e}<br />${r} ${R()}`), i.delay = 3e4), a(i);
|
||||
let o;
|
||||
o = n instanceof Error ? n.stack : I(n) ? n?.exceptionStacktrace?.join("\n") : n?.toString(), v.emit("system-info-with-callback", {
|
||||
callback: (t) => v.send("copilot-error", {
|
||||
message: `Copilot internal error: ${e}`,
|
||||
details: o,
|
||||
versions: t
|
||||
}),
|
||||
notify: !1
|
||||
});
|
||||
}
|
||||
function B(e) {
|
||||
return e?.stack?.includes("cdn.vaadin.com/copilot") || e?.stack?.includes("/copilot/copilot/") || e?.stack?.includes("/copilot/copilot-private/");
|
||||
}
|
||||
function V() {
|
||||
let e = window.onerror;
|
||||
window.onerror = (t, n, r, i, a) => {
|
||||
if (B(a)) {
|
||||
z(t.toString(), a);
|
||||
return;
|
||||
}
|
||||
e && e(t, n, r, i, a);
|
||||
}, l((e) => {
|
||||
B(e) && z("", e);
|
||||
});
|
||||
let t = window.Vaadin.ConsoleErrors;
|
||||
if (Array.isArray(t)) for (let e of t) Array.isArray(e) ? Q.push(...e) : Q.push(e);
|
||||
U((e) => Q.push(e));
|
||||
}
|
||||
function H(e, t, n, r, i, a) {
|
||||
let o = { ...e }, s = window.Vaadin.copilot.tree, c = window.Vaadin.copilot.customComponentHandler;
|
||||
o.nodes.forEach((e) => {
|
||||
e.node = s.allNodesFlat.find((t) => {
|
||||
if (!t.isFlowComponent) return !1;
|
||||
let n = t.node;
|
||||
return n.uiId === e.uiId && n.nodeId === e.nodeId;
|
||||
});
|
||||
});
|
||||
let l = [];
|
||||
n && l.push(`Error Message -> ${n}`), r && l.push(`Error Details -> ${r}`), l.push(`Active Level -> ${c.getActiveDrillDownContext() ? c.getActiveDrillDownContext()?.nameAndIdentifier : "No active level"}`), o.nodes.length > 0 && (l.push("\nRelevant Nodes:"), o.nodes.forEach((e) => {
|
||||
l.push(`${e.relevance} -> ${e.node?.nameAndIdentifier ?? "Node not found"}`);
|
||||
})), o.relevantPairs.length > 0 && (l.push("\nAdditional Info:"), o.relevantPairs.forEach((e) => {
|
||||
l.push(`${e.relevance} -> ${e.value}`);
|
||||
})), a && (l.push("Versions"), l.push(a));
|
||||
let u = {
|
||||
name: "Info",
|
||||
content: l.join("\n")
|
||||
};
|
||||
o.items.unshift(u), i && o.items.push({
|
||||
name: "Stacktrace",
|
||||
content: i
|
||||
}), v.emit("system-info-with-callback", {
|
||||
callback: (e) => {
|
||||
o.items.push({
|
||||
name: "Versions",
|
||||
content: e
|
||||
}), t(o);
|
||||
},
|
||||
notify: !1
|
||||
});
|
||||
}
|
||||
function U(e) {
|
||||
let n = window.Vaadin.ConsoleErrors;
|
||||
window.Vaadin.ConsoleErrors = { push: (r) => {
|
||||
r[0] === null || r[0] === void 0 || (r[0].type !== void 0 && r[0].message !== void 0 ? e({
|
||||
type: r[0].type,
|
||||
message: r[0].message,
|
||||
internal: !!r[0].internal,
|
||||
details: r[0].details,
|
||||
link: r[0].link
|
||||
}) : e({
|
||||
type: t.ERROR,
|
||||
message: r.map((e) => W(e)).join(" "),
|
||||
internal: !1
|
||||
}), n.push(r));
|
||||
} };
|
||||
}
|
||||
function W(e) {
|
||||
return e.message ? e.message.toString() : e.toString();
|
||||
}
|
||||
var G, K, q, J, Y, X, Z, Q, $ = e((() => {
|
||||
f(), M(), o(), _(), b(), ie(), F(), n(), s(), u(), G = (e, t) => e.error ? (Z(e.error, t), !0) : !1, K = (e, n, r) => {
|
||||
a({
|
||||
type: t.ERROR,
|
||||
message: e,
|
||||
details: h(m`${q(n)} ${Y(r)}`),
|
||||
delay: 3e4
|
||||
});
|
||||
}, q = (e) => e.length === 0 ? d : e.length < 80 ? J(e) : m`<vaadin-details class="flex flex-col peer w-full" theme="no-padding reverse">
|
||||
<vaadin-details-summary class="font-medium -ms-3 self-start text-secondary text-xs" slot="summary"
|
||||
>Details</vaadin-details-summary
|
||||
>
|
||||
${J(e)}
|
||||
</vaadin-details>`, J = (e) => m`<code class="codeblock"
|
||||
>${A(e)}<copilot-copy class="absolute end-0 flex top-0"></copilot-copy
|
||||
></code>`, Y = (e) => e ? m`
|
||||
<vaadin-button
|
||||
class="peer-has-[[opened]]:mt-2"
|
||||
@click="${() => {
|
||||
e && v.emit("submit-exception-report-clicked", e);
|
||||
}}"
|
||||
id="report-issue">
|
||||
<vaadin-icon slot="prefix" .svg="${p.bugReport}"></vaadin-icon>
|
||||
Report Issue</vaadin-button
|
||||
>
|
||||
` : d, X = (e, t, n, r, i) => {
|
||||
let a = y.newVaadinVersionState?.versions?.length === 0;
|
||||
i && a ? H(i, (n) => {
|
||||
K(e, t, n);
|
||||
}, e, t, n) : K(e, t), re() && (r?.templateData && typeof r.templateData == "string" && r.templateData.startsWith("data") && (r.templateData = "<IMAGE_DATA>"), v.emit("system-info-with-callback", {
|
||||
callback: (t) => v.send("copilot-error", {
|
||||
message: e,
|
||||
details: String(n).replace(" ", "\n") + (r ? `\n \nRequest: \n${JSON.stringify(r)}\n` : ""),
|
||||
versions: t
|
||||
}),
|
||||
notify: !1
|
||||
})), y.clearOperationWaitsHmrUpdate();
|
||||
}, Z = (e, t) => {
|
||||
X(e.message, e.exceptionMessage ?? "", e.exceptionStacktrace?.join("\n") ?? "", t, e.exceptionReport);
|
||||
}, Q = [];
|
||||
}));
|
||||
//#endregion
|
||||
export { G as a, F as c, M as d, A as f, z as i, P as l, Q as n, $ as o, L as r, V as s, U as t, N as u };
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { $ as t, et as n, n as r, o as i, r as a, t as o, u as s } from "./icons-CwakCZgK.js";
|
||||
import { a as c, d as l, i as u, l as d, o as f, r as p, s as m } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { n as h, r as g } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { r as _, t as v } from "./stats-CRkPKCLQ.js";
|
||||
import { c as y, l as b, o as x, r as S, u as C } from "./copilot-error-handler-9OpssAH1.js";
|
||||
import { n as w, t as T } from "./copilot-stored-machine-state-D6qB_Peh.js";
|
||||
import { n as E, r as D } from "./copilot-notification-CCNJdNg4.js";
|
||||
import { n as O, t as k } from "./base-panel-Fr0D1ZcU.js";
|
||||
//#region frontend/copilot/shared/copilot-experimental-features.ts
|
||||
var A, j, M, N, P, F, I, L, R = e((() => {
|
||||
T(), g(), A = (e) => h.userInfo?.copilotExperimentalFeatureFlag === !0 && w.isExperimentalFeatureEnabled(e), j = {
|
||||
id: "theme-from-image",
|
||||
name: "Theme from Image",
|
||||
description: "Generate a custom theme based on an image you provide.",
|
||||
enabled: () => A(j),
|
||||
available: () => h.appTheme === "lumo",
|
||||
requiresReload: !1
|
||||
}, M = {
|
||||
id: "ai-docs-assistant",
|
||||
name: "AI Docs Assistant",
|
||||
description: "AI-powered Vaadin documentation assistant.",
|
||||
enabled: () => A(M),
|
||||
available: () => !0,
|
||||
requiresReload: !1
|
||||
}, N = {
|
||||
id: "testbench-test-recorder",
|
||||
name: "TestBench Test Recorder",
|
||||
description: "Record user interactions to generate end-to-end Vaadin TestBench tests automatically.",
|
||||
enabled: () => A(N),
|
||||
available: () => !0,
|
||||
requiresReload: !0
|
||||
}, P = {
|
||||
id: "i18n",
|
||||
name: "Internationalization",
|
||||
description: "Edit and manage translations for your application.",
|
||||
enabled: () => A(P),
|
||||
available: () => !0,
|
||||
requiresReload: !0
|
||||
}, F = {
|
||||
id: "annotations",
|
||||
name: "Annotations",
|
||||
description: "Add and manage comments and annotations on your application views.",
|
||||
enabled: () => A(F),
|
||||
available: () => !0,
|
||||
requiresReload: !0
|
||||
}, I = {
|
||||
id: "ui-test-generator",
|
||||
name: "UI Test Generator",
|
||||
description: "Generate Playwright UI Test for your application views.",
|
||||
enabled: () => A(I),
|
||||
available: () => !0,
|
||||
requiresReload: !0
|
||||
}, L = [
|
||||
j,
|
||||
M,
|
||||
N,
|
||||
P,
|
||||
F,
|
||||
I
|
||||
];
|
||||
})), z, B, V, H;
|
||||
//#endregion
|
||||
e((() => {
|
||||
x(), v(), a(), m(), R(), E(), n(), T(), g(), y(), r(), u(), O(), f(), z = window.Vaadin.devTools, B = class extends k {
|
||||
constructor(...e) {
|
||||
super(...e), this.toggledFeaturesThatAreRequiresServerRestart = [];
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback(), this.classList.add("contents");
|
||||
}
|
||||
render() {
|
||||
let e = h.userInfo?.copilotExperimentalFeatureFlag;
|
||||
return s`
|
||||
<div class="flex flex-col gap-6 px-4 py-0.5">
|
||||
<div class="border-dashed flex flex-col divide-y">
|
||||
${h.featureFlags.slice().sort((e, t) => e.title.localeCompare(t.title)).map((e) => s`
|
||||
<div class="flex gap-2 justify-between py-3.5">
|
||||
<div class="flex flex-col">
|
||||
<label id="${e.id}-label">${e.title}</label>
|
||||
<a
|
||||
class="flex gap-0.5 text-xs"
|
||||
href="${e.moreInfoLink}"
|
||||
id="${e.id}-desc"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>More info<vaadin-icon class="icon-sm" .svg="${o.arrowOutward}"></vaadin-icon
|
||||
></a>
|
||||
</div>
|
||||
<copilot-toggle-button
|
||||
accessible-name-ref="${e.id}-label"
|
||||
accessible-desc-ref="${e.id}-desc"
|
||||
?checked=${e.enabled}
|
||||
@on-change=${(t) => this.toggleFeatureFlag(t, e)}>
|
||||
</copilot-toggle-button>
|
||||
</div>
|
||||
`)}
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
${e ? s`<h3 class="font-semibold my-0 text-sm">Copilot Experimental Features</h3>
|
||||
<div class="border-dashed flex flex-col divide-y">
|
||||
${L.filter((e) => e.available()).slice().sort((e, t) => e.name.localeCompare(t.name)).map((e) => s`
|
||||
<div class="flex gap-2 justify-between py-3.5">
|
||||
<div class="flex flex-col">
|
||||
<label id="${e.id}-label">${e.description}</label>
|
||||
</div>
|
||||
<copilot-toggle-button
|
||||
accessible-name-ref="${e.id}-label"
|
||||
?checked=${w.isExperimentalFeatureEnabled(e)}
|
||||
@on-change=${(t) => this.toggleExperimentalFeatureFlag(t, e)}>
|
||||
</copilot-toggle-button>
|
||||
</div>
|
||||
`)}
|
||||
</div>` : i}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
toggleFeatureFlag(e, n) {
|
||||
let r = e.target.checked;
|
||||
_("use-feature", {
|
||||
source: "toggle",
|
||||
enabled: r,
|
||||
id: n.id
|
||||
}), z.frontendConnection ? (z.frontendConnection.send("setFeature", {
|
||||
featureId: n.id,
|
||||
enabled: r
|
||||
}), n.requiresServerRestart && h.toggleServerRequiringFeatureFlag(n), D({
|
||||
type: t.INFORMATION,
|
||||
message: `“${n.title}” ${r ? "enabled" : "disabled"}`,
|
||||
details: n.requiresServerRestart ? S() : void 0,
|
||||
dismissId: `feature${n.id}${r ? "Enabled" : "Disabled"}`
|
||||
}), n.id === "copilotExperimentalFeatures" && h.userInfo && h.setUserInfo({
|
||||
...h.userInfo,
|
||||
copilotExperimentalFeatureFlag: r
|
||||
})) : z.log("error", `Unable to toggle feature ${n.title}: No server connection available`);
|
||||
}
|
||||
toggleExperimentalFeatureFlag(e, t) {
|
||||
let n = e.target.checked;
|
||||
_("use-experimental-feature", {
|
||||
source: "toggle",
|
||||
enabled: n,
|
||||
id: t.id
|
||||
});
|
||||
let r = w.isExperimentalFeatureEnabled(t);
|
||||
w.setExperimentalFeatureEnabled(t, n), t.requiresReload && n && !r && window.location.reload();
|
||||
}
|
||||
}, c([d()], B.prototype, "toggledFeaturesThatAreRequiresServerRestart", void 0), B = c([l("copilot-features-panel")], B), V = class extends p {
|
||||
constructor(...e) {
|
||||
super(...e), this.serverRestarting = !1;
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
render() {
|
||||
if (h.serverRestartRequiringToggledFeatureFlags.length === 0 || !C()) return i;
|
||||
let e = this.serverRestarting ? "Restarting..." : "Click to restart server";
|
||||
return s`
|
||||
<vaadin-button
|
||||
aria-label="Restart server"
|
||||
?disabled="${this.serverRestarting}"
|
||||
theme="icon tertiary"
|
||||
@click=${() => {
|
||||
this.serverRestarting = !0, b();
|
||||
}}>
|
||||
<vaadin-icon .svg="${o.refresh}"></vaadin-icon>
|
||||
<vaadin-tooltip slot="tooltip" text=${e}></vaadin-tooltip>
|
||||
</vaadin-button>
|
||||
`;
|
||||
}
|
||||
}, c([d()], V.prototype, "serverRestarting", void 0), V = c([l("copilot-features-actions")], V), H = {
|
||||
header: "Features",
|
||||
tag: "copilot-features-panel",
|
||||
helpUrl: "https://vaadin.com/docs/latest/flow/configuration/feature-flags",
|
||||
actionsTag: "copilot-features-actions",
|
||||
toolbarOptions: {
|
||||
allowedModesWithOrder: { common: 0 },
|
||||
iconKey: "listAlt"
|
||||
}
|
||||
}, window.Vaadin.copilot.plugins.push({ init(e) {
|
||||
e.addPanel(H);
|
||||
} });
|
||||
}))();
|
||||
export { V as CopilotFeaturesActions, B as CopilotFeaturesPanel };
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { _ as t, at as n, dt as r, g as i, n as a, r as o, st as s, t as c, u as l } from "./icons-CwakCZgK.js";
|
||||
import { l as u, o as d } from "./consts-CSALuSsm.js";
|
||||
import { a as f, c as p, d as m, i as h, l as g, n as _, o as v, r as y, s as b, t as x } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { a as S, i as C, n as w, r as T } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { r as E, t as D } from "./stats-CRkPKCLQ.js";
|
||||
import { n as O, t as k } from "./base-panel-Fr0D1ZcU.js";
|
||||
import { n as A, t as j } from "./copilot-message-box-CVAh5PSs.js";
|
||||
//#region frontend/copilot/plugins/copilot-feedback/copilot-feedback-plugin.ts
|
||||
var M, N, P, F, I, L, R, z, B;
|
||||
//#endregion
|
||||
e((() => {
|
||||
b(), O(), h(), o(), n(), j(), i(), u(), S(), a(), x(), D(), T(), v(), M = "https://github.com/vaadin", N = "https://github.com/vaadin/copilot/issues/new", P = "?template=feature_request.md&title=%5BFEATURE%5D", F = "A short, concise description of the bug and why you consider it a bug. Any details like exceptions and logs can be helpful as well.", I = "Please provide as many details as possible, this will help us deliver a fix as soon as possible.%0AThank you!%0A%0A%23%23%23 Description of the Bug%0A%0A{description}%0A%0A%23%23%23 Expected Behavior%0A%0AA description of what you would expect to happen. (Sometimes it is clear what the expected outcome is if something does not work, other times, it is not super clear.)%0A%0A%23%23%23 Minimal Reproducible Example%0A%0AWe would appreciate the minimum code with which we can reproduce the issue.%0A%0A%23%23%23 Versions%0A{versionsInfo}", L = s({
|
||||
showForm: !0,
|
||||
submitDisabled: !1
|
||||
}), R = class extends k {
|
||||
constructor() {
|
||||
super(), this.description = "", this.types = [
|
||||
{
|
||||
label: "General feedback",
|
||||
value: "feedback",
|
||||
ghTitle: ""
|
||||
},
|
||||
{
|
||||
label: "Report a bug",
|
||||
value: "bug",
|
||||
ghTitle: "[BUG]"
|
||||
},
|
||||
{
|
||||
label: "Ask a question",
|
||||
value: "question",
|
||||
ghTitle: "[QUESTION]"
|
||||
},
|
||||
{
|
||||
label: "Share an idea",
|
||||
value: "idea",
|
||||
ghTitle: "[FEATURE]"
|
||||
}
|
||||
], this.type = this.types[0].value, this.topics = [
|
||||
{
|
||||
label: "Generic",
|
||||
value: "platform"
|
||||
},
|
||||
{
|
||||
label: "Flow",
|
||||
value: "flow"
|
||||
},
|
||||
{
|
||||
label: "Hilla",
|
||||
value: "hilla"
|
||||
},
|
||||
{
|
||||
label: "Copilot",
|
||||
value: "copilot"
|
||||
}
|
||||
], this.topic = this.topics[0].value;
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback(), this.classList.add("contents");
|
||||
}
|
||||
willUpdate(e) {
|
||||
super.willUpdate(e), this.syncFooterState();
|
||||
}
|
||||
syncFooterState() {
|
||||
let e = this.message === void 0, t = this.type === "question" && !this.email;
|
||||
(L.showForm !== e || L.submitDisabled !== t) && r(() => {
|
||||
L.showForm = e, L.submitDisabled = t;
|
||||
});
|
||||
}
|
||||
getPreferredHeight() {
|
||||
return 620;
|
||||
}
|
||||
render() {
|
||||
return l`<div class="flex flex-col gap-4 pb-4 px-4">${this.renderContent()}</div>`;
|
||||
}
|
||||
renderContent() {
|
||||
return this.message === void 0 ? l`
|
||||
${A("info", "Your feedback means a lot to us. Whether you've encountered an issue, have a question, or have ideas to improve our platform, we'd love to hear from you. Feel free to leave your email and we'll get back to you — you can also share a code snippet to help us better understand your experience.", void 0, { icon: c.favorite })}
|
||||
<vaadin-radio-group
|
||||
label="Type"
|
||||
theme="toggle"
|
||||
.value="${this.type}"
|
||||
@value-changed=${(e) => {
|
||||
this.type = e.detail.value;
|
||||
}}>
|
||||
${this.types.map((e) => l`<vaadin-radio-button .value="${e.value}" label="${e.label}"></vaadin-radio-button>`)}
|
||||
</vaadin-radio-group>
|
||||
<vaadin-select
|
||||
label="Topic"
|
||||
overlay-class="alwaysVisible"
|
||||
.items=${this.topics}
|
||||
.value="${this.topic}"
|
||||
.hidden=${this.type !== "feedback"}
|
||||
@value-changed=${(e) => {
|
||||
this.topic = e.detail.value;
|
||||
}}>
|
||||
</vaadin-select>
|
||||
<vaadin-text-area
|
||||
min-rows="3"
|
||||
.value="${this.description}"
|
||||
@keydown=${this.keyDown}
|
||||
@focus=${() => {
|
||||
this.descriptionField.invalid = !1, this.descriptionField.placeholder = "";
|
||||
}}
|
||||
@value-changed=${(e) => {
|
||||
this.description = e.detail.value;
|
||||
}}
|
||||
label="Your Feedback"
|
||||
placeholder="What happened, what you expected, or what you'd change..."></vaadin-text-area>
|
||||
<vaadin-email-field
|
||||
@keydown=${this.keyDown}
|
||||
@value-changed=${(e) => {
|
||||
this.email = e.detail.value;
|
||||
}}
|
||||
.required=${this.type === "question"}
|
||||
id="email"
|
||||
value="${w.userInfo?.email}"
|
||||
label="Email${this.type === "question" ? "" : " (optional)"}"></vaadin-email-field>
|
||||
` : l`<p class="m-0">${this.message}</p>`;
|
||||
}
|
||||
createGithubIssue() {
|
||||
C.emit("system-info-with-callback", {
|
||||
callback: (e) => this.openGithub(e, this),
|
||||
notify: !1
|
||||
});
|
||||
}
|
||||
close() {
|
||||
_.closePanel("copilot-feedback-panel");
|
||||
}
|
||||
submit() {
|
||||
if (E("feedback", {
|
||||
github: !1,
|
||||
type: this.type,
|
||||
topic: this.topic
|
||||
}), this.description.trim() === "") {
|
||||
this.descriptionField.invalid = !0, this.descriptionField.placeholder = "Please tell us more before sending", this.descriptionField.value = "";
|
||||
return;
|
||||
}
|
||||
let e = {
|
||||
description: this.description,
|
||||
email: this.email,
|
||||
type: this.type,
|
||||
topic: this.topic
|
||||
};
|
||||
C.emit("system-info-with-callback", {
|
||||
callback: (n) => t(`${d}feedback`, {
|
||||
...e,
|
||||
versions: n
|
||||
}),
|
||||
notify: !1
|
||||
}), this.parentNode?.style.setProperty("--section-height", "150px"), this.message = "Thank you for sharing feedback.";
|
||||
}
|
||||
keyDown(e) {
|
||||
(e.key === "Backspace" || e.key === "Delete") && e.stopPropagation();
|
||||
}
|
||||
openGithub(e, t) {
|
||||
if (E("feedback", {
|
||||
github: !0,
|
||||
type: this.type,
|
||||
topic: this.topic
|
||||
}), this.type === "idea") {
|
||||
window.open(`${N}${P}`);
|
||||
return;
|
||||
}
|
||||
if (this.type === "feedback") {
|
||||
window.open(`${M}/${this.topic}/issues/new`);
|
||||
return;
|
||||
}
|
||||
let n = e ? e.replace(/\n/g, "%0A") : "Activate Copilot to include version info.", r = `${t.types.find((e) => e.value === this.type)?.ghTitle}`, i = t.description === "" ? F : t.description, a = I.replace("{description}", i).replace("{versionsInfo}", n);
|
||||
window.open(`${N}?title=${r}&body=${a}`, "_blank")?.focus();
|
||||
}
|
||||
}, f([g()], R.prototype, "description", void 0), f([g()], R.prototype, "type", void 0), f([g()], R.prototype, "topic", void 0), f([g()], R.prototype, "email", void 0), f([g()], R.prototype, "message", void 0), f([g()], R.prototype, "types", void 0), f([g()], R.prototype, "topics", void 0), f([p("vaadin-text-area")], R.prototype, "descriptionField", void 0), R = f([m("copilot-feedback-panel")], R), z = class extends y {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback(), this.classList.add("contents");
|
||||
}
|
||||
getPanel() {
|
||||
return this.closest("vaadin-dialog")?.querySelector("copilot-feedback-panel") ?? null;
|
||||
}
|
||||
render() {
|
||||
return L.showForm ? l`
|
||||
<vaadin-button
|
||||
style="margin-inline-end: auto"
|
||||
theme="tertiary"
|
||||
@click=${() => this.getPanel()?.createGithubIssue()}>
|
||||
<vaadin-icon slot="prefix" .svg="${c.github}"></vaadin-icon>
|
||||
Create GitHub Issue
|
||||
</vaadin-button>
|
||||
<vaadin-button theme="tertiary" @click=${() => this.getPanel()?.close()}>Cancel</vaadin-button>
|
||||
<vaadin-button
|
||||
theme="primary"
|
||||
?disabled=${L.submitDisabled}
|
||||
@click=${() => this.getPanel()?.submit()}>
|
||||
Submit
|
||||
</vaadin-button>
|
||||
` : l`<vaadin-button @click=${() => this.getPanel()?.close()}>Close</vaadin-button>`;
|
||||
}
|
||||
}, z = f([m("copilot-feedback-footer-actions")], z), B = {
|
||||
header: "Help Us Improve!",
|
||||
tag: "copilot-feedback-panel",
|
||||
footerActionsTag: "copilot-feedback-footer-actions",
|
||||
individual: !0
|
||||
}, window.Vaadin.copilot.plugins.push({ init(e) {
|
||||
e.addPanel(B);
|
||||
} }), _.addPanel(B);
|
||||
}))();
|
||||
export { z as CopilotFeedbackFooterActions, R as CopilotFeedbackPanel };
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { c as t, d as n } from "./dom-utils-Cuv93-tQ.js";
|
||||
//#region frontend/copilot/copilot-focus-trap.ts
|
||||
function r() {
|
||||
return document.body.querySelector("copilot-main");
|
||||
}
|
||||
var i, a;
|
||||
//#endregion
|
||||
e((() => {
|
||||
t(), i = class {
|
||||
constructor() {
|
||||
this.active = !1, this.activate = () => {
|
||||
this.active = !0;
|
||||
let e = this.getApplicationRootElement();
|
||||
e && e instanceof HTMLElement && e.addEventListener("focusin", this.focusInEventListener), r()?.focus(), r()?.addEventListener("focusout", this.keepFocusInCopilot);
|
||||
}, this.deactivate = () => {
|
||||
this.active = !1;
|
||||
let e = this.getApplicationRootElement();
|
||||
e && e instanceof HTMLElement && e.removeEventListener("focusin", this.focusInEventListener), r()?.removeEventListener("focusout", this.keepFocusInCopilot);
|
||||
}, this.focusInEventListener = (e) => {
|
||||
this.active && (e.preventDefault(), e.stopPropagation(), n(e.target) || requestAnimationFrame(() => {
|
||||
e.target.blur && e.target.blur(), r()?.focus();
|
||||
}));
|
||||
};
|
||||
}
|
||||
getApplicationRootElement() {
|
||||
return document.body.firstElementChild;
|
||||
}
|
||||
keepFocusInCopilot(e) {
|
||||
e.preventDefault(), e.stopPropagation(), r()?.focus();
|
||||
}
|
||||
}, a = new i();
|
||||
}))();
|
||||
export { a as copilotFocusTrap };
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { A as t, J as n, K as r, L as i, N as a, P as ee, R as o, _ as s, a as c, at as l, g as te, n as u, o as d, ot as f, q as p, r as m, st as h, t as g, u as _ } from "./icons-CwakCZgK.js";
|
||||
import { l as v, o as y } from "./consts-CSALuSsm.js";
|
||||
import { c as b, g as x } from "./dom-utils-Cuv93-tQ.js";
|
||||
import { a as S, n as C, o as w, t as T } from "./copilot-tree-impl-DxBvMTRa.js";
|
||||
import { a as E, i as D } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { i as O, n as k } from "./copilot-server-communicator-impl-B7YDzJpM.js";
|
||||
import { a as A, i as j, t as M } from "./stats-CRkPKCLQ.js";
|
||||
import { i as N, o as P } from "./copilot-error-handler-9OpssAH1.js";
|
||||
//#region frontend/copilot/show-in-ide.ts
|
||||
function F(e, n) {
|
||||
I(e) ? (j("show-in-ide", {
|
||||
attach: n ?? !1,
|
||||
goToCustomComponentFile: !0
|
||||
}), s(`${y}show-in-ide`, {
|
||||
javaClassName: e.className,
|
||||
fileName: e.absoluteFilePath
|
||||
})) : ee(e) ? (j("show-in-ide", { attach: n ?? !1 }), s(`${y}show-in-ide`, {
|
||||
...t(e),
|
||||
attach: n ?? !1
|
||||
})) : (A("show-in-ide"), s(`${y}show-in-ide`, e));
|
||||
}
|
||||
function I(e) {
|
||||
return e === void 0 ? !1 : e.className === void 0 ? e.absoluteFilePath !== void 0 : !0;
|
||||
}
|
||||
function L(e) {
|
||||
if (!e.isReactComponent) return;
|
||||
let t = p(e.node);
|
||||
if (t) return t;
|
||||
let n = r(e.node);
|
||||
if (n) return n;
|
||||
let i = e.children.sort((e, t) => e.siblingIndex - t.siblingIndex).find((e) => e.isReactComponent && L(e) !== void 0);
|
||||
if (!i) throw Error(`Could not find the source of ${e.nameAndIdentifier}`);
|
||||
return p(i.node);
|
||||
}
|
||||
var R = e((() => {
|
||||
a(), n(), v(), E(), te(), M(), D.on("show-in-ide", (e) => {
|
||||
let t = e.detail.node;
|
||||
if (e.detail.source) {
|
||||
F(e.detail.source);
|
||||
return;
|
||||
}
|
||||
if (e.detail.javaSource) {
|
||||
F(e.detail.javaSource);
|
||||
return;
|
||||
}
|
||||
if (!t) return;
|
||||
if (t.isFlowComponent) {
|
||||
F(t.node, e.detail.attach);
|
||||
return;
|
||||
}
|
||||
let n = L(t);
|
||||
n && F(n);
|
||||
});
|
||||
}));
|
||||
//#endregion
|
||||
//#region frontend/copilot/empty-app-initializer.ts
|
||||
function z(e) {
|
||||
let t = document.createElement("div");
|
||||
document.body.innerHTML = "", document.body.appendChild(t), c(e, t);
|
||||
}
|
||||
function B() {
|
||||
z(_`<div class="flex flex-col gap-4 h-screen items-center justify-center">
|
||||
<vaadin-icon class="animate-spin" .svg=${g.progressActivity}></vaadin-icon>
|
||||
<h3 class="m-0">The files have been created</h3>
|
||||
<p class="m-0">Restart the server to load the new view</p>
|
||||
<p class="m-0"><small>The page will refresh automatically when the server is ready.</small></p>
|
||||
</div>`);
|
||||
}
|
||||
async function V() {
|
||||
let e = 1e3, t = 12e4, n = Date.now(), r = async () => {
|
||||
try {
|
||||
return (await fetch(globalThis.location.href, { method: "HEAD" })).ok;
|
||||
} catch {
|
||||
return !1;
|
||||
}
|
||||
}, i = !1;
|
||||
for (; Date.now() - n < t;) {
|
||||
if (!await r()) {
|
||||
i = !0;
|
||||
break;
|
||||
}
|
||||
await new Promise((t) => {
|
||||
setTimeout(t, e);
|
||||
});
|
||||
}
|
||||
for (; Date.now() - n < t;) {
|
||||
if (await r() && i) {
|
||||
sessionStorage.removeItem(G), globalThis.location.reload();
|
||||
return;
|
||||
}
|
||||
await new Promise((t) => {
|
||||
setTimeout(t, e);
|
||||
});
|
||||
}
|
||||
}
|
||||
function H(e) {
|
||||
z(_`<div class="flex flex-col gap-4 h-screen items-center justify-center">
|
||||
<vaadin-icon class="animate-spin" .svg=${g.progressActivity}></vaadin-icon>
|
||||
<h3 class="m-0">Creating your ${e === "flow" ? "Flow" : "Hilla"} view...</h3>
|
||||
</div>`), o("copilot-init-app", { framework: e }, async (e) => {
|
||||
if (e.data.success) sessionStorage.setItem(G, "true"), B(), V();
|
||||
else {
|
||||
let t = e.data.reason;
|
||||
N(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
function U() {
|
||||
z(_`<div class="m-8">
|
||||
<h3>No views found</h3>
|
||||
<p>To get started, you can</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
@click=${(e) => {
|
||||
e.preventDefault(), H("flow");
|
||||
}}
|
||||
>Create a Flow view using Copilot</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
Create a view manually in your IDE, see
|
||||
<a target="_blank" href="https://vaadin.com/docs/latest/tutorial">the tutorial</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Learn more at <a target="_blank" href="https://vaadin.com/docs">https://vaadin.com/docs</a>.</p>
|
||||
</div>`);
|
||||
}
|
||||
function W() {
|
||||
sessionStorage.getItem(G) ? (B(), V()) : U();
|
||||
}
|
||||
var G, K = e((() => {
|
||||
i(), P(), u(), m(), G = "vaadin.copilot.viewCreated";
|
||||
})), q, J = e((() => {
|
||||
E(), q = class {
|
||||
constructor(e) {
|
||||
this._currentTree = e;
|
||||
}
|
||||
get root() {
|
||||
return this.currentTree.root;
|
||||
}
|
||||
get allNodesFlat() {
|
||||
return this.currentTree.allNodesFlat;
|
||||
}
|
||||
getNodeOfElement(e) {
|
||||
return this.currentTree.getNodeOfElement(e);
|
||||
}
|
||||
getChildren(e) {
|
||||
return this.currentTree.getChildren(e);
|
||||
}
|
||||
hasFlowComponents() {
|
||||
return this.currentTree.hasFlowComponents();
|
||||
}
|
||||
findNodeByUuid(e) {
|
||||
return this.currentTree.findNodeByUuid(e);
|
||||
}
|
||||
getElementByNodeUuid(e) {
|
||||
return this.currentTree.getElementByNodeUuid(e);
|
||||
}
|
||||
findByTreePath(e) {
|
||||
return this.currentTree.findByTreePath(e);
|
||||
}
|
||||
get currentTree() {
|
||||
return this._currentTree;
|
||||
}
|
||||
set currentTree(e) {
|
||||
let t = this._currentTree;
|
||||
this._currentTree = e, D.emit("copilot-tree-created", {
|
||||
prev: t,
|
||||
curr: e
|
||||
});
|
||||
}
|
||||
get customComponentDataLoaded() {
|
||||
return this._currentTree.customComponentDataLoaded;
|
||||
}
|
||||
};
|
||||
})), Y = e((() => {
|
||||
E(), D.on("navigate", (e) => {
|
||||
let t = window.history.state?.idx, n = {};
|
||||
t !== void 0 && (n.idx = t + 1), window.history.pushState(n, "", e.detail.path), window.dispatchEvent(new PopStateEvent("popstate"));
|
||||
});
|
||||
}));
|
||||
//#endregion
|
||||
//#region frontend/copilot/shared/copilot-storage-utils.ts
|
||||
function X(e) {
|
||||
let t = window.Vaadin.copilot.tree;
|
||||
return e.map((e) => {
|
||||
let n = null, { nodeUuid: r, treePath: i, childIndex: a } = e;
|
||||
if (r) {
|
||||
let e = t.findNodeByUuid(r);
|
||||
e && (n = e);
|
||||
}
|
||||
return n ||= t.findByTreePath(i) ?? null, n && a !== void 0 && n.children.length > a ? n.children[a] : n;
|
||||
}).filter((e) => e !== null);
|
||||
}
|
||||
var Z = e((() => {})), Q, ne = e((() => {
|
||||
m(), u(), l(), w(), Z(), b(), Q = class e {
|
||||
constructor() {
|
||||
this.drillDownComponentStack = [], f(this, { drillDownComponentStack: h.shallow });
|
||||
}
|
||||
getCustomComponentIcon(e) {
|
||||
let t = this.getIconTag(e);
|
||||
return t === void 0 ? d : g[t];
|
||||
}
|
||||
getIconTag(e) {
|
||||
let t = this.getCustomComponentInfo(e)?.type;
|
||||
if (t === "IN_PROJECT") return "thermostatCarbon";
|
||||
if (t === "EXTERNAL") return "deployedCube";
|
||||
}
|
||||
getCustomComponentInfo(t) {
|
||||
if (t.customComponentData && e.isCustomComponentInstanceInfo(t.customComponentData)) return t.customComponentData;
|
||||
}
|
||||
isCustomComponent(e) {
|
||||
return this.getCustomComponentInfo(e) !== void 0;
|
||||
}
|
||||
isVisibleAndSelectable(e) {
|
||||
if (!this.getTree().customComponentDataLoaded) return !0;
|
||||
let t = this.getActiveDrillDownContext();
|
||||
if (!e.customComponentData) return e.isReactComponent && !e.parent && e.name === "App" && !t;
|
||||
if (e.uuid === t?.uuid) return !0;
|
||||
let n = this.getActiveDrillDownData(), r = e.customComponentData;
|
||||
if (!n?.filePath) {
|
||||
if (r) return !r.childOfCustomComponent;
|
||||
} else if (e.customComponentData) return this.checkNodeIsInDrillDownContext(r, n);
|
||||
else return !1;
|
||||
return !0;
|
||||
}
|
||||
pushDrillDownContext(e) {
|
||||
this.drillDownComponentStack.length > 0 && this.drillDownComponentStack[this.drillDownComponentStack.length - 1].uuid === e.uuid || (this.drillDownComponentStack.push(e), this.persistIntoStorage(), x(e));
|
||||
}
|
||||
isDrillDownContext(e) {
|
||||
return this.getActiveDrillDownContext()?.uuid === e.uuid;
|
||||
}
|
||||
getActiveDrillDownContext() {
|
||||
if (this.drillDownComponentStack.length !== 0) return this.resolveCurrentTreeNode(this.drillDownComponentStack[this.drillDownComponentStack.length - 1]);
|
||||
}
|
||||
clearDrillDownContext() {
|
||||
this.drillDownComponentStack = [], this.persistIntoStorage();
|
||||
}
|
||||
popDrillDownContext() {
|
||||
this.filterOutNonConnectedElementsFromDrillDownContextStack(), this.drillDownComponentStack.pop(), this.persistIntoStorage();
|
||||
}
|
||||
hasParentDrillDownContext() {
|
||||
return this.drillDownComponentStack.length > 1;
|
||||
}
|
||||
getParentDrillDownContext() {
|
||||
if (this.hasParentDrillDownContext()) return this.resolveCurrentTreeNode(this.drillDownComponentStack[this.drillDownComponentStack.length - 2]);
|
||||
}
|
||||
isChildInDrillContext(e) {
|
||||
let t = e.customComponentData;
|
||||
if (!t) return !0;
|
||||
let n = this.getActiveDrillDownData();
|
||||
return n ? this.checkNodeIsInDrillDownContext(t, n) : !1;
|
||||
}
|
||||
getActiveDrillDownData() {
|
||||
let e = this.getActiveDrillDownContext();
|
||||
if (e === void 0) return;
|
||||
let t = this.getCustomComponentInfo(e);
|
||||
if (!t?.javaClassName && !t?.reactMethodName) return;
|
||||
let n = e.node;
|
||||
return {
|
||||
className: t.javaClassName,
|
||||
methodName: t.reactMethodName,
|
||||
nodeId: n.nodeId,
|
||||
uiId: n.uiId,
|
||||
filePath: t.customComponentFilePath ?? void 0
|
||||
};
|
||||
}
|
||||
checkNodeIsInDrillDownContext(e, t) {
|
||||
return e.createLocationMethodName && t.methodName ? e.createLocationMethodName === t.methodName && t.filePath === e.createLocationPath : t.filePath === e.createLocationPath && t.className === e.createdClassName;
|
||||
}
|
||||
persistIntoStorage() {
|
||||
let e = this.drillDownComponentStack.map((e) => ({
|
||||
treePath: e.path,
|
||||
nodeUuid: e.uuid
|
||||
}));
|
||||
S.saveDrillDownContextReference(e);
|
||||
}
|
||||
restoreDrillDownFromStorage() {
|
||||
let t = S.getDrillDownContextReference(), n = [];
|
||||
if (t === void 0) {
|
||||
let t = this.getTree().allNodesFlat.find((e) => e.customComponentData?.routeView);
|
||||
t?.customComponentData && e.isCustomComponentInstanceInfo(t.customComponentData) && (n = [t]);
|
||||
} else n = X(t);
|
||||
n.forEach((e) => {
|
||||
let t = this.drillDownComponentStack.findIndex((t) => t.uuid === e.uuid);
|
||||
t !== -1 && this.drillDownComponentStack.splice(t, 1), this.drillDownComponentStack.push(e);
|
||||
});
|
||||
let r = this.drillDownComponentStack.filter((e) => !!this.getTree().findNodeByUuid(e.uuid));
|
||||
r.length !== this.drillDownComponentStack.length && (this.drillDownComponentStack = r, this.persistIntoStorage()), this.filterOutNonConnectedElementsFromDrillDownContextStack();
|
||||
let i = this.getActiveDrillDownContext();
|
||||
i && x(i);
|
||||
}
|
||||
areInternalsVisible(e) {
|
||||
if (!this.getCustomComponentInfo(e)) return !0;
|
||||
let t = this.getActiveDrillDownData(), n;
|
||||
return t && t.filePath && (n = t.filePath), n ? this.checkChildrenCreateLocationToDisplayInternals(e.children, n) : !1;
|
||||
}
|
||||
checkChildrenCreateLocationToDisplayInternals(e, t) {
|
||||
for (let n of e) {
|
||||
let e = n.customComponentData;
|
||||
if (e && e.createLocationPath === t || this.checkChildrenCreateLocationToDisplayInternals(n.children, t)) return !0;
|
||||
}
|
||||
return !1;
|
||||
}
|
||||
getDescendantsCreatedInActiveDrillDownContextFlatten(t) {
|
||||
if (t.customComponentData && e.isCustomComponentInstanceInfo(t.customComponentData)) {
|
||||
let e = this.getActiveDrillDownData(), n;
|
||||
if (e && e.filePath ? n = e.filePath : this.getRouteViewPath() && (n = this.getRouteViewPath()), n) return this.getChildrenInPathFlattenRecursively(t, n);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
getChildrenInPathFlattenRecursively(e, t) {
|
||||
let n = e.children, r = [];
|
||||
for (let e of n) {
|
||||
let n = e.customComponentData;
|
||||
n && n.createLocationPath === t && r.push(e), r.push(...this.getChildrenInPathFlattenRecursively(e, t));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
getTree() {
|
||||
return window.Vaadin.copilot.tree;
|
||||
}
|
||||
getRouteViewPath() {
|
||||
let e = this.getTree().allNodesFlat.find((e) => e.customComponentData?.routeView === !0);
|
||||
if (e) return e.customComponentData?.createLocationPath ?? void 0;
|
||||
}
|
||||
resolveCurrentTreeNode(e) {
|
||||
return this.getTree().findNodeByUuid(e.uuid) ?? this.getTree().findByTreePath(e.path) ?? e;
|
||||
}
|
||||
filterOutNonConnectedElementsFromDrillDownContextStack() {
|
||||
this.drillDownComponentStack = this.drillDownComponentStack.filter((e) => e.element === void 0 ? !0 : e.element.isConnected);
|
||||
}
|
||||
static isCustomComponentInstanceInfo(e) {
|
||||
return "type" in e && "activeLevel" in e;
|
||||
}
|
||||
};
|
||||
})), $;
|
||||
//#endregion
|
||||
e((() => {
|
||||
R(), k(), K(), J(), Y(), C(), ne(), window.Vaadin.copilot.comm = O, $ = new T(), window.Vaadin.copilot.tree = new q($), window.Vaadin.copilot.customComponentHandler = new Q(), window.Vaadin.copilot.initEmptyApp = H, window.Vaadin.copilot.noRoutesInProject = W;
|
||||
}))();
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { L as t, Q as n, R as r, et as i, n as a, r as o, t as s, u as c } from "./icons-CwakCZgK.js";
|
||||
import { a as l, d as u, l as d, n as f, o as p, s as m, t as h } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { n as g, t as _ } from "./copilot-stored-machine-state-D6qB_Peh.js";
|
||||
import { n as v, t as y } from "./early-project-state-LGwavSyI.js";
|
||||
import { n as b, t as x } from "./base-panel-Fr0D1ZcU.js";
|
||||
//#region frontend/copilot/application-user-switcher.ts
|
||||
function S(e) {
|
||||
return r("copilot-switch-user", { username: e }, (e) => e.data.error ? {
|
||||
success: !1,
|
||||
errorMessage: e.data.error.message
|
||||
} : { success: !0 });
|
||||
}
|
||||
var C = e((() => {
|
||||
t();
|
||||
})), w, T;
|
||||
//#endregion
|
||||
e((() => {
|
||||
o(), m(), i(), b(), a(), C(), _(), v(), h(), p(), w = class extends x {
|
||||
constructor(...e) {
|
||||
super(...e), this.username = "", this.errorMessage = "", this.isLoading = !1, this.handleKeyDown = async (e) => {
|
||||
e.key === "Enter" && this.username && !this.isLoading && await this.handleSwitchUser();
|
||||
}, this.handleSwitchUser = async () => {
|
||||
if (!(!this.username || this.isLoading)) {
|
||||
this.isLoading = !0, this.errorMessage = "";
|
||||
try {
|
||||
let e = await S(this.username);
|
||||
e.success ? (g.addRecentSwitchedUsername(this.username), globalThis.location.reload()) : (this.errorMessage = e.errorMessage, this.isLoading = !1);
|
||||
} catch {
|
||||
this.errorMessage = "An unexpected error occurred", this.isLoading = !1;
|
||||
}
|
||||
}
|
||||
}, this.switchToRecentUser = async (e) => {
|
||||
this.username = e, await this.handleSwitchUser();
|
||||
}, this.removeRecentUser = (e, t) => {
|
||||
t.stopPropagation(), g.removeRecentSwitchedUsername(e), this.requestUpdate();
|
||||
};
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback(), this.classList.add("contents"), this.reaction(() => g.getRecentSwitchedUsernames(), () => {
|
||||
this.requestUpdate();
|
||||
});
|
||||
}
|
||||
render() {
|
||||
if (!y.springSecurityEnabled) return c`
|
||||
<div class="flex flex-col items-center pb-4 px-4">
|
||||
<vaadin-icon class="icon-lg mb-2" .svg="${s.accountCircle}"></vaadin-icon>
|
||||
<h3 class="mb-0.5 mt-0 text-semibold text-sm">Spring Security Disabled</h3>
|
||||
<p class="m-0 text-balance text-center text-secondary text-xs">
|
||||
User impersonation requires Spring Security to be configured in your application
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
let e = g.getRecentSwitchedUsernames();
|
||||
return c`
|
||||
<div class="flex flex-col gap-4 pb-4 px-4">
|
||||
<div class="flex gap-4 items-baseline">
|
||||
<vaadin-text-field
|
||||
class="flex-1"
|
||||
label="Username"
|
||||
.value="${this.username}"
|
||||
.errorMessage="${this.errorMessage}"
|
||||
.invalid="${this.errorMessage !== ""}"
|
||||
?disabled="${this.isLoading}"
|
||||
@value-changed="${(e) => {
|
||||
this.username = e.detail.value, this.errorMessage = "";
|
||||
}}"
|
||||
@keydown="${this.handleKeyDown}">
|
||||
<vaadin-icon slot="prefix" .svg="${s.accountCircle}"></vaadin-icon>
|
||||
</vaadin-text-field>
|
||||
<vaadin-button
|
||||
theme="primary"
|
||||
?disabled="${!this.username || this.isLoading}"
|
||||
@click="${this.handleSwitchUser}">
|
||||
<vaadin-icon slot="prefix" .svg="${s.swapHoriz}"></vaadin-icon>
|
||||
${this.isLoading ? "Switching..." : "Switch User"}
|
||||
</vaadin-button>
|
||||
</div>
|
||||
|
||||
${e.length > 0 ? c`
|
||||
<div class="flex flex-col gap-2 mt-1">
|
||||
<h3 class="m-0 text-semibold text-sm">Recent Usernames</h3>
|
||||
<ul
|
||||
class="bg-gray-2 dark:bg-gray-6 border border-gray-3 dark:border-gray-7 divide-y list-none m-0 p-0 rounded-md">
|
||||
${e.map((e) => c`
|
||||
<li class="flex gap-1 items-center pe-1 ps-3 py-1">
|
||||
<span class="flex-1">${e}</span>
|
||||
<vaadin-button theme="icon tertiary" @click="${() => this.switchToRecentUser(e)}">
|
||||
<vaadin-icon .svg="${s.swapHoriz}"></vaadin-icon>
|
||||
<vaadin-tooltip slot="tooltip" text="Switch to ${e}"></vaadin-tooltip>
|
||||
</vaadin-button>
|
||||
<vaadin-button
|
||||
aria-label="Remove ${e}"
|
||||
class="text-ruby-11"
|
||||
theme="icon tertiary"
|
||||
@click="${(t) => this.removeRecentUser(e, t)}">
|
||||
<vaadin-icon .svg="${s.delete}"></vaadin-icon>
|
||||
<vaadin-tooltip slot="tooltip" text="Remove ${e}"></vaadin-tooltip>
|
||||
</vaadin-button>
|
||||
</li>
|
||||
`)}
|
||||
</ul>
|
||||
</div>
|
||||
` : ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}, l([d()], w.prototype, "username", void 0), l([d()], w.prototype, "errorMessage", void 0), l([d()], w.prototype, "isLoading", void 0), w = l([u("copilot-impersonator")], w), T = {
|
||||
header: "Impersonate User",
|
||||
tag: n.IMPERSONATOR,
|
||||
individual: !0,
|
||||
toolbarOptions: {
|
||||
allowedModesWithOrder: { common: 0 },
|
||||
iconKey: "accountCircle"
|
||||
}
|
||||
}, globalThis.Vaadin.copilot.plugins.push({ init(e) {
|
||||
e.addPanel(T);
|
||||
} }), f.addPanel(T);
|
||||
}))();
|
||||
export { w as CopilotImpersonatorPanel };
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
import { i as e, n as t } from "./chunk-DiqZc92J.js";
|
||||
import { n, o as r, r as i, t as a, u as o } from "./icons-CwakCZgK.js";
|
||||
import { a as s, d as c, i as l, l as u, n as d, o as f, r as p, s as m, t as h } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { a as g, i as _, n as v, r as y } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { c as b, r as x, s as S } from "./copilot-development-setup-user-guide-utils-DzEVQbWO.js";
|
||||
import { n as C, t as w } from "./base-panel-Fr0D1ZcU.js";
|
||||
import { n as T, r as E, t as D } from "./copy-to-clipboard-4Y12mBRr.js";
|
||||
//#region frontend/copilot/plugins/copilot-info/copilot-info-plugin.ts
|
||||
function O(e, t) {
|
||||
let n;
|
||||
return n = e === !0 ? "text-teal-11" : e === "partial" ? "text-amber-11" : "text-ruby-11", o`<span class="${n}">${t}</span>`;
|
||||
}
|
||||
var k, A, j, M;
|
||||
//#endregion
|
||||
t((() => {
|
||||
m(), i(), C(), y(), g(), l(), n(), k = /* @__PURE__ */ e(D(), 1), S(), h(), T(), f(), A = class extends w {
|
||||
constructor(...e) {
|
||||
super(...e), this.sortedEntries = [];
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback(), this.classList.add("contents"), this.reaction(() => v.projectInfoEntries, () => {
|
||||
if (!v.projectInfoEntries) return;
|
||||
let e = [...v.projectInfoEntries, {
|
||||
name: "Development Workflow",
|
||||
value: ""
|
||||
}];
|
||||
e = e.filter((e) => e.name !== "Java Hotswap"), this.sortedEntries = e.sort((e, t) => e.name.localeCompare(t.name));
|
||||
}, { fireImmediately: !0 });
|
||||
}
|
||||
render() {
|
||||
return o` <div class="flex flex-col py-2 px-4">
|
||||
<dl class="border-dashed divide-y m-0">
|
||||
${E(this.sortedEntries.filter((e) => e.name !== "Java Hotswap"), (e) => e.name, (e) => this.renderRow(e))}
|
||||
</dl>
|
||||
</div>`;
|
||||
}
|
||||
renderRow(e) {
|
||||
if (e.name === "Development Workflow") return this.renderDevelopmentWorkflowButton();
|
||||
let t = e.name === "IDE Plugin" && e.value === !0 && v.idePluginState?.ide ? v.idePluginState.ide : e.value, n = this.getIcon(e.name, t), i = this.getIconColor(e.name), a = this.getTextColor(e);
|
||||
return o`
|
||||
<div class="flex gap-2 py-2">
|
||||
<dt class="flex gap-2">
|
||||
${n ? o`<vaadin-icon class="${i}" .svg="${n}"></vaadin-icon>` : r} ${e.name}
|
||||
</dt>
|
||||
<dd class="flex gap-2 m-0 ${a}">${this.renderRowValue(e)}</dd>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
renderRowValue(e) {
|
||||
return e.name === "Vaadin Employee" && e.value === !0 ? o`
|
||||
<vaadin-icon id="vaadin-employee" class="text-teal-11" .svg="${a.check}"></vaadin-icon>
|
||||
<vaadin-tooltip for="vaadin-employee" text="Yes"></vaadin-tooltip>
|
||||
` : o` ${!e.booleanInfo && typeof e.value == "string" ? e.value : r}
|
||||
${e.booleanInfo && typeof e.value == "boolean" ? O(e.value, e.booleanInfo.ariaLabel) : r}
|
||||
${e.booleanInfo?.text ? e.booleanInfo.text : r}
|
||||
${e.name === "Vaadin" ? this.renderVaadinRowMore() : r}`;
|
||||
}
|
||||
renderVaadinRowMore() {
|
||||
let e = v.newVaadinVersionState?.versions !== void 0 && v.newVaadinVersionState.versions.length > 0;
|
||||
return o`
|
||||
${v.projectVersionReleaseNoteInfo && v.projectVersionReleaseNoteInfo.url ? o`<a
|
||||
class="flex gap-0.5 items-center"
|
||||
href="${v.projectVersionReleaseNoteInfo.url}"
|
||||
id="release-notes-link"
|
||||
target="_blank"
|
||||
>Release notes <vaadin-icon class="icon-sm" .svg="${a.arrowOutward}"></vaadin-icon
|
||||
></a>` : r}
|
||||
<vaadin-button
|
||||
aria-label="Edit Vaadin version"
|
||||
class="-my-1.5 relative"
|
||||
@click="${(e) => {
|
||||
e.stopPropagation(), d.openPanel("copilot-vaadin-versions");
|
||||
}}"
|
||||
id="new-vaadin-version-btn"
|
||||
theme="icon tertiary">
|
||||
<vaadin-icon .svg="${a.editSquare}"></vaadin-icon>
|
||||
<vaadin-tooltip slot="tooltip" text="Edit Vaadin version"></vaadin-tooltip>
|
||||
${e ? o`<span aria-hidden="true" class="absolute bg-amber-11 end-0.5 rounded-full size-1 top-0.5"></span>` : ""}
|
||||
</vaadin-button>
|
||||
`;
|
||||
}
|
||||
renderDevelopmentWorkflowButton() {
|
||||
let e = x(), t = "", n = a.doneAll, r = "";
|
||||
return e.status === "success" ? (t = "text-teal-11", r = "IDE Plugin & Java Hotswap") : e.status === "warning" ? (t = "text-amber-11", n = a.arrowUploadReady, r = "Improve") : e.status === "error" && (t = "text-ruby-11", n = a.handyman, r = "Fix"), o`
|
||||
<div class="flex gap-2 py-2">
|
||||
<dt class="flex gap-2">
|
||||
<vaadin-icon class="text-amber-11" .svg="${a.bolt}"></vaadin-icon>
|
||||
Development Workflow
|
||||
</dt>
|
||||
<dd class="m-0">
|
||||
<vaadin-button
|
||||
class="-my-1.5 ${t}"
|
||||
id="development-workflow-status-detail"
|
||||
theme="tertiary"
|
||||
@click=${() => {
|
||||
b();
|
||||
}}>
|
||||
<vaadin-icon slot="prefix" .svg="${n}"></vaadin-icon>
|
||||
${r}
|
||||
</vaadin-button>
|
||||
</dd>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
getIconColor(e) {
|
||||
return e.includes("Vaadin") || e === "Copilot" ? "text-vaadin-blue" : "";
|
||||
}
|
||||
getTextColor(e) {
|
||||
if (typeof e.value == "string") {
|
||||
if (e.value.startsWith("Enabled")) return "text-teal-11";
|
||||
if (e.value.startsWith("Disabled")) return "text-ruby-11";
|
||||
}
|
||||
return "text-secondary";
|
||||
}
|
||||
getIcon(e, t) {
|
||||
switch (e) {
|
||||
case "Browser": {
|
||||
let e = typeof t == "string" ? t.toLowerCase() : "";
|
||||
return e.includes("chrome") && !e.includes("edg") ? a.chrome : e.includes("firefox") ? a.firefox : e.includes("safari") && !e.includes("chrome") ? a.safari : e.includes("edg") ? a.edge : a.webAsset;
|
||||
}
|
||||
case "Copilot": return a.vaadin;
|
||||
case "Flow": return a.flow;
|
||||
case "Frontend Hotswap": return a.swapHoriz;
|
||||
case "Hilla": return a.hilla;
|
||||
case "Java": return a.java;
|
||||
case "OS": {
|
||||
let e = typeof t == "string" ? t.toLowerCase() : "";
|
||||
return e.includes("mac") ? a.apple : e.includes("win") ? a.windows : a.computer;
|
||||
}
|
||||
case "Spring": return a.spring;
|
||||
case "Spring Boot": return a.springBoot;
|
||||
case "Spring Data JPA": return a.springData;
|
||||
case "Spring Security": return a.springSecurity;
|
||||
case "Vaadin":
|
||||
case "Vaadin Employee": return a.vaadin;
|
||||
case "Java Hotswap": return a.swapHoriz;
|
||||
case "IDE Plugin": return typeof t == "string" ? t.toLowerCase() === "intellij" ? a.intelliJ : t.toLowerCase() === "vscode" ? a.vsCode : t.toLowerCase() === "eclipse" ? a.eclipse : a.developerModeTv : a.developerModeTv;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}, s([u()], A.prototype, "sortedEntries", void 0), A = s([c("copilot-info-panel")], A), j = class extends p {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback(), this.style.display = "flex";
|
||||
}
|
||||
render() {
|
||||
return o` <vaadin-button
|
||||
aria-label="Copy to clipboard"
|
||||
@click=${() => {
|
||||
_.emit("system-info-with-callback", {
|
||||
callback: k.default,
|
||||
notify: !0
|
||||
});
|
||||
}}
|
||||
theme="icon tertiary">
|
||||
<vaadin-icon .svg="${a.fileCopy}"></vaadin-icon>
|
||||
<vaadin-tooltip slot="tooltip" text="Copy to clipboard"></vaadin-tooltip>
|
||||
</vaadin-button>`;
|
||||
}
|
||||
}, j = s([c("copilot-info-actions")], j), M = {
|
||||
header: "Info",
|
||||
tag: "copilot-info-panel",
|
||||
actionsTag: "copilot-info-actions",
|
||||
eager: !0,
|
||||
toolbarOptions: {
|
||||
iconKey: "info",
|
||||
allowedModesWithOrder: { common: 0 }
|
||||
}
|
||||
}, window.Vaadin.copilot.plugins.push({ init(e) {
|
||||
e.addPanel(M);
|
||||
} });
|
||||
}))();
|
||||
export { j as Actions, A as CopilotInfoPanel };
|
||||
+3920
File diff suppressed because it is too large
Load Diff
+222
@@ -0,0 +1,222 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { $ as t, C as n, D as r, at as i, dt as a, et as o, n as s, ot as c, r as l, t as u, u as d, w as f } from "./icons-CwakCZgK.js";
|
||||
import { a as p, d as m, i as h, l as g, n as _, o as v, r as y, s as b, t as x } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { a as S, i as C, n as w, r as T } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { r as E, t as D } from "./stats-CRkPKCLQ.js";
|
||||
import { n as O, o as k, t as A } from "./copilot-error-handler-9OpssAH1.js";
|
||||
import { n as j, t as M } from "./base-panel-Fr0D1ZcU.js";
|
||||
import { n as N, t as P } from "./copilot-message-box-CVAh5PSs.js";
|
||||
//#region frontend/copilot/copilot-time-formatter.ts
|
||||
var F, I, L = e((() => {
|
||||
F = () => {
|
||||
let e = {
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
second: "numeric",
|
||||
fractionalSecondDigits: 3
|
||||
}, t, n = navigator.language ?? "", r = n.indexOf("@"), i = r === -1 ? n : n.slice(0, r);
|
||||
try {
|
||||
t = new Intl.DateTimeFormat(Intl.getCanonicalLocales(i), e);
|
||||
} catch (n) {
|
||||
console.error("Failed to create date time formatter for ", i, n), t = new Intl.DateTimeFormat("en-US", e);
|
||||
}
|
||||
return t;
|
||||
}, I = F();
|
||||
}));
|
||||
//#endregion
|
||||
//#region frontend/copilot/plugins/copilot-log/copilot-log-plugin.ts
|
||||
function R(e) {
|
||||
return I.format(e);
|
||||
}
|
||||
var z, B, V, H, U, W, G;
|
||||
//#endregion
|
||||
e((() => {
|
||||
l(), b(), o(), j(), S(), T(), i(), n(), h(), s(), k(), D(), P(), x(), L(), v(), V = class {
|
||||
constructor() {
|
||||
this.showTimestamps = !1, c(this);
|
||||
}
|
||||
toggleShowTimestamps() {
|
||||
this.showTimestamps = !this.showTimestamps;
|
||||
}
|
||||
}, H = new V(), U = (z = class extends M {
|
||||
constructor(...e) {
|
||||
super(...e), this.unreadErrors = !1, this.messages = [], this.nextMessageId = 1, this.transitionDuration = 0, this.errorHandlersAdded = !1;
|
||||
}
|
||||
connectedCallback() {
|
||||
if (super.connectedCallback(), this.classList.add("contents"), this.onCommand("log", (e) => {
|
||||
this.handleLogEventData({
|
||||
type: e.data.type,
|
||||
message: e.data.message
|
||||
});
|
||||
}), this.onEventBus("log", (e) => this.handleLogEvent(e)), this.onEventBus("update-log", (e) => this.updateLog(e.detail)), this.onEventBus("notification-shown", (e) => this.handleNotification(e)), this.onEventBus("clear-log", () => this.clear()), this.reaction(() => w.sectionPanelResizing, () => {
|
||||
this.requestUpdate();
|
||||
}), this.transitionDuration = parseInt(window.getComputedStyle(this).getPropertyValue("--dev-tools-transition-duration"), 10), !this.errorHandlersAdded) {
|
||||
let e = (e) => {
|
||||
a(() => {
|
||||
_.attentionRequiredPanelTag = "copilot-log-panel";
|
||||
}), this.log(t.ERROR, e.message, !!e.internal, e.details, e.link);
|
||||
};
|
||||
A((t) => {
|
||||
e(t);
|
||||
}), O.forEach((t) => {
|
||||
e(t);
|
||||
}), O.length = 0, this.errorHandlersAdded = !0;
|
||||
}
|
||||
}
|
||||
clear() {
|
||||
this.messages = [];
|
||||
}
|
||||
handleNotification(e) {
|
||||
this.log(e.detail.type, e.detail.message, !0, e.detail.details, e.detail.link);
|
||||
}
|
||||
handleLogEvent(e) {
|
||||
this.handleLogEventData(e.detail);
|
||||
}
|
||||
handleLogEventData(e) {
|
||||
this.log(e.type, e.message, !!e.internal, e.details, e.link, r(e.expandedMessage), r(e.expandedDetails), e.id);
|
||||
}
|
||||
activate() {
|
||||
this.unreadErrors = !1, this.updateComplete.then(() => {
|
||||
let e = this.renderRoot.querySelector(".message:last-child");
|
||||
e && e.scrollIntoView();
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return d`
|
||||
${this.messages.length === 0 ? N("info", "Communication between application and backend services, errors, and all notifications will appear here.", "mb-3 mt-0 mx-3") : d`<ul class="border-dashed divide-y list-none m-0 pb-4 px-4">
|
||||
${this.messages.map((e) => this.renderMessage(e))}
|
||||
</ul>`}
|
||||
`;
|
||||
}
|
||||
renderMessage(e) {
|
||||
let n, i, a;
|
||||
e.type === t.ERROR ? (i = u.warning, n = "Error", a = "text-ruby-11") : e.type === t.WARNING ? (i = u.warning, n = "Warning", a = "text-amber-11") : (i = u.info, n = "Info", a = "text-blue-11");
|
||||
let o = e.expanded ? "" : "truncate";
|
||||
return d`
|
||||
<li class="flex gap-2 py-2" data-id="${e.id}">
|
||||
<vaadin-icon
|
||||
aria-label="${n}"
|
||||
class="${a}"
|
||||
id="log-icon-${e.id}"
|
||||
.svg="${i}"></vaadin-icon>
|
||||
<vaadin-tooltip for="log-icon-${e.id}" text="${n}"></vaadin-tooltip>
|
||||
<span class="flex flex-col flex-grow overflow-hidden" @click=${() => this.toggleExpanded(e)}>
|
||||
<span class="${o}" ?hidden=${!H.showTimestamps}>${R(e.timestamp)}</span>
|
||||
<span class="${o}">
|
||||
${e.expanded && e.expandedMessage ? e.expandedMessage : e.message}
|
||||
</span>
|
||||
${e.expanded ? d`<span id="log-details-${e.id}"
|
||||
>${e.expandedDetails ?? e.details}</span
|
||||
>` : d`<span
|
||||
class="${o}"
|
||||
id="log-details-${e.id}"
|
||||
?hidden="${!e.details && !e.link}">
|
||||
${r(e.details)}
|
||||
${e.link ? d`<a class="block mb-1" href="${e.link}" target="_blank">Learn more</a>` : ""}
|
||||
</span>`}
|
||||
</span>
|
||||
<vaadin-button
|
||||
aria-controls="log-details-${e.id}"
|
||||
aria-expanded="${e.expanded}"
|
||||
aria-label="${e.expanded ? "Collapse details" : "Expand details"}"
|
||||
theme="icon tertiary"
|
||||
@click=${() => this.toggleExpanded(e)}
|
||||
?hidden=${!this.canBeExpanded(e)}>
|
||||
<vaadin-icon
|
||||
class="${e.expanded ? "rotate-90" : ""} transition"
|
||||
.svg="${u.chevronRight}"></vaadin-icon>
|
||||
<vaadin-tooltip
|
||||
slot="tooltip"
|
||||
text="${e.expanded ? "Collapse details" : "Expand details"}"></vaadin-tooltip>
|
||||
</vaadin-button>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
log(e, n, r, i, a, o, s, c) {
|
||||
let l = this.nextMessageId;
|
||||
this.nextMessageId += 1, s ||= n;
|
||||
let u = {
|
||||
id: l,
|
||||
type: e,
|
||||
message: n,
|
||||
details: i,
|
||||
link: a,
|
||||
dontShowAgain: !1,
|
||||
deleted: !1,
|
||||
expanded: !1,
|
||||
expandedMessage: o,
|
||||
expandedDetails: s,
|
||||
timestamp: /* @__PURE__ */ new Date(),
|
||||
internal: r,
|
||||
userId: c
|
||||
};
|
||||
for (this.messages.push(u); this.messages.length > B.MAX_LOG_ROWS;) this.messages.shift();
|
||||
return this.requestUpdate(), this.updateComplete.then(() => {
|
||||
let n = this.renderRoot.querySelector(".message:last-child");
|
||||
n ? (setTimeout(() => n.scrollIntoView({ behavior: "smooth" }), this.transitionDuration), this.unreadErrors = !1) : e === t.ERROR && (this.unreadErrors = !0);
|
||||
}), u;
|
||||
}
|
||||
updateLog(e) {
|
||||
let n = this.messages.find((t) => t.userId === e.id);
|
||||
n ||= this.log(t.INFORMATION, "<Log message to update was not found>", !1), Object.assign(n, e), f(n.expandedDetails) && (n.expandedDetails = r(n.expandedDetails)), this.requestUpdate();
|
||||
}
|
||||
updated() {
|
||||
let e = this.querySelector(".row:last-child");
|
||||
e && this.isTooLong(e.querySelector(".firstrowmessage")) && e.querySelector("button.expand")?.removeAttribute("hidden");
|
||||
}
|
||||
toggleExpanded(e) {
|
||||
this.canBeExpanded(e) && (e.expanded = !e.expanded, this.requestUpdate()), E("use-log", { source: "toggleExpanded" });
|
||||
}
|
||||
canBeExpanded(e) {
|
||||
if (e.expandedMessage || e.expanded) return !0;
|
||||
let t = this.querySelector(`[data\\-id="${e.id}"]`)?.querySelector(".firstrowmessage");
|
||||
return this.isTooLong(t);
|
||||
}
|
||||
isTooLong(e) {
|
||||
return e && e.offsetWidth < e.scrollWidth;
|
||||
}
|
||||
}, B = z, z.MAX_LOG_ROWS = 1e3, z), p([g()], U.prototype, "unreadErrors", void 0), p([g()], U.prototype, "messages", void 0), U = B = p([m("copilot-log-panel")], U), W = class extends y {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
render() {
|
||||
return d`
|
||||
<style>
|
||||
copilot-log-panel-actions {
|
||||
display: contents;
|
||||
}
|
||||
</style>
|
||||
<vaadin-button
|
||||
aria-label="Clear log"
|
||||
@click=${() => {
|
||||
C.emit("clear-log", {});
|
||||
}}
|
||||
theme="icon tertiary">
|
||||
<vaadin-icon .svg="${u.delete}"></vaadin-icon>
|
||||
<vaadin-tooltip slot="tooltip" text="Clear log"></vaadin-tooltip>
|
||||
</vaadin-button>
|
||||
<vaadin-button
|
||||
aria-label="Toggle timestamps"
|
||||
@click=${() => {
|
||||
H.toggleShowTimestamps();
|
||||
}}
|
||||
theme="icon tertiary">
|
||||
<vaadin-icon .svg="${H.showTimestamps ? u.schedule : u.historyToggleOff}"></vaadin-icon>
|
||||
<vaadin-tooltip slot="tooltip" text="Toggle timestamps"></vaadin-tooltip>
|
||||
</vaadin-button>
|
||||
`;
|
||||
}
|
||||
}, W = p([m("copilot-log-panel-actions")], W), G = {
|
||||
header: "Log",
|
||||
tag: "copilot-log-panel",
|
||||
actionsTag: "copilot-log-panel-actions",
|
||||
individual: !0,
|
||||
toolbarOptions: {
|
||||
allowedModesWithOrder: { common: 0 },
|
||||
iconKey: "terminal"
|
||||
}
|
||||
}, window.Vaadin.copilot.plugins.push({ init(e) {
|
||||
e.addPanel(G);
|
||||
} }), _.addPanel(G);
|
||||
}))();
|
||||
export { W as Actions, U as CopilotLogPanel };
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { n as t, r as n, t as r, u as i } from "./icons-CwakCZgK.js";
|
||||
//#region frontend/copilot/shared/copilot-message-box.ts
|
||||
function a(e, t, n, r) {
|
||||
let { bg: a, iconClass: s, textClass: c, icon: l } = o[e], u = r?.icon ?? l, d = r?.iconExtraClass ? `${s} ${r.iconExtraClass}` : s;
|
||||
return i`
|
||||
<div class="${`${a} flex gap-2 pe-3 ps-2 py-2 rounded-md text-sm${n ? ` ${n}` : ""}`}">
|
||||
<vaadin-icon class="${d}" .svg="${u}"></vaadin-icon>
|
||||
${typeof t == "string" ? i`<span class="message-box-content${c ? ` ${c}` : ""}">${t}</span>` : t}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
var o, s = e((() => {
|
||||
n(), t(), o = {
|
||||
info: {
|
||||
bg: "bg-blue-3 dark:bg-blue-5",
|
||||
iconClass: "text-blue-11",
|
||||
textClass: "text-blue-12",
|
||||
icon: r.info
|
||||
},
|
||||
warning: {
|
||||
bg: "bg-amber-3 dark:bg-amber-5",
|
||||
iconClass: "text-amber-11",
|
||||
textClass: "text-amber-12",
|
||||
icon: r.warning
|
||||
},
|
||||
error: {
|
||||
bg: "bg-ruby-3 dark:bg-ruby-5",
|
||||
iconClass: "text-ruby-11",
|
||||
textClass: "text-ruby-12",
|
||||
icon: r.error
|
||||
},
|
||||
success: {
|
||||
bg: "bg-teal-3 dark:bg-teal-5",
|
||||
iconClass: "text-teal-11",
|
||||
textClass: "text-teal-12",
|
||||
icon: r.check
|
||||
},
|
||||
loading: {
|
||||
bg: "bg-gray-3 dark:bg-gray-6",
|
||||
iconClass: "animate-spin",
|
||||
textClass: "",
|
||||
icon: r.progressActivity
|
||||
}
|
||||
};
|
||||
}));
|
||||
//#endregion
|
||||
export { a as n, s as t };
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
import { i as e, n as t } from "./chunk-DiqZc92J.js";
|
||||
import { _ as n, g as r } from "./icons-CwakCZgK.js";
|
||||
import { l as i, n as a, s as o } from "./consts-CSALuSsm.js";
|
||||
import { n as s, t as c } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { n as l, r as u } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
//#region frontend/copilot/plugins/copilot-plugins.ts
|
||||
function d(e) {
|
||||
e.init({
|
||||
addPanel: (e) => {
|
||||
s.addPanel(e);
|
||||
},
|
||||
send(e, t) {
|
||||
n(e, t);
|
||||
}
|
||||
});
|
||||
}
|
||||
function f() {
|
||||
h().publicPluginsState === "NOT_INITIALIZED" && (g.push(import("./copilot-log-plugin-CmwIHcBw.js")), g.push(import("./copilot-info-plugin-9l6uSELy.js")), g.push(import("./copilot-features-plugin-DwQSwtbQ.js")), g.push(import("./copilot-feedback-plugin-JMYrBCmQ.js")), g.push(import("./copilot-settings-panel-qUN2f6RH.js")), g.push(import("./copilot-impersonator-plugin-iN25IekB.js")), g.push(import("./copilot-development-setup-user-guide-Db31eO1T.js")), g.push(import("./copilot-vaadin-versions-CkxDkDmp.js")), v = !0, h().setPublicPluginsState("IMPORTED"));
|
||||
}
|
||||
function p() {
|
||||
if (h().privatePluginsState === "NOT_INITIALIZED") {
|
||||
let e = window.Vaadin?.copilot?._localPluginsUrl, t = `https://cdn.vaadin.com/copilot/${o}/copilot-plugins${a}.js`, n = e || t;
|
||||
console.debug(`Loading private plugins from: ${n}`), import(
|
||||
/* @vite-ignore */
|
||||
n
|
||||
).then(() => {
|
||||
h().setPrivatePluginsState("INITIALIZED");
|
||||
}).catch((e) => {
|
||||
console.warn(`Unable to load plugins from ${n}. Some Copilot features are unavailable.`, e);
|
||||
});
|
||||
}
|
||||
}
|
||||
function m() {
|
||||
Promise.all(g).then(() => {
|
||||
let e = window.Vaadin;
|
||||
if (e.copilot.plugins) {
|
||||
let t = e.copilot.plugins;
|
||||
e.copilot.plugins.push = (e) => d(e), Array.from(t).forEach((e) => {
|
||||
_.includes(e) || (d(e), _.push(e));
|
||||
});
|
||||
}
|
||||
}), g = [], v && h().setPublicPluginsState("INITIALIZED");
|
||||
}
|
||||
function h() {
|
||||
return window.Vaadin.copilot._uiState;
|
||||
}
|
||||
var g, _, v, y = t((() => {
|
||||
c(), i(), r(), g = [], _ = [], v = !1;
|
||||
})), b, x = t((() => {
|
||||
b = window.Vaadin.copilot.overlayManager;
|
||||
}));
|
||||
//#endregion
|
||||
//#region frontend/copilot/dynamic-module-loader.ts
|
||||
async function S() {
|
||||
return (await import("./copilot-focus-trap-CaZw1c70.js")).copilotFocusTrap;
|
||||
}
|
||||
function C() {
|
||||
return import("./typescript-BkEBjsia.js").then((t) => /* @__PURE__ */ e(t.default, 1));
|
||||
}
|
||||
var w = t((() => {}));
|
||||
//#endregion
|
||||
//#region frontend/copilot/shared/copilot-modes.ts
|
||||
function T() {
|
||||
b.addOverlayOutsideClickEvent(), b.activate();
|
||||
}
|
||||
function E() {
|
||||
b.removeOverlayOutsideClickEvent(), b.deactivate();
|
||||
}
|
||||
function D() {
|
||||
let e = k[l.activeMode];
|
||||
return l.forceSelectionEnabled ? {
|
||||
...e,
|
||||
appInteractable: !1
|
||||
} : e;
|
||||
}
|
||||
function O(e) {
|
||||
return k[e];
|
||||
}
|
||||
var k, A = t((() => {
|
||||
u(), y(), w(), x(), k = {
|
||||
edit: {
|
||||
label: "Edit",
|
||||
appInteractable: !1,
|
||||
toolbarIcon: "code",
|
||||
toolbarOrder: 0,
|
||||
onActivation: async () => {
|
||||
let e = await S();
|
||||
e.active || e.activate(), T(), p();
|
||||
}
|
||||
},
|
||||
test: {
|
||||
label: "Test",
|
||||
appInteractable: !0,
|
||||
toolbarIcon: "bugReport",
|
||||
toolbarOrder: 1,
|
||||
onActivation: async (e) => {
|
||||
(e === "edit" || e === "inspect") && ((await S()).deactivate(), E()), p();
|
||||
}
|
||||
},
|
||||
inspect: {
|
||||
label: "Inspect",
|
||||
appInteractable: !1,
|
||||
toolbarIcon: "visibility",
|
||||
toolbarOrder: 2,
|
||||
onActivation: async () => {
|
||||
let e = await S();
|
||||
e.active || e.activate(), T(), p();
|
||||
}
|
||||
},
|
||||
play: {
|
||||
label: "Play",
|
||||
appInteractable: !0,
|
||||
toolbarIcon: "playCircle",
|
||||
toolbarOrder: 3,
|
||||
default: !0,
|
||||
onActivation: async (e) => {
|
||||
(e === "edit" || e === "inspect") && ((await S()).deactivate(), E());
|
||||
}
|
||||
}
|
||||
};
|
||||
}));
|
||||
//#endregion
|
||||
export { C as a, x as c, m as d, A as i, f as l, D as n, w as o, O as r, b as s, k as t, y as u };
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { a as t, i as n, n as r, r as i } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { n as a, t as o } from "./copilot-stored-machine-state-D6qB_Peh.js";
|
||||
//#region frontend/copilot/shared/copilot-notification.ts
|
||||
function s(e) {
|
||||
r.notifications.includes(e) && (e.dontShowAgain && e.dismissId && l(e.dismissId), r.removeNotification(e), n.emit("notification-dismissed", e));
|
||||
}
|
||||
function c(e) {
|
||||
return a.getDismissedNotifications().includes(e);
|
||||
}
|
||||
function l(e) {
|
||||
c(e) || a.addDismissedNotification(e);
|
||||
}
|
||||
function u(e) {
|
||||
return !(e.dismissId && (c(e.dismissId) || r.notifications.find((t) => t.dismissId === e.dismissId)));
|
||||
}
|
||||
function d(e) {
|
||||
if (u(e)) return f(e);
|
||||
}
|
||||
function f(e) {
|
||||
let t = m;
|
||||
m += 1;
|
||||
let i = {
|
||||
...e,
|
||||
id: t,
|
||||
dontShowAgain: !1,
|
||||
animatingIn: !0,
|
||||
animatingOut: !1
|
||||
};
|
||||
return r.setNotifications([...r.notifications, i]), requestAnimationFrame(() => {
|
||||
i.animatingIn = !1, r.setNotifications([...r.notifications]);
|
||||
}), (e.delay || !e.link && !e.dismissId) && setTimeout(() => {
|
||||
s(i);
|
||||
}, e.delay ?? p), n.emit("notification-shown", e), i;
|
||||
}
|
||||
var p, m, h = e((() => {
|
||||
t(), o(), i(), p = 5e3, m = 1;
|
||||
}));
|
||||
//#endregion
|
||||
export { h as n, d as r, s as t };
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import { n as e, r as t } from "./copilot-notification-CCNJdNg4.js";
|
||||
e();
|
||||
export { t as showNotification };
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { _ as t, g as n } from "./icons-CwakCZgK.js";
|
||||
import { i as r, r as i } from "./copilot-tree-impl-DxBvMTRa.js";
|
||||
import { a, i as o } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
//#region frontend/copilot/copilot-server-communicator-impl.ts
|
||||
function s(e) {
|
||||
for (let t of d) if (t.handleMessage(e)) return d.splice(d.indexOf(t), 1), !0;
|
||||
if (o.emitUnsafe({
|
||||
type: e.command,
|
||||
data: e.data
|
||||
})) return !0;
|
||||
for (let t of u()) if (c(t, e)) return !0;
|
||||
return f.push(e), !1;
|
||||
}
|
||||
function c(e, t) {
|
||||
return e.handleMessage?.call(e, t);
|
||||
}
|
||||
function l() {
|
||||
if (f.length) for (let e of u()) for (let t = 0; t < f.length; t++) c(e, f[t]) && (f.splice(t, 1), t--);
|
||||
}
|
||||
function u() {
|
||||
let e = document.querySelector("copilot-main");
|
||||
if (!e) return [];
|
||||
let t = [];
|
||||
return Array.from(e.shadowRoot.querySelectorAll("copilot-panel-manager vaadin-dialog[panel-container]")).forEach((e) => {
|
||||
let n = e.dataset.panelTag;
|
||||
if (n) {
|
||||
let r = e.querySelector(n);
|
||||
r && t.push(r);
|
||||
}
|
||||
}), t;
|
||||
}
|
||||
var d, f, p, m = e((() => {
|
||||
i(), a(), n(), d = [], f = [], p = async (e, n, i) => {
|
||||
let a, o;
|
||||
n.reqId = r();
|
||||
let s = new Promise((e, t) => {
|
||||
a = e, o = t;
|
||||
});
|
||||
return d.push({ handleMessage(e) {
|
||||
if (e?.data?.reqId !== n.reqId) return !1;
|
||||
try {
|
||||
a(i(e));
|
||||
} catch (e) {
|
||||
o(e);
|
||||
}
|
||||
return !0;
|
||||
} }), t(e, n), s;
|
||||
};
|
||||
}));
|
||||
//#endregion
|
||||
export { p as i, m as n, l as r, s as t };
|
||||
+430
@@ -0,0 +1,430 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { L as t, Q as n, R as r, et as i, n as a, o, r as s, t as c, u as l } from "./icons-CwakCZgK.js";
|
||||
import { l as u, o as d } from "./consts-CSALuSsm.js";
|
||||
import { a as f, d as p, l as m, o as h, s as g } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { a as _, n as v, r as y } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { r as b, t as x } from "./stats-CRkPKCLQ.js";
|
||||
import { t as S } from "./directive-DWLihZIi.js";
|
||||
import { a as C, d as w, f as T, o as E } from "./copilot-error-handler-9OpssAH1.js";
|
||||
import { n as D, t as O } from "./copilot-stored-machine-state-D6qB_Peh.js";
|
||||
import { a as k, i as A } from "./copilot-shortcuts-BzZuUtjW.js";
|
||||
import { n as j, t as M } from "./base-panel-Fr0D1ZcU.js";
|
||||
import { n as N, r as P, t as F } from "./lit-renderer-fa_B9boC.js";
|
||||
//#region node_modules/@vaadin/select/src/lit/renderer-directives.js
|
||||
var I, L, R = e((() => {
|
||||
P(), N(), I = class extends F {
|
||||
addRenderer() {
|
||||
this.element.renderer = (e, t) => {
|
||||
this.renderRenderer(e, t);
|
||||
};
|
||||
}
|
||||
runRenderer() {
|
||||
this.element.requestContentUpdate();
|
||||
}
|
||||
removeRenderer() {
|
||||
this.element.renderer = null;
|
||||
}
|
||||
}, L = S(I);
|
||||
})), z = e((() => {
|
||||
R();
|
||||
})), B, V = e((() => {
|
||||
if (_(), B = window.Vaadin.copilot.tree, !B) throw Error("Tried to access copilot tree before it was initialized.");
|
||||
})), H, U;
|
||||
//#endregion
|
||||
e((() => {
|
||||
g(), s(), a(), i(), w(), z(), A(), V(), O(), y(), j(), t(), u(), E(), x(), h(), H = class extends M {
|
||||
constructor(...e) {
|
||||
super(...e), this.selectedTab = 0, this.activationShortcutEnabled = D.isActivationShortcut(), this.aiUsage = D.isAIUsageAllowed(), this.sendErrorReportsAllowed = D.isSendErrorReportsAllowed(), this.hideCopilotRequestOngoing = !1, this.hideCopilotDialogVisible = !1, this.sizeItems = [{
|
||||
label: "Default",
|
||||
value: "default"
|
||||
}, {
|
||||
label: "Compact",
|
||||
value: "compact"
|
||||
}], this.themeItems = [
|
||||
{
|
||||
label: "System",
|
||||
value: "system"
|
||||
},
|
||||
{
|
||||
label: "Light",
|
||||
value: "light"
|
||||
},
|
||||
{
|
||||
label: "Dark",
|
||||
value: "dark"
|
||||
}
|
||||
], this.toolbarExpandModeItems = [
|
||||
{
|
||||
label: "Proximity",
|
||||
value: "proximity",
|
||||
description: "The toolbar expands and becomes fully visible as the mouse pointer approaches it."
|
||||
},
|
||||
{
|
||||
label: "Click",
|
||||
value: "click",
|
||||
description: "The toolbar expands and becomes fully visible when Play mode is clicked."
|
||||
},
|
||||
{
|
||||
label: "Hover",
|
||||
value: "hover",
|
||||
description: "The toolbar expands and becomes fully visible when the mouse hovers over it."
|
||||
},
|
||||
{
|
||||
label: "Always expanded",
|
||||
value: "always",
|
||||
description: "The toolbar remains fully visible at all times and never collapses or becomes translucent."
|
||||
},
|
||||
{
|
||||
label: "Disabled",
|
||||
value: "never",
|
||||
description: "Only Play mode is visible. Changing Copilot mode is not available, and keyboard shortcuts are disabled."
|
||||
}
|
||||
], this.badgePositionItems = [{
|
||||
label: "Smart",
|
||||
value: "smart",
|
||||
description: "Automatically finds the best position by avoiding overlaps with nearby elements."
|
||||
}, {
|
||||
label: "Static",
|
||||
value: "static",
|
||||
description: "Keeps the badge in a predefined position regardless of surrounding elements."
|
||||
}], this.aiUsageItems = [
|
||||
{
|
||||
label: "Ask each time",
|
||||
value: "ask"
|
||||
},
|
||||
{
|
||||
label: "Allow",
|
||||
value: "yes"
|
||||
},
|
||||
{
|
||||
label: "Deny",
|
||||
value: "no"
|
||||
}
|
||||
], this.aiProviderItems = [{
|
||||
label: "Any region",
|
||||
value: "ANY"
|
||||
}, {
|
||||
label: "EU only",
|
||||
value: "EU_ONLY"
|
||||
}], this.toggleActivationShortcut = () => {
|
||||
this.activationShortcutEnabled = !this.activationShortcutEnabled, D.setActivationShortcut(this.activationShortcutEnabled);
|
||||
}, this.toggleSendErrorReports = () => {
|
||||
this.sendErrorReportsAllowed = !this.sendErrorReportsAllowed, D.setSendErrorReportsAllowed(this.sendErrorReportsAllowed);
|
||||
};
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback(), this.classList.add("flex", "flex-col", "h-full");
|
||||
}
|
||||
updated(e) {
|
||||
super.updated(e);
|
||||
}
|
||||
renderKbd(e) {
|
||||
return T(e.replace(/<kbd([^>]*)class="([^"]*)"/, "<kbd$1class=\"$2 font-sans ms-auto\"").replace(/<kbd(?![^>]*class=)/, "<kbd class=\"font-sans ms-auto\""));
|
||||
}
|
||||
render() {
|
||||
return l`
|
||||
<vaadin-tabs>
|
||||
<vaadin-tab ?selected=${this.selectedTab === 0} @click=${() => this.selectedTab = 0}>General</vaadin-tab>
|
||||
<vaadin-tab ?selected=${this.selectedTab === 1} @click=${() => this.selectedTab = 1}>Shortcuts</vaadin-tab>
|
||||
<vaadin-tab ?selected=${this.selectedTab === 2} @click=${() => this.selectedTab = 2}>AI</vaadin-tab>
|
||||
</vaadin-tabs>
|
||||
${this.selectedTab === 0 ? this.renderGeneralTab() : null}
|
||||
${this.selectedTab === 1 ? this.renderShortcutsTab() : null} ${this.selectedTab === 2 ? this.renderAiTab() : null}
|
||||
`;
|
||||
}
|
||||
renderGeneralTab() {
|
||||
let e = D.getSelectedSize(), t = D.getSelectedTheme(), n = D.getToolbarExpandMode(), r = D.getBadgePositionMode();
|
||||
return l`
|
||||
<div class="border-dashed flex flex-col flex-grow divide-y pb-4 pt-0.5 px-4" part="general-tab-container">
|
||||
<div class="flex gap-2 items-start justify-between py-2">
|
||||
<label class="py-1.5" id="size">Size</label>
|
||||
<vaadin-select
|
||||
accessible-name-ref="size"
|
||||
class="flex-shrink-0"
|
||||
theme="auto-width no-border"
|
||||
.items="${this.sizeItems}"
|
||||
.value="${e}"
|
||||
@change="${(e) => {
|
||||
D.setSelectedSize(e.target.value);
|
||||
}}"></vaadin-select>
|
||||
</div>
|
||||
<div class="flex gap-2 items-start justify-between py-2">
|
||||
<label class="py-1.5" id="theme">Theme</label>
|
||||
<vaadin-select
|
||||
accessible-name-ref="theme"
|
||||
class="flex-shrink-0"
|
||||
theme="auto-width no-border"
|
||||
.items="${this.themeItems}"
|
||||
.value="${t}"
|
||||
@change="${(e) => {
|
||||
D.setSelectedTheme(e.target.value);
|
||||
}}"></vaadin-select>
|
||||
</div>
|
||||
<div class="flex gap-2 items-start justify-between py-2">
|
||||
<div class="flex flex-col py-1.5">
|
||||
<label id="toolbar-button-expand-mode">Toolbar behavior</label>
|
||||
<span class="text-secondary text-xs">How it appears & expands</span>
|
||||
</div>
|
||||
<vaadin-select
|
||||
accessible-name-ref="toolbar-expand-mode"
|
||||
class="flex-shrink-0"
|
||||
theme="auto-width no-border"
|
||||
.value="${n}"
|
||||
${L(() => l`
|
||||
<vaadin-list-box class="max-w-xs">
|
||||
${this.toolbarExpandModeItems.map((e) => l`
|
||||
<vaadin-item class="items-start" label="${e.label}" value="${e.value}">
|
||||
<span class="flex flex-col gap-0.5">
|
||||
<span>${e.label}</span>
|
||||
<span class="text-secondary text-xs">${e.description}</span>
|
||||
</span>
|
||||
</vaadin-item>
|
||||
`)}
|
||||
</vaadin-list-box>
|
||||
`)}
|
||||
@change="${(e) => {
|
||||
let t = D.getToolbarExpandMode();
|
||||
D.setToolbarExpandMode(e.target.value), b("toolbar-expand-mode-change", {
|
||||
selected: D.getToolbarExpandMode(),
|
||||
previous: t
|
||||
});
|
||||
}}"></vaadin-select>
|
||||
</div>
|
||||
<div class="flex gap-2 items-start justify-between py-2">
|
||||
<div class="flex flex-col py-1.5">
|
||||
<label id="toolbar-button-expand-mode">Badge positioning</label>
|
||||
<span class="text-secondary text-xs">How it is placed</span>
|
||||
</div>
|
||||
<vaadin-select
|
||||
accessible-name-ref="badge-position-mode"
|
||||
class="flex-shrink-0"
|
||||
theme="auto-width no-border"
|
||||
.value="${r}"
|
||||
${L(() => l`
|
||||
<vaadin-list-box class="max-w-xs">
|
||||
${this.badgePositionItems.map((e) => l`
|
||||
<vaadin-item class="items-start" label="${e.label}" value="${e.value}">
|
||||
<span class="flex flex-col gap-0.5">
|
||||
<span>${e.label}</span>
|
||||
<span class="text-secondary text-xs">${e.description}</span>
|
||||
</span>
|
||||
</vaadin-item>
|
||||
`)}
|
||||
</vaadin-list-box>
|
||||
`)}
|
||||
@change="${(e) => {
|
||||
let t = D.getBadgePositionMode();
|
||||
D.setBadgePositionMode(e.target.value), b("badge-position-mode-changed", {
|
||||
selected: D.getBadgePositionMode(),
|
||||
previous: t
|
||||
});
|
||||
}}"></vaadin-select>
|
||||
</div>
|
||||
<div class="flex gap-2 justify-between mb-4 py-3.5">
|
||||
<div class="flex flex-col">
|
||||
<label id="error-reports-label">Send error reports</label>
|
||||
<span id="error-reports-desc" class="text-secondary text-xs">Helps us improve the user experience</span>
|
||||
</div>
|
||||
<button
|
||||
aria-checked="${this.sendErrorReportsAllowed}"
|
||||
aria-labelledby="error-reports-label"
|
||||
aria-describedby="error-reports-desc"
|
||||
class="my-px"
|
||||
role="switch"
|
||||
type="button"
|
||||
@click=${() => this.toggleSendErrorReports()}>
|
||||
<span></span>
|
||||
</button>
|
||||
</div>
|
||||
<vaadin-button
|
||||
data-test-id="hide-copilot-btn"
|
||||
@click="${this.handleHideCopilotButtonClick}"
|
||||
class="justify-start mt-auto self-start">
|
||||
<vaadin-icon slot="prefix" .svg="${c.close}"></vaadin-icon>
|
||||
Hide Copilot until server restart
|
||||
</vaadin-button>
|
||||
</div>
|
||||
|
||||
<vaadin-confirm-dialog
|
||||
id="hideCopilotDialog"
|
||||
header="Temporarily Hide Copilot"
|
||||
.confirmText=${this.hideCopilotRequestOngoing ? "Hiding…" : "Continue"}
|
||||
cancel-text="Cancel"
|
||||
cancel-button-visible
|
||||
confirm-theme="primary"
|
||||
.confirmDisabled=${this.hideCopilotRequestOngoing}
|
||||
.cancelDisabled=${this.hideCopilotRequestOngoing}
|
||||
.noCloseOnEsc=${this.hideCopilotRequestOngoing}
|
||||
.opened="${this.hideCopilotDialogVisible}"
|
||||
.noCloseOnOutsideClick=${this.hideCopilotRequestOngoing}
|
||||
@cancel=${() => {
|
||||
this.hideCopilotDialogVisible = !1;
|
||||
}}
|
||||
@confirm=${this.onDisableConfirm}>
|
||||
This will hide the Copilot until the server restarts. The page will reload to apply the change. Do you want to
|
||||
continue?
|
||||
${this.hideCopilotRequestOngoing ? l`
|
||||
<div style="display:flex; align-items:center; gap:var(--lumo-space-s); margin-top:var(--lumo-space-m);">
|
||||
<vaadin-progress-indicator indeterminate></vaadin-progress-indicator>
|
||||
<span>Hiding…</span>
|
||||
</div>
|
||||
` : null}
|
||||
</vaadin-confirm-dialog>
|
||||
`;
|
||||
}
|
||||
renderShortcutsTab() {
|
||||
let e = B.hasFlowComponents();
|
||||
return l`<div class="flex flex-col gap-4 pb-2 pt-4 px-4 ">
|
||||
<div class="flex justify-between">
|
||||
<div class="flex flex-col">
|
||||
<label id="enable-shortcuts-label">Enable keyboard shortcut</label>
|
||||
<span id="enable-shortcuts-desc" class="text-secondary text-xs"
|
||||
>Switch anytime to Play mode with ${this.renderKbd(k.toggleCopilot)}</span
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
aria-checked="${this.activationShortcutEnabled}"
|
||||
aria-labelledby="enable-shortcuts-label"
|
||||
aria-describedby="enable-shortcuts-desc"
|
||||
class="my-px"
|
||||
role="switch"
|
||||
type="button"
|
||||
@click=${() => this.toggleActivationShortcut()}>
|
||||
<span></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<h3 class="font-semibold my-0 text-sm">Global</h3>
|
||||
<ul class="border-dashed divide-y flex flex-col list-none m-0 p-0">
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.vaadin}"></vaadin-icon>
|
||||
<span>Switch Play Mode / Last Mode</span>
|
||||
${this.renderKbd(k.toggleCopilot)}
|
||||
</li>
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.undo}"></vaadin-icon>
|
||||
<span>Undo</span>
|
||||
${this.renderKbd(k.undo)}
|
||||
</li>
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.redo}"></vaadin-icon>
|
||||
<span>Redo</span>
|
||||
${this.renderKbd(k.redo)}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<h3 class="font-semibold my-0 text-sm">Component Selection</h3>
|
||||
<ul class="border-dashed divide-y flex flex-col list-none m-0 p-0">
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.sparkles}"></vaadin-icon>
|
||||
<span>Open AI prompt</span>
|
||||
${this.renderKbd(k.openAiPopover)}
|
||||
</li>
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.code}"></vaadin-icon>
|
||||
<span>Go to source</span>
|
||||
${this.renderKbd(k.goToSource)}
|
||||
</li>
|
||||
${e ? l`<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.code}"></vaadin-icon>
|
||||
<span>Go to attach source</span>
|
||||
${this.renderKbd(k.goToAttachSource)}
|
||||
</li>` : o}
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.contentCopy}"></vaadin-icon>
|
||||
<span>Copy</span>
|
||||
${this.renderKbd(k.copy)}
|
||||
</li>
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.contentPaste}"></vaadin-icon>
|
||||
<span>Paste</span>
|
||||
${this.renderKbd(k.paste)}
|
||||
</li>
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.fileCopy}"></vaadin-icon>
|
||||
<span>Duplicate</span>
|
||||
${this.renderKbd(k.duplicate)}
|
||||
</li>
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.turnLeft}"></vaadin-icon>
|
||||
<span>Select parent</span>
|
||||
${this.renderKbd(k.selectParent)}
|
||||
</li>
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.north}"></vaadin-icon>
|
||||
<span>Select previous sibling</span>
|
||||
${this.renderKbd(k.selectPreviousSibling)}
|
||||
</li>
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.south}"></vaadin-icon>
|
||||
<span>Select first child / next sibling</span>
|
||||
${this.renderKbd(k.selectNextSibling)}
|
||||
</li>
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.delete}"></vaadin-icon>
|
||||
<span>Delete</span>
|
||||
${this.renderKbd(k.delete)}
|
||||
</li>
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${c.dashboardCustomize}"></vaadin-icon>
|
||||
<span>Add component</span>
|
||||
<kbd class="font-sans ms-auto">A – Z</kbd>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
renderAiTab() {
|
||||
let e = v.userInfo?.copilotProjectCannotLeaveLocalhost ?? !1, t = e ? "no" : this.aiUsage, n = v.userInfo?.copilotProjectCannotLeaveEU ? "EU_ONLY" : "ANY";
|
||||
return l`<div class="border-dashed flex flex-col divide-y px-4 py-0.5">
|
||||
<div class="flex gap-2 items-start justify-between py-2">
|
||||
<div class="flex flex-col py-1.5">
|
||||
<label id="ai-usage">AI usage</label>
|
||||
<span class="text-secondary text-xs">All AI features are clearly labelled </span>
|
||||
${e ? l`<span class="text-secondary text-xs"
|
||||
>Restricted for your account. Ask your Vaadin account manager to change it.</span
|
||||
>` : o}
|
||||
</div>
|
||||
<vaadin-select
|
||||
accessible-name-ref="ai-usage"
|
||||
class="flex-shrink-0"
|
||||
theme="auto-width no-border"
|
||||
.items="${this.aiUsageItems}"
|
||||
.value="${t}"
|
||||
?disabled="${e}"
|
||||
@value-changed="${(t) => {
|
||||
e || (this.aiUsage = t.detail.value, D.setAIUsageAllowed(t.detail.value));
|
||||
}}"></vaadin-select>
|
||||
</div>
|
||||
<div class="flex gap-2 items-start justify-between py-2">
|
||||
<div class="flex flex-col py-1.5">
|
||||
<label id="ai-provider">AI provider</label>
|
||||
<span class="text-secondary text-xs">Restricted at account level, contact Vaadin to modify it.</span>
|
||||
</div>
|
||||
<vaadin-select
|
||||
accessible-name-ref="ai-provider"
|
||||
class="flex-shrink-0"
|
||||
theme="auto-width no-border"
|
||||
.items="${this.aiProviderItems}"
|
||||
.value="${n}"
|
||||
disabled></vaadin-select>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
handleHideCopilotButtonClick() {
|
||||
this.hideCopilotDialogVisible = !0;
|
||||
}
|
||||
onDisableConfirm() {
|
||||
this.hideCopilotRequestOngoing = !0, r(`${d}hide-copilot`, {}, (e) => {
|
||||
C(e.data, {}) || (this.hideCopilotRequestOngoing = !1, window.location.reload());
|
||||
});
|
||||
}
|
||||
}, f([m()], H.prototype, "selectedTab", void 0), f([m()], H.prototype, "activationShortcutEnabled", void 0), f([m()], H.prototype, "aiUsage", void 0), f([m()], H.prototype, "sendErrorReportsAllowed", void 0), f([m()], H.prototype, "hideCopilotRequestOngoing", void 0), f([m()], H.prototype, "hideCopilotDialogVisible", void 0), H = f([p("copilot-settings-panel")], H), U = {
|
||||
header: "Settings",
|
||||
tag: n.SETTINGS
|
||||
}, window.Vaadin.copilot.plugins.push({ init(e) {
|
||||
e.addPanel(U);
|
||||
} });
|
||||
}))();
|
||||
export { H as CopilotSettingsPanel, U as panelConfig };
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { a as t, c as n, d as r, n as i, o as a, t as o } from "./dom-utils-Cuv93-tQ.js";
|
||||
import { a as s, i as c, n as l, r as u } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { i as d, n as f } from "./copilot-modes-wJyMqHUb.js";
|
||||
import { n as p, t as m } from "./copilot-stored-machine-state-D6qB_Peh.js";
|
||||
import { n as h, t as g } from "./track-active-mode-event-DkX0nsC6.js";
|
||||
//#region frontend/copilot/shared/os-utils.ts
|
||||
function _() {
|
||||
let e = window.navigator.userAgent;
|
||||
return e.indexOf("Windows") === -1 ? e.indexOf("Mac") === -1 ? e.indexOf("Linux") === -1 ? null : "Linux" : "Mac" : "Windows";
|
||||
}
|
||||
function v() {
|
||||
return _() === "Mac";
|
||||
}
|
||||
function y() {
|
||||
return v() ? "⌘" : "Ctrl";
|
||||
}
|
||||
var b = e((() => {}));
|
||||
//#endregion
|
||||
//#region frontend/copilot/copilot-shortcuts.ts
|
||||
function x(e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === "c" && !e.shiftKey) {
|
||||
let e = document.querySelector("copilot-main")?.shadowRoot, t;
|
||||
if (t = typeof e?.getSelection == "function" ? e?.getSelection() : document.getSelection() ?? void 0, t && t.rangeCount === 1) {
|
||||
let e = t.getRangeAt(0).commonAncestorContainer;
|
||||
if (e.nodeType === Node.TEXT_NODE) return r(e);
|
||||
}
|
||||
}
|
||||
return !1;
|
||||
}
|
||||
function S(e) {
|
||||
let t = i(e, "vaadin-context-menu-overlay");
|
||||
if (!t) return !1;
|
||||
let n = t.owner;
|
||||
return n ? !!i(n, "copilot-component-overlay") : !1;
|
||||
}
|
||||
function C() {
|
||||
return l.idePluginState?.supportedActions?.find((e) => e === "undo");
|
||||
}
|
||||
function w(e) {
|
||||
let t = e;
|
||||
if (o(e)) return !0;
|
||||
let n = a(t);
|
||||
for (let e of n) if (o(e)) return !0;
|
||||
return !1;
|
||||
}
|
||||
var T, E, D, O, k, A, j, M, N, P, F = e((() => {
|
||||
s(), u(), b(), m(), n(), d(), g(), T = !1, E = 0, D = (e) => {
|
||||
if (p.isActivationShortcut() && p.getToolbarExpandMode() !== "never") if (e.key === "Shift" && !e.ctrlKey && !e.altKey && !e.metaKey) T = !0;
|
||||
else if (T && e.shiftKey && (e.key === "Control" || e.key === "Meta")) {
|
||||
if (E++, E === 2) return l.activeMode === "play" ? l.lastNonPlayMode === void 0 ? l.setActiveMode("edit", !0) : l.setActiveMode(l.lastNonPlayMode, !0) : l.setActiveMode("play", !0), h(), E = 0, !0;
|
||||
setTimeout(() => {
|
||||
E = 0;
|
||||
}, 500);
|
||||
} else E = 0;
|
||||
return !1;
|
||||
}, O = (e) => {
|
||||
if (D(e)) {
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
if (f()?.appInteractable) return;
|
||||
let n = t();
|
||||
if (!n) return;
|
||||
let r = S(n), a = i(n, "vaadin-dialog") ?? (n.localName === "vaadin-dialog" ? n : null), o = a !== null && a.hasAttribute("panel-container"), s = n.localName === "copilot-main", u = i(n, "copilot-outline-panel") !== null, d = i(n, "copilot-toolbar") !== null;
|
||||
if (!s && !r && e.key !== "Escape" && !u && !d) {
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
let p = !0, m = !1;
|
||||
if (x(e)) p = !1;
|
||||
else if (e.key === "Escape") {
|
||||
if (l.loginCheckActive && l.setLoginCheckActive(!1), w(n)) {
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
c.emit("escape-key-pressed", { event: e });
|
||||
} else j(e) && l.activeMode === "edit" && (!o || u) ? (c.emit("delete-selected", {}), m = !0) : (e.ctrlKey || e.metaKey) && e.key === "d" && l.activeMode === "edit" && (!o || u) ? (c.emit("duplicate-selected", {}), m = !0) : (e.ctrlKey || e.metaKey) && e.key === "b" && (!o || u) ? (c.emit("show-selected-in-ide", { attach: e.shiftKey }), m = !0) : (e.ctrlKey || e.metaKey) && e.key === "z" && C() && (!o || u) ? (c.emit("undoRedo", { undo: !e.shiftKey }), m = !0) : x(e) || c.emit("keyboard-event", { event: e });
|
||||
l.setMultiSelectionOn(A(e)), p && e.stopPropagation(), m && e.preventDefault();
|
||||
}, k = (e) => {
|
||||
f()?.appInteractable || t() && A(e) && l.setMultiSelectionOn(!1);
|
||||
}, A = (e) => (e.key === "Control" || e.key === "Meta") && !e.shiftKey && !e.altKey, j = (e) => (e.key === "Backspace" || e.key === "Delete") && !e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey, M = y(), N = "⇧", P = {
|
||||
toggleCopilot: `<kbd>${N} + ${M} ${M}</kbd>`,
|
||||
openAiPopover: `<kbd>${N} + Space</kbd>`,
|
||||
undo: `<kbd>${M} + Z</kbd>`,
|
||||
redo: `<kbd>${M} + ${N} + Z</kbd>`,
|
||||
duplicate: `<kbd>${M} + D</kbd>`,
|
||||
goToSource: `<kbd>${M} + B</kbd>`,
|
||||
goToAttachSource: `<kbd>${M} + ${N} + B</kbd>`,
|
||||
selectParent: "<kbd>←</kbd>",
|
||||
selectPreviousSibling: "<kbd>↑</kbd>",
|
||||
selectNextSibling: "<kbd>↓</kbd>",
|
||||
delete: "<kbd>DEL</kbd>",
|
||||
copy: `<kbd>${M} + C</kbd>`,
|
||||
paste: `<kbd>${M} + V</kbd>`
|
||||
};
|
||||
}));
|
||||
//#endregion
|
||||
export { P as a, F as i, k as n, O as r, D as t };
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
//#region frontend/copilot/shared/copilot-stored-machine-state.ts
|
||||
var t, n = e((() => {
|
||||
if (t = window.Vaadin.copilot._machineState, !t) throw Error("Trying to use stored machine state before it was initialized");
|
||||
}));
|
||||
//#endregion
|
||||
export { t as n, n as t };
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { $ as t, B as n, F as r, G as i, H as a, I as o, J as s, L as c, N as l, R as u, U as d, V as f, W as p, X as m, Y as h, Z as g, et as _, j as v, k as y } from "./icons-CwakCZgK.js";
|
||||
import { c as b, h as x, l as S, m as C, s as w, u as T } from "./dom-utils-Cuv93-tQ.js";
|
||||
import { a as E, i as D } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { i as O, o as k, r as A } from "./copilot-error-handler-9OpssAH1.js";
|
||||
import { n as j, r as M } from "./copilot-notification-CCNJdNg4.js";
|
||||
//#region frontend/copilot/shared/copilot-storage.ts
|
||||
var N, P, F = e((() => {
|
||||
N = "copilot-conf", P = class {
|
||||
static get sessionConfiguration() {
|
||||
let e = sessionStorage.getItem(N);
|
||||
return e ? JSON.parse(e) : {};
|
||||
}
|
||||
static saveCopilotActiveMode(e, t) {
|
||||
let n = this.sessionConfiguration;
|
||||
n.activeMode = e, n.lastNonPlayMode = t, this.persist(n);
|
||||
}
|
||||
static getCopilotActiveMode() {
|
||||
return this.sessionConfiguration.activeMode;
|
||||
}
|
||||
static getCopilotLastNonPlayMode() {
|
||||
return this.sessionConfiguration.lastNonPlayMode;
|
||||
}
|
||||
static savePanelConfigurations(e) {
|
||||
let t = this.sessionConfiguration;
|
||||
t.sectionPanelState = e, this.persist(t);
|
||||
}
|
||||
static getPanelConfigurations() {
|
||||
return this.sessionConfiguration.sectionPanelState;
|
||||
}
|
||||
static persist(e) {
|
||||
sessionStorage.setItem(N, JSON.stringify(e));
|
||||
}
|
||||
static savePrompts(e) {
|
||||
let t = this.sessionConfiguration;
|
||||
t.prompts = e, this.persist(t);
|
||||
}
|
||||
static getPrompts() {
|
||||
return this.sessionConfiguration.prompts || [];
|
||||
}
|
||||
static saveCurrentSelection(e) {
|
||||
let t = this.sessionConfiguration;
|
||||
t.selection = t.selection ?? {}, t.selection && (t.selection.current = e, t.selection.location = window.location.pathname, this.persist(t));
|
||||
}
|
||||
static savePendingSelection(e) {
|
||||
let t = this.sessionConfiguration;
|
||||
t.selection = t.selection ?? {}, t.selection && (t.selection.pending = e, t.selection.location = window.location.pathname, this.persist(t));
|
||||
}
|
||||
static getCurrentSelection() {
|
||||
let e = this.sessionConfiguration.selection;
|
||||
if (e?.location === window.location.pathname) return e.current;
|
||||
}
|
||||
static getPendingSelection() {
|
||||
let e = this.sessionConfiguration.selection;
|
||||
if (e?.location === window.location.pathname) return e.pending;
|
||||
}
|
||||
static saveDrillDownContextReference(e) {
|
||||
let t = this.sessionConfiguration;
|
||||
t.drillDownContext = t.drillDownContext ?? {}, t.drillDownContext && (t.drillDownContext.location = window.location.pathname, t.drillDownContext.stack = e, this.persist(t));
|
||||
}
|
||||
static getDrillDownContextReference() {
|
||||
let e = this.sessionConfiguration;
|
||||
if (e?.drillDownContext?.location === window.location.pathname) return e.drillDownContext?.stack;
|
||||
}
|
||||
static savePanelTagsState(e, t) {
|
||||
let n = this.sessionConfiguration;
|
||||
n.openPanelTags = Array.from(e), n.switchModeClosedPanelTags = Array.from(t), this.persist(n);
|
||||
}
|
||||
static getOpenPanelTags() {
|
||||
let e = this.sessionConfiguration.openPanelTags;
|
||||
return e ? new Set(e) : /* @__PURE__ */ new Set();
|
||||
}
|
||||
static getSwitchModeClosedPanelTags() {
|
||||
let e = this.sessionConfiguration.switchModeClosedPanelTags;
|
||||
return e ? new Set(e) : /* @__PURE__ */ new Set();
|
||||
}
|
||||
static saveCustomPanelTags(e) {
|
||||
let t = this.sessionConfiguration;
|
||||
t.customPanelTags = Object.fromEntries(e), this.persist(t);
|
||||
}
|
||||
static getCustomPanelTags() {
|
||||
let e = this.sessionConfiguration.customPanelTags;
|
||||
return e ? new Map(Object.entries(e)) : /* @__PURE__ */ new Map();
|
||||
}
|
||||
static savePositionUpdatedManuallyPanelTags(e) {
|
||||
let t = this.sessionConfiguration;
|
||||
t.positionUpdatedManuallyPanelTags = Array.from(e), this.persist(t);
|
||||
}
|
||||
static getPositionUpdatedManuallyPanelTags() {
|
||||
let e = this.sessionConfiguration.positionUpdatedManuallyPanelTags;
|
||||
return e ? new Set(e) : /* @__PURE__ */ new Set();
|
||||
}
|
||||
static savePanelStackingOrder(e) {
|
||||
let t = this.sessionConfiguration;
|
||||
t.panelStackingOrder = e, this.persist(t);
|
||||
}
|
||||
static getPanelStackingOrder() {
|
||||
return this.sessionConfiguration.panelStackingOrder ?? [];
|
||||
}
|
||||
static saveToolbarPosition(e, t) {
|
||||
let n = this.sessionConfiguration;
|
||||
n.toolbarPosition = {
|
||||
right: e,
|
||||
top: t
|
||||
}, this.persist(n);
|
||||
}
|
||||
static getToolbarPosition() {
|
||||
return this.sessionConfiguration.toolbarPosition;
|
||||
}
|
||||
};
|
||||
}));
|
||||
//#endregion
|
||||
//#region node_modules/uuid/dist/stringify.js
|
||||
function ee(e, t = 0) {
|
||||
return (I[e[t + 0]] + I[e[t + 1]] + I[e[t + 2]] + I[e[t + 3]] + "-" + I[e[t + 4]] + I[e[t + 5]] + "-" + I[e[t + 6]] + I[e[t + 7]] + "-" + I[e[t + 8]] + I[e[t + 9]] + "-" + I[e[t + 10]] + I[e[t + 11]] + I[e[t + 12]] + I[e[t + 13]] + I[e[t + 14]] + I[e[t + 15]]).toLowerCase();
|
||||
}
|
||||
var I, L = e((() => {
|
||||
I = [];
|
||||
for (let e = 0; e < 256; ++e) I.push((e + 256).toString(16).slice(1));
|
||||
}));
|
||||
//#endregion
|
||||
//#region node_modules/uuid/dist/rng.js
|
||||
function R() {
|
||||
if (!z) {
|
||||
if (typeof crypto > "u" || !crypto.getRandomValues) throw Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
|
||||
z = crypto.getRandomValues.bind(crypto);
|
||||
}
|
||||
return z(B);
|
||||
}
|
||||
var z, B, V = e((() => {
|
||||
B = new Uint8Array(16);
|
||||
})), H, U, W = e((() => {
|
||||
H = typeof crypto < "u" && crypto.randomUUID && crypto.randomUUID.bind(crypto), U = { randomUUID: H };
|
||||
}));
|
||||
//#endregion
|
||||
//#region node_modules/uuid/dist/v4.js
|
||||
function G(e, t, n) {
|
||||
e ||= {};
|
||||
let r = e.random ?? e.rng?.() ?? R();
|
||||
if (r.length < 16) throw Error("Random bytes length must be >= 16");
|
||||
if (r[6] = r[6] & 15 | 64, r[8] = r[8] & 63 | 128, t) {
|
||||
if (n ||= 0, n < 0 || n + 16 > t.length) throw RangeError(`UUID byte range ${n}:${n + 15} is out of buffer bounds`);
|
||||
for (let e = 0; e < 16; ++e) t[n + e] = r[e];
|
||||
return t;
|
||||
}
|
||||
return ee(r);
|
||||
}
|
||||
function K(e, t, n) {
|
||||
return U.randomUUID && !t && !e ? U.randomUUID() : G(e, t, n);
|
||||
}
|
||||
var q = e((() => {
|
||||
W(), V(), L();
|
||||
})), J = e((() => {
|
||||
L(), V(), q();
|
||||
}));
|
||||
//#endregion
|
||||
//#region frontend/copilot/copilot-tree-impl.ts
|
||||
function Y(e) {
|
||||
return `${e.uiId}#${e.nodeId}`;
|
||||
}
|
||||
function X(e) {
|
||||
if (!e.parent) return e.name;
|
||||
let t = 0;
|
||||
for (let n = 0; n < e.siblingIndex + 1; n++) e.parent.children[n].name === e.name && t++;
|
||||
return `${e.parent.path} > ${e.name}[${t}]`;
|
||||
}
|
||||
function Z(e, t) {
|
||||
return t ? `${e} "${t}"` : e;
|
||||
}
|
||||
var Q, $, te = e((() => {
|
||||
s(), J(), l(), b(), k(), c(), j(), _(), E(), Q = /* @__PURE__ */ new WeakMap(), $ = class {
|
||||
constructor() {
|
||||
this.root = null, this.nodeUuidNodeMapFlat = /* @__PURE__ */ new Map(), this.aborted = !1, this._hasFlowComponent = !1, this.flowNodesInSource = {}, this.flowCustomComponentData = {}, this.hillaCustomComponentData = {}, this.componentDragDropApiInfosMap = {}, this.waitForHillaCustomComponentResponseData = () => new Promise((e) => {
|
||||
let t = setTimeout(() => {
|
||||
console.warn("Timed out waiting for hilla custom component data; continuing without it"), e({});
|
||||
}, 1e4);
|
||||
D.emit("request-hilla-custom-component-data-with-callback", {
|
||||
tree: this,
|
||||
callback: (n) => {
|
||||
clearTimeout(t), e(n);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
async init() {
|
||||
let e = f();
|
||||
if (e) {
|
||||
let t = await this.addToTree(e);
|
||||
t && this.root?.abstractRootNode && this.root.children.length === 1 && (this.root = this.root.children[0]), t && (await this.addOverlayContentToTreeIfExists("vaadin-popover[opened]"), await this.addOverlayContentToTreeIfExists("vaadin-dialog[opened]")), this.hillaCustomComponentData = await this.waitForHillaCustomComponentResponseData();
|
||||
}
|
||||
}
|
||||
getChildren(e) {
|
||||
return this.nodeUuidNodeMapFlat.get(e)?.children ?? [];
|
||||
}
|
||||
get allNodesFlat() {
|
||||
return Array.from(this.nodeUuidNodeMapFlat.values());
|
||||
}
|
||||
getNodeOfElement(e) {
|
||||
if (e) return this.allNodesFlat.find((t) => t.element === e);
|
||||
}
|
||||
async handleRouteContainers(e, t) {
|
||||
let n = S(e);
|
||||
if (!n && r(e)) {
|
||||
let n = p(e);
|
||||
if (n && n.nextElementSibling) return await this.addToTree(n.nextElementSibling, t), !0;
|
||||
}
|
||||
if (n && e.localName === "react-router-outlet") {
|
||||
for (let n of Array.from(e.children)) {
|
||||
let e = i(n);
|
||||
e && await this.addToTree(e, t);
|
||||
}
|
||||
return !0;
|
||||
}
|
||||
return !1;
|
||||
}
|
||||
includeReactNode(e) {
|
||||
return d(e) === "PreconfiguredAuthProvider" || d(e) === "RouterProvider" ? !1 : g(e) || m(e);
|
||||
}
|
||||
async includeFlowNode(e) {
|
||||
if (o(e)) return !1;
|
||||
let t = y(e);
|
||||
return t && this.nodeUuidNodeMapFlat.has(Y(t)) ? !1 : this.isInitializedInProjectSources(e);
|
||||
}
|
||||
async isInitializedInProjectSources(e) {
|
||||
let n = y(e);
|
||||
if (!n) return !1;
|
||||
let { nodeId: r, uiId: i } = n;
|
||||
if (!this.flowNodesInSource[i]) {
|
||||
let e = await u("copilot-get-component-source-info", { uiId: i }, (e) => e.data);
|
||||
e.error && O("Failed to get component source info", e.error), e.suggestRestart && M({
|
||||
type: t.WARNING,
|
||||
message: "Route view deleted",
|
||||
details: A(),
|
||||
dismissId: "view-deleted-restart-required",
|
||||
delay: 3e4
|
||||
}), this.flowCustomComponentData[i] = e.customComponentResponse, this.flowNodesInSource[i] = new Set(e.nodeIdsInProject), this.componentDragDropApiInfosMap[i] = e.dragDropApiInfos;
|
||||
}
|
||||
return this.flowNodesInSource[i].has(r);
|
||||
}
|
||||
async addToTree(e, n) {
|
||||
if (this.isAborted()) return !1;
|
||||
let r = await this.handleRouteContainers(e, n);
|
||||
if (r) return r;
|
||||
let i = S(e), o;
|
||||
if (!i) this.includeReactNode(e) && (o = this.generateNodeFromFiber(e, n));
|
||||
else if (await this.includeFlowNode(e)) {
|
||||
let t = this.generateNodeFromFlow(e, n);
|
||||
if (!t) return !1;
|
||||
this._hasFlowComponent = !0, o = t;
|
||||
}
|
||||
if (n) o && (o.parent = n, n.children ||= [], n.children.push(o));
|
||||
else {
|
||||
if (!o) {
|
||||
if (!(e instanceof Element) && h(e)) return M({
|
||||
type: t.WARNING,
|
||||
message: "Copilot is partly usable",
|
||||
details: `${d(e)} should be a function component to make Copilot work properly`,
|
||||
dismissId: "react_route_component_is_class"
|
||||
}), !1;
|
||||
if (o = i ? this.generateNodeFromFlow(e) : this.generateNodeFromFiber(e), !o) return O("Unable to add node", /* @__PURE__ */ Error("Tree root node is undefined")), !1;
|
||||
o.abstractRootNode = !0;
|
||||
}
|
||||
this.root = o;
|
||||
}
|
||||
o && this.nodeUuidNodeMapFlat.set(o.uuid, o);
|
||||
let s = o ?? n, c = i ? Array.from(e.children) : a(e);
|
||||
for (let e of c) await this.addToTree(e, s);
|
||||
return o !== void 0;
|
||||
}
|
||||
generateNodeFromFiber(e, t) {
|
||||
let n = g(e) ? p(e) : void 0, r = t?.children.length ?? 0, i = this, a;
|
||||
if (!(n && (a = T(n), a === "parent"))) return {
|
||||
node: e,
|
||||
parent: t,
|
||||
element: n,
|
||||
depth: t && t.depth + 1 || 0,
|
||||
children: [],
|
||||
siblingIndex: r,
|
||||
isFlowComponent: !1,
|
||||
isReactComponent: !0,
|
||||
isLitTemplate: !1,
|
||||
zeroSize: n ? C(n) : void 0,
|
||||
get uuid() {
|
||||
if (Q.has(e)) return Q.get(e);
|
||||
if (e.alternate && Q.has(e.alternate)) return Q.get(e.alternate);
|
||||
let t = K();
|
||||
return Q.set(e, t), t;
|
||||
},
|
||||
get name() {
|
||||
return x(d(e));
|
||||
},
|
||||
get identifier() {
|
||||
return w(n);
|
||||
},
|
||||
get nameAndIdentifier() {
|
||||
return Z(this.name, this.identifier);
|
||||
},
|
||||
get previousSibling() {
|
||||
if (r !== 0) return t?.children[r - 1];
|
||||
},
|
||||
get nextSibling() {
|
||||
if (!(t === void 0 || r === t.children.length - 1)) return t.children[r + 1];
|
||||
},
|
||||
get path() {
|
||||
return X(this);
|
||||
},
|
||||
get customComponentData() {
|
||||
if (i.hillaCustomComponentData[this.uuid]) return i.hillaCustomComponentData[this.uuid];
|
||||
},
|
||||
get selfHiddenElement() {
|
||||
return a === "self";
|
||||
}
|
||||
};
|
||||
}
|
||||
generateNodeFromFlow(e, t) {
|
||||
let n = y(e);
|
||||
if (!n) return;
|
||||
let r = T(e);
|
||||
if (!n.hiddenByServer && r === "parent") return;
|
||||
let i = t?.children.length ?? 0, a = this.flowCustomComponentData, o = this.componentDragDropApiInfosMap;
|
||||
return {
|
||||
node: n,
|
||||
parent: t,
|
||||
element: e,
|
||||
depth: t && t.depth + 1 || 0,
|
||||
children: [],
|
||||
siblingIndex: i,
|
||||
get uuid() {
|
||||
return Y(n);
|
||||
},
|
||||
isFlowComponent: !0,
|
||||
isReactComponent: !1,
|
||||
get isLitTemplate() {
|
||||
return !!this.customComponentData?.litTemplate;
|
||||
},
|
||||
zeroSize: e ? C(e) : void 0,
|
||||
get name() {
|
||||
return v(n) ?? x(n.element.localName);
|
||||
},
|
||||
get identifier() {
|
||||
return w(e);
|
||||
},
|
||||
get nameAndIdentifier() {
|
||||
return Z(this.name, this.identifier);
|
||||
},
|
||||
get previousSibling() {
|
||||
if (i !== 0) return t?.children[i - 1];
|
||||
},
|
||||
get nextSibling() {
|
||||
if (!(t === void 0 || i === t.children.length - 1)) return t.children[i + 1];
|
||||
},
|
||||
get path() {
|
||||
return X(this);
|
||||
},
|
||||
get customComponentData() {
|
||||
if (a[n.uiId]) return a[n.uiId].allComponentsInfoForCustomComponentSupport[n.nodeId];
|
||||
},
|
||||
get componentDragDropApiInfo() {
|
||||
if (!o[n.uiId]) return;
|
||||
let e = o[n.uiId];
|
||||
if (e[n.nodeId]) return e[n.nodeId];
|
||||
},
|
||||
get selfHiddenElement() {
|
||||
return r === "self";
|
||||
}
|
||||
};
|
||||
}
|
||||
async addOverlayContentToTreeIfExists(e) {
|
||||
let t = document.body.querySelector(e);
|
||||
if (!t) return;
|
||||
let r = !0;
|
||||
if (!this.getNodeOfElement(t)) {
|
||||
let e = n(i(t));
|
||||
r = await this.addToTree(e ?? t, this.root);
|
||||
}
|
||||
if (r) for (let e of Array.from(t.children)) await this.addToTree(e, this.getNodeOfElement(t));
|
||||
}
|
||||
hasFlowComponents() {
|
||||
return this._hasFlowComponent;
|
||||
}
|
||||
findNodeByUuid(e) {
|
||||
if (e) return this.nodeUuidNodeMapFlat.get(e);
|
||||
}
|
||||
getElementByNodeUuid(e) {
|
||||
return this.findNodeByUuid(e)?.element;
|
||||
}
|
||||
findByTreePath(e) {
|
||||
if (e) return this.allNodesFlat.find((t) => t.path === e);
|
||||
}
|
||||
isAborted() {
|
||||
return this.aborted;
|
||||
}
|
||||
abort() {
|
||||
this.aborted = !0;
|
||||
}
|
||||
get customComponentDataLoaded() {
|
||||
return Object.keys(this.hillaCustomComponentData).length !== 0 || Object.keys(this.flowCustomComponentData).length !== 0;
|
||||
}
|
||||
};
|
||||
}));
|
||||
//#endregion
|
||||
export { P as a, K as i, te as n, F as o, J as r, $ as t };
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
//#region frontend/copilot/shared/copilot-eventbus.ts
|
||||
var t, n = e((() => {
|
||||
if (t = window.Vaadin.copilot.eventbus, !t) throw Error("Tried to access copilot eventbus before it was initialized.");
|
||||
})), r, i, a = e((() => {
|
||||
if (r = {
|
||||
AddEventListener: "Add Event Listener",
|
||||
AI: "AI",
|
||||
Delete: "Delete",
|
||||
DragAndDrop: "Drag and Drop",
|
||||
Duplicate: "Duplicate",
|
||||
SetLabel: "Set label",
|
||||
SetText: "Set text",
|
||||
SetHelper: "Set helper text",
|
||||
SetTitle: "Set title text",
|
||||
WrapWithTag: "Wrapping with tag",
|
||||
Alignment: "Alignment",
|
||||
Padding: "Padding",
|
||||
ModifyComponentSource: "Modify component source",
|
||||
Gap: "Gap",
|
||||
RedoUndo: "Redo/Undo",
|
||||
Sizing: "Sizing",
|
||||
ConnectToService: "ConnectToService",
|
||||
SetStaticData: "SetStaticData",
|
||||
ExtractComponent: "ExtractComponent",
|
||||
SetViewAccessRequirement: "SetViewAccessRequirement"
|
||||
}, i = window.Vaadin.copilot._uiState, !i) throw Error("Tried to access copilot ui state before it was initialized.");
|
||||
}));
|
||||
//#endregion
|
||||
export { n as a, t as i, i as n, a as r, r as t };
|
||||
+68
File diff suppressed because one or more lines are too long
+141
@@ -0,0 +1,141 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
import { $ as t, L as n, R as r, at as i, et as a, n as o, o as s, r as c, t as l, u, ut as d } from "./icons-CwakCZgK.js";
|
||||
import { l as f, o as p } from "./consts-CSALuSsm.js";
|
||||
import { a as m, d as h, i as g, l as _, o as v, r as y, s as b } from "./section-panel-ui-state-hOj_RfX_.js";
|
||||
import { n as x, r as S } from "./copilot-ui-state-Dc6l_5DA.js";
|
||||
import { a as C, c as w, i as T, l as E, o as D, u as O } from "./copilot-error-handler-9OpssAH1.js";
|
||||
import { n as k, t as A } from "./copilot-stored-machine-state-D6qB_Peh.js";
|
||||
import { n as j, r as M } from "./copilot-notification-CCNJdNg4.js";
|
||||
import { n as N, t as P } from "./base-panel-Fr0D1ZcU.js";
|
||||
import { n as F, t as I } from "./copilot-message-box-CVAh5PSs.js";
|
||||
//#region frontend/copilot/plugins/copilot-vaadin-versions/vaadin-version-request.ts
|
||||
function L() {
|
||||
x.setVaadinVersionState({ loading: !0 }), r(`${p}get-new-vaadin-versions`, { includePreReleases: k.getNewVersionPreReleasesVisible() }, (e) => {
|
||||
let t = e.data;
|
||||
if (t.error) {
|
||||
x.setVaadinVersionState({
|
||||
loading: !1,
|
||||
errorMessage: t.error.message,
|
||||
hasError: !0,
|
||||
versions: []
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!t.newVersions) return;
|
||||
let n = JSON.parse(t.newVersions);
|
||||
x.setVaadinVersionState({
|
||||
versions: n,
|
||||
loading: !1,
|
||||
hasError: !1
|
||||
});
|
||||
});
|
||||
}
|
||||
var R = e((() => {
|
||||
n(), f(), A(), S(), i(), d(() => k.getNewVersionPreReleasesVisible(), () => {
|
||||
L();
|
||||
}, { fireImmediately: !0 });
|
||||
})), z, B, V;
|
||||
//#endregion
|
||||
e((() => {
|
||||
b(), a(), c(), N(), S(), g(), o(), A(), n(), f(), j(), I(), R(), D(), w(), v(), z = class extends P {
|
||||
constructor(...e) {
|
||||
super(...e), this.renderIcon = (e) => e.name === "Flow" ? l.flow : e.name === "Hilla" ? l.hilla : e.name === "Web Components" || e.name === "Flow Components" ? l.uiComponents : e.name === "TestBench" ? l.checklist : e.name.indexOf("MPR") === -1 ? e.name.indexOf("AppSec") === -1 ? e.name.indexOf("Collaboration") === -1 ? e.name === "Copilot" || e.name === "CoPilot" ? l.copilot : e.name.indexOf("Kubernetes") === -1 ? e.name.indexOf("Observability") === -1 ? e.name.indexOf("SSO") === -1 ? e.name.indexOf("Modernization") === -1 ? l.code : l.rocketLaunch : l.login : l.visibility : l.kubernetes : l.group : l.verifiedUser : l.playCircle;
|
||||
}
|
||||
render() {
|
||||
return u`
|
||||
<ul class="list-none m-0 pb-2.5 px-2">
|
||||
${this.renderContent()}
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
renderContent() {
|
||||
return x.newVaadinVersionState === void 0 || x.newVaadinVersionState.loading ? F("loading", "Versions are loading...") : x.newVaadinVersionState.hasError ? F("error", x.newVaadinVersionState.errorMessage ?? "Unable to display new versions") : !x.newVaadinVersionState.versions || x.newVaadinVersionState.versions.length === 0 ? F("success", "Vaadin version is up to date") : u`
|
||||
${x.newVaadinVersionState.versions.map((e, t) => this.renderNewVersionItem(e, t === 0))}
|
||||
`;
|
||||
}
|
||||
renderNewVersionItem(e, t) {
|
||||
return u`
|
||||
<li>
|
||||
<vaadin-details ?opened="${t}">
|
||||
<vaadin-details-summary class="relative" slot="summary">
|
||||
<div class="flex gap-2 items-center">
|
||||
${e.version}
|
||||
${t ? u`<span
|
||||
class="bg-blue-3 dark:bg-blue-7 font-normal inline-flex px-1.5 py-px rounded-full text-xs text-blue-11 dark:text-blue-12"
|
||||
>Latest</span
|
||||
>` : s}
|
||||
<vaadin-button
|
||||
aria-label="Update"
|
||||
class="absolute end-0 top-0"
|
||||
theme="icon tertiary"
|
||||
?disabled="${this.updateClickedVersion !== void 0}"
|
||||
@click="${(t) => {
|
||||
t.stopPropagation(), this.sendUpdateRequest(e.version, e.preRelease);
|
||||
}}">
|
||||
<vaadin-icon
|
||||
class="${this.updateClickedVersion === e.version ? "animate-spin" : ""}"
|
||||
.svg="${this.updateClickedVersion === e.version ? l.progressActivity : l.upgrade}"></vaadin-icon>
|
||||
<vaadin-tooltip slot="tooltip" text="Update"></vaadin-tooltip>
|
||||
</vaadin-button>
|
||||
</div>
|
||||
</vaadin-details-summary>
|
||||
<ul class="border-dashed divide-y list-none m-0 pe-2 ps-8">
|
||||
${e.changelogs.map((e) => u`
|
||||
<li class="flex gap-2 py-2">
|
||||
<vaadin-icon .svg="${this.renderIcon(e)}"></vaadin-icon>
|
||||
${e.name}
|
||||
<a href="${e.url}" target="_blank">${e.version}</a>
|
||||
</li>
|
||||
`)}
|
||||
</ul>
|
||||
</vaadin-details>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
sendUpdateRequest(e, n) {
|
||||
this.updateClickedVersion = e;
|
||||
let i = {
|
||||
newVersion: e,
|
||||
preRelease: n
|
||||
};
|
||||
r(`${p}update-vaadin-version`, i, (e) => {
|
||||
this.updateClickedVersion = void 0;
|
||||
let n = !C(e.data, i);
|
||||
if (n) {
|
||||
let e = "Please restart the server";
|
||||
O() && (e = "Server will be restarted by the IDE plugin", E()), M({
|
||||
message: "Version updated",
|
||||
type: t.INFORMATION,
|
||||
details: e
|
||||
});
|
||||
}
|
||||
return n;
|
||||
}).catch((e) => {
|
||||
T("Error updating version", e);
|
||||
});
|
||||
}
|
||||
}, m([_()], z.prototype, "updateClickedVersion", void 0), z = m([h("copilot-vaadin-versions")], z), B = class extends y {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
render() {
|
||||
let e = k.getNewVersionPreReleasesVisible();
|
||||
return u`<vaadin-button
|
||||
aria-pressed="${e}"
|
||||
theme="tertiary"
|
||||
@click="${(e) => {
|
||||
e.stopPropagation(), k.setNewVersionPreReleasesVisible(!k.getNewVersionPreReleasesVisible());
|
||||
}}">
|
||||
<vaadin-icon slot="prefix" .svg="${e ? l.visibility : l.visibilityOff}"></vaadin-icon>
|
||||
Prereleases
|
||||
</vaadin-button>`;
|
||||
}
|
||||
}, B = m([h("copilot-vaadin-versions-actions")], B), V = {
|
||||
header: "Vaadin Versions",
|
||||
tag: "copilot-vaadin-versions",
|
||||
actionsTag: "copilot-vaadin-versions-actions"
|
||||
}, window.Vaadin.copilot.plugins.push({ init(e) {
|
||||
e.addPanel(V);
|
||||
} });
|
||||
}))();
|
||||
export { z as CopilotVaadinVersions, B as CopilotVersionCheckerActions, V as versionCheckerPanel };
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
import { n as e, t } from "./chunk-DiqZc92J.js";
|
||||
import { l as n, s as r } from "./icons-CwakCZgK.js";
|
||||
import { i, n as a, r as o, t as s } from "./directive-DWLihZIi.js";
|
||||
import { a as c, i as l, o as u, r as d, s as f, t as p } from "./directive-helpers-BTt8P8-5.js";
|
||||
//#region node_modules/lit-html/directives/repeat.js
|
||||
var m, h, g = e((() => {
|
||||
n(), o(), d(), m = (e, t, n) => {
|
||||
let r = /* @__PURE__ */ new Map();
|
||||
for (let i = t; i <= n; i++) r.set(e[i], i);
|
||||
return r;
|
||||
}, h = s(class extends a {
|
||||
constructor(e) {
|
||||
if (super(e), e.type !== i.CHILD) throw Error("repeat() can only be used in text expressions");
|
||||
}
|
||||
dt(e, t, n) {
|
||||
let r;
|
||||
n === void 0 ? n = t : t !== void 0 && (r = t);
|
||||
let i = [], a = [], o = 0;
|
||||
for (let t of e) i[o] = r ? r(t, o) : o, a[o] = n(t, o), o++;
|
||||
return {
|
||||
values: a,
|
||||
keys: i
|
||||
};
|
||||
}
|
||||
render(e, t, n) {
|
||||
return this.dt(e, t, n).values;
|
||||
}
|
||||
update(e, [t, n, i]) {
|
||||
let a = c(e), { values: o, keys: s } = this.dt(t, n, i);
|
||||
if (!Array.isArray(a)) return this.ut = s, o;
|
||||
let d = this.ut ??= [], h = [], g, _, v = 0, y = a.length - 1, b = 0, x = o.length - 1;
|
||||
for (; v <= y && b <= x;) if (a[v] === null) v++;
|
||||
else if (a[y] === null) y--;
|
||||
else if (d[v] === s[b]) h[b] = f(a[v], o[b]), v++, b++;
|
||||
else if (d[y] === s[x]) h[x] = f(a[y], o[x]), y--, x--;
|
||||
else if (d[v] === s[x]) h[x] = f(a[v], o[x]), u(e, h[x + 1], a[v]), v++, x--;
|
||||
else if (d[y] === s[b]) h[b] = f(a[y], o[b]), u(e, a[v], a[y]), y--, b++;
|
||||
else if (g === void 0 && (g = m(s, b, x), _ = m(d, v, y)), g.has(d[v])) if (g.has(d[y])) {
|
||||
let t = _.get(s[b]), n = t === void 0 ? null : a[t];
|
||||
if (n === null) {
|
||||
let t = u(e, a[v]);
|
||||
f(t, o[b]), h[b] = t;
|
||||
} else h[b] = f(n, o[b]), u(e, a[v], n), a[t] = null;
|
||||
b++;
|
||||
} else p(a[y]), y--;
|
||||
else p(a[v]), v++;
|
||||
for (; b <= x;) {
|
||||
let t = u(e, h[x + 1]);
|
||||
f(t, o[b]), h[b++] = t;
|
||||
}
|
||||
for (; v <= y;) {
|
||||
let e = a[v++];
|
||||
e !== null && p(e);
|
||||
}
|
||||
return this.ut = s, l(e, h), r;
|
||||
}
|
||||
});
|
||||
})), _ = e((() => {
|
||||
g();
|
||||
})), v = /* @__PURE__ */ t(((e, t) => {
|
||||
t.exports = function() {
|
||||
var e = document.getSelection();
|
||||
if (!e.rangeCount) return function() {};
|
||||
for (var t = document.activeElement, n = [], r = 0; r < e.rangeCount; r++) n.push(e.getRangeAt(r));
|
||||
switch (t.tagName.toUpperCase()) {
|
||||
case "INPUT":
|
||||
case "TEXTAREA":
|
||||
t.blur();
|
||||
break;
|
||||
default:
|
||||
t = null;
|
||||
break;
|
||||
}
|
||||
return e.removeAllRanges(), function() {
|
||||
e.type === "Caret" && e.removeAllRanges(), e.rangeCount || n.forEach(function(t) {
|
||||
e.addRange(t);
|
||||
}), t && t.focus();
|
||||
};
|
||||
};
|
||||
})), y = /* @__PURE__ */ t(((e, t) => {
|
||||
var n = v(), r = {
|
||||
"text/plain": "Text",
|
||||
"text/html": "Url",
|
||||
default: "Text"
|
||||
}, i = "Copy to clipboard: #{key}, Enter";
|
||||
function a(e) {
|
||||
var t = (/mac os x/i.test(navigator.userAgent) ? "⌘" : "Ctrl") + "+C";
|
||||
return e.replace(/#{\s*key\s*}/g, t);
|
||||
}
|
||||
function o(e, t) {
|
||||
var o, s, c, l, u, d, f = !1;
|
||||
t ||= {}, o = t.debug || !1;
|
||||
try {
|
||||
if (c = n(), l = document.createRange(), u = document.getSelection(), d = document.createElement("span"), d.textContent = e, d.ariaHidden = "true", d.style.all = "unset", d.style.position = "fixed", d.style.top = 0, d.style.clip = "rect(0, 0, 0, 0)", d.style.whiteSpace = "pre", d.style.webkitUserSelect = "text", d.style.MozUserSelect = "text", d.style.msUserSelect = "text", d.style.userSelect = "text", d.addEventListener("copy", function(n) {
|
||||
if (n.stopPropagation(), t.format) if (n.preventDefault(), n.clipboardData === void 0) {
|
||||
o && console.warn("unable to use e.clipboardData"), o && console.warn("trying IE specific stuff"), window.clipboardData.clearData();
|
||||
var i = r[t.format] || r.default;
|
||||
window.clipboardData.setData(i, e);
|
||||
} else n.clipboardData.clearData(), n.clipboardData.setData(t.format, e);
|
||||
t.onCopy && (n.preventDefault(), t.onCopy(n.clipboardData));
|
||||
}), document.body.appendChild(d), l.selectNodeContents(d), u.addRange(l), !document.execCommand("copy")) throw Error("copy command was unsuccessful");
|
||||
f = !0;
|
||||
} catch (n) {
|
||||
o && console.error("unable to copy using execCommand: ", n), o && console.warn("trying IE specific stuff");
|
||||
try {
|
||||
window.clipboardData.setData(t.format || "text", e), t.onCopy && t.onCopy(window.clipboardData), f = !0;
|
||||
} catch (n) {
|
||||
o && console.error("unable to copy using clipboardData: ", n), o && console.error("falling back to prompt"), s = a("message" in t ? t.message : i), window.prompt(s, e);
|
||||
}
|
||||
} finally {
|
||||
u && (typeof u.removeRange == "function" ? u.removeRange(l) : u.removeAllRanges()), d && document.body.removeChild(d), c();
|
||||
}
|
||||
return f;
|
||||
}
|
||||
t.exports = o;
|
||||
}));
|
||||
//#endregion
|
||||
export { _ as n, h as r, y as t };
|
||||
@@ -0,0 +1,31 @@
|
||||
import { n as e } from "./chunk-DiqZc92J.js";
|
||||
//#region node_modules/lit-html/directive.js
|
||||
var t, n, r, i = e((() => {
|
||||
t = {
|
||||
ATTRIBUTE: 1,
|
||||
CHILD: 2,
|
||||
PROPERTY: 3,
|
||||
BOOLEAN_ATTRIBUTE: 4,
|
||||
EVENT: 5,
|
||||
ELEMENT: 6
|
||||
}, n = (e) => (...t) => ({
|
||||
_$litDirective$: e,
|
||||
values: t
|
||||
}), r = class {
|
||||
constructor(e) {}
|
||||
get _$AU() {
|
||||
return this._$AM._$AU;
|
||||
}
|
||||
_$AT(e, t, n) {
|
||||
this._$Ct = e, this._$AM = t, this._$Ci = n;
|
||||
}
|
||||
_$AS(e, t) {
|
||||
return this.update(e, t);
|
||||
}
|
||||
update(e, t) {
|
||||
return this.render(...t);
|
||||
}
|
||||
};
|
||||
}));
|
||||
//#endregion
|
||||
export { t as i, r as n, i as r, n as t };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user