@cosmicds/vue-toolkit 0.1.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +24 -0
  2. package/dist/index.common.js +11331 -10679
  3. package/dist/index.umd.js +11346 -10686
  4. package/dist/index.umd.min.js +6 -4
  5. package/dist/src/components/CreditLogos.vue.d.ts +22 -2
  6. package/dist/src/components/FundingAcknowledgement.vue.d.ts +21 -0
  7. package/dist/src/components/Gallery.vue.d.ts +79 -2
  8. package/dist/src/components/GeolocationButton.vue.d.ts +148 -0
  9. package/dist/src/components/IconButton.vue.d.ts +132 -2
  10. package/dist/src/components/LocationSelector.vue.d.ts +139 -2
  11. package/dist/src/components/WwtHud.vue.d.ts +69 -0
  12. package/dist/src/composables.d.ts +17 -0
  13. package/dist/src/geolocation.d.ts +25 -0
  14. package/dist/src/index.d.ts +10 -7
  15. package/dist/src/keyboard.d.ts +1 -1
  16. package/dist/src/mapbox.d.ts +46 -0
  17. package/dist/src/types.d.ts +122 -0
  18. package/dist/src/utils.d.ts +4 -0
  19. package/dist/src/wwt-utils.d.ts +5 -0
  20. package/dist/tsconfig.tsbuildinfo +1 -1
  21. package/package.json +5 -3
  22. package/src/composables.ts +56 -0
  23. package/src/geolocation.ts +110 -0
  24. package/src/index.ts +21 -14
  25. package/src/keyboard.ts +3 -3
  26. package/src/mapbox.ts +161 -0
  27. package/src/types.ts +149 -0
  28. package/src/utils.ts +33 -0
  29. package/src/wwt-utils.ts +51 -0
  30. package/dist/index.common.js.map +0 -1
  31. package/dist/index.umd.js.map +0 -1
  32. package/dist/index.umd.min.js.map +0 -1
  33. package/dist/src/components/DefaultMiniCredits.vue.d.ts +0 -3
  34. package/dist/src/components/FundingAcknowledgment.vue.d.ts +0 -3
  35. package/dist/src/components/MiniDSBase.d.ts +0 -373
  36. package/dist/src/components/wwtHUD.vue.d.ts +0 -3
package/src/index.ts CHANGED
@@ -1,29 +1,36 @@
1
+ import { BackgroundImageset, skyBackgroundImagesets } from "./background";
2
+ import { useFullscreen, useWindowShape, WindowShape } from "./composables";
3
+ import { PositionCoords, useGeolocation } from "./geolocation";
1
4
  import { KeyboardControlSettings } from "./keyboard";
2
- import MiniDSBase from "./components/MiniDSBase";
5
+ import { useWWTKeyboardControls } from "./wwt-utils";
6
+
3
7
  import IconButton from "./components/IconButton.vue";
4
8
  import LocationSelector from "./components/LocationSelector.vue";
5
- import DefaultMiniCredits from "./components/DefaultMiniCredits.vue";
6
- import FundingAcknowledgment from "./components/FundingAcknowledgment.vue";
9
+ import FundingAcknowledgement from "./components/FundingAcknowledgement.vue";
7
10
  import CreditLogos from "./components/CreditLogos.vue";
8
- import wwtHUD from "./components/wwtHUD.vue";
9
- import { BackgroundImageset, skyBackgroundImagesets } from "./background";
11
+ import GeolocationButton from "./components/GeolocationButton.vue";
12
+ import WwtHud from "./components/WwtHud.vue";
10
13
  import Gallery from "./components/Gallery.vue";
11
- import { API_BASE_URL, MINIDS_BASE_URL, R2D, D2R } from "./utils";
14
+
12
15
 
13
16
  export {
14
17
  BackgroundImageset,
15
18
  KeyboardControlSettings,
16
- MiniDSBase,
17
19
  Gallery,
18
20
  IconButton,
19
21
  LocationSelector,
20
22
  skyBackgroundImagesets,
21
- DefaultMiniCredits,
22
- FundingAcknowledgment,
23
+ useFullscreen,
24
+ useWindowShape,
25
+ WindowShape,
26
+ FundingAcknowledgement,
23
27
  CreditLogos,
24
- wwtHUD,
25
- API_BASE_URL,
26
- MINIDS_BASE_URL,
27
- R2D,
28
- D2R
28
+ GeolocationButton,
29
+ WwtHud,
30
+ PositionCoords,
31
+ useGeolocation,
32
+ useWWTKeyboardControls,
29
33
  };
34
+
35
+ export * from "./types";
36
+ export * from "./utils";
package/src/keyboard.ts CHANGED
@@ -72,10 +72,10 @@ export class KeyboardControlSettings {
72
72
  makeListener(
73
73
  actionName: KeyboardControlSettings["actionTypes"][number],
74
74
  action: () => void
75
- ): (e: KeyboardEvent) => void {
76
- return (e) => {
75
+ ): (event: KeyboardEvent) => void {
76
+ return (event) => {
77
77
  for (const keyPress of this[actionName]) {
78
- if (keyPress.matches(e)) {
78
+ if (keyPress.matches(event)) {
79
79
  action();
80
80
  }
81
81
  }
package/src/mapbox.ts ADDED
@@ -0,0 +1,161 @@
1
+ export interface MapBoxContextItem {
2
+ id: string;
3
+ // eslint-disable-next-line @typescript-eslint/naming-convention
4
+ mapbox_id: string;
5
+ text: string;
6
+ wikidata: string;
7
+ // eslint-disable-next-line @typescript-eslint/naming-convention
8
+ short_code?: string;
9
+ }
10
+
11
+ // The field names here come from MapBox
12
+ export interface MapBoxFeature {
13
+ // eslint-disable-next-line @typescript-eslint/naming-convention
14
+ place_type: string[];
15
+ // eslint-disable-next-line @typescript-eslint/naming-convention
16
+ place_name: string;
17
+ text?: string;
18
+ // eslint-disable-next-line @typescript-eslint/naming-convention
19
+ properties: { short_code: string; };
20
+ center: [number, number];
21
+ context: MapBoxContextItem[];
22
+ }
23
+
24
+ export interface MapBoxFeatureCollection {
25
+ type: "FeatureCollection";
26
+ features: MapBoxFeature[];
27
+ }
28
+
29
+ export type MapBoxFeatureType = "country" | "region" | "postcode" | "district" | "place" | "locality" | "neighborhood" | "street" | "address";
30
+ export type MapBoxWorldviewType = "ar" | "cn" | "in" | "jp" | "ma" | "ru" | "tr" | "us";
31
+
32
+ // For countries, use the ISO 3166-1 alpha-2 country codes:
33
+ // https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
34
+ interface BaseMapBoxGeocodingOptions {
35
+ permanent?: boolean;
36
+ language?: string;
37
+ limit?: number;
38
+ countries?: string[];
39
+ types?: MapBoxFeatureType[];
40
+
41
+ // MapBox API access token
42
+ // eslint-disable-next-line @typescript-eslint/naming-convention
43
+ access_token: string;
44
+ }
45
+
46
+ interface MapBoxForwardGeocodingOptions extends BaseMapBoxGeocodingOptions {
47
+ autocomplete?: boolean;
48
+ bbox?: number;
49
+ format?: "geojson" | "v5";
50
+ proximity?: string;
51
+ }
52
+
53
+ interface MapBoxReverseGeocodingOptions extends BaseMapBoxGeocodingOptions {
54
+ worldview?: MapBoxWorldviewType[];
55
+ }
56
+
57
+ interface MapBoxAdjustedGeocodingParams {
58
+ countries?: string;
59
+ types?: string;
60
+ }
61
+
62
+ type MapBoxQueryOptions<T extends BaseMapBoxGeocodingOptions> = {
63
+ [K in keyof Omit<T, "countries" | "types">]: T[K];
64
+ } & MapBoxAdjustedGeocodingParams;
65
+
66
+ const DEFAULT_RELEVANT_FEATURE_TYPES = ["postcode", "place", "region", "country"];
67
+ const NA_COUNTRIES = ["United States", "Canada", "Mexico"];
68
+ const NA_ABBREVIATIONS = ["US-", "CA-", "MX-"];
69
+
70
+ export function findBestFeature(collection: MapBoxFeatureCollection, relevantTypes?: MapBoxFeatureType[]): MapBoxFeature | null {
71
+ const types = relevantTypes ?? DEFAULT_RELEVANT_FEATURE_TYPES;
72
+ const relevantFeatures = collection.features.filter(feature => types.some(type => feature.place_type.includes(type)));
73
+ const placeFeature = relevantFeatures.find(feature => feature.place_type.includes("place")) ?? (relevantFeatures.find(feature => feature.place_type.includes("postcode")) ?? undefined);
74
+ if (placeFeature !== undefined) {
75
+ return placeFeature;
76
+ }
77
+ const regionFeature = relevantFeatures.find(feature => feature.place_type.includes("region"));
78
+ if (regionFeature !== undefined) {
79
+ return regionFeature;
80
+ }
81
+ const countryFeature = relevantFeatures.find(feature => feature.place_type.includes("country"));
82
+ if (countryFeature !== undefined) {
83
+ return countryFeature;
84
+ }
85
+ return null;
86
+ }
87
+
88
+ export function textForMapboxFeature(feature: MapBoxFeature, relevantTypes?: MapBoxFeatureType[]): string {
89
+ const types = relevantTypes ?? DEFAULT_RELEVANT_FEATURE_TYPES;
90
+ const pieces: string[] = [];
91
+ if (feature.text) {
92
+ pieces.push(feature.text);
93
+ }
94
+ feature.context.forEach(item => {
95
+ const itemType = item.id.split(".")[0];
96
+ if (!types.includes(itemType)) {
97
+ return;
98
+ }
99
+ let text = null as string | null;
100
+ const shortCode = item.short_code;
101
+ if (itemType === "region" && shortCode != null) {
102
+ if (NA_ABBREVIATIONS.some(abbr => shortCode.startsWith(abbr))) {
103
+ text = shortCode.substring(3);
104
+ }
105
+ } else if (itemType === "country") {
106
+ const itemText = item.text;
107
+ if (!NA_COUNTRIES.includes(itemText)) {
108
+ text = itemText;
109
+ }
110
+ }
111
+ if (text !== null) {
112
+ pieces.push(text);
113
+ }
114
+ });
115
+ return pieces.join(", ");
116
+ }
117
+
118
+ export function textForMapboxResults(results: MapBoxFeatureCollection, relevantTypes?: MapBoxFeatureType[]): string {
119
+ const feature = findBestFeature(results, relevantTypes);
120
+ return feature !== null ? textForMapboxFeature(feature) : "";
121
+ }
122
+
123
+ function convertOptionsToQueryParams<T extends BaseMapBoxGeocodingOptions>(options: T): MapBoxQueryOptions<T> {
124
+ const { types, countries, ...params } = options;
125
+ const queryParams = params as MapBoxQueryOptions<T>;
126
+ queryParams.types = (types ?? ["place", "postcode"]).join(",");
127
+ if (countries) {
128
+ queryParams.countries = countries.join(",");
129
+ }
130
+ return queryParams;
131
+ }
132
+
133
+ function searchParams(options: MapBoxForwardGeocodingOptions | MapBoxReverseGeocodingOptions): URLSearchParams {
134
+ const search = new URLSearchParams();
135
+ const queryParams = convertOptionsToQueryParams(options);
136
+ Object.entries(queryParams).forEach(([key, value]) => search.set(key, value.toString()));
137
+ return search;
138
+ }
139
+
140
+ export async function textForLocation(longitudeDeg: number, latitudeDeg: number, options: MapBoxReverseGeocodingOptions): Promise<string> {
141
+ const search = searchParams(options);
142
+ const url = `https://api.mapbox.com/geocoding/v5/mapbox.places/${longitudeDeg},${latitudeDeg}.json?${search.toString()}`;
143
+ return fetch(url)
144
+ .then(response => response.json())
145
+ .then((result: MapBoxFeatureCollection) => {
146
+ if (result.features.length === 0) {
147
+ const ns = latitudeDeg >= 0 ? 'N' : 'S';
148
+ const ew = longitudeDeg >= 0 ? 'E' : 'W';
149
+ const lat = Math.abs(latitudeDeg).toFixed(3);
150
+ const lon = Math.abs(longitudeDeg).toFixed(3);
151
+ return `${lat}° ${ns}, ${lon}° ${ew}`;
152
+ }
153
+ return textForMapboxResults(result);
154
+ });
155
+ }
156
+
157
+ export async function geocodingInfoForSearch(searchText: string, options: MapBoxForwardGeocodingOptions): Promise<MapBoxFeatureCollection> {
158
+ const search = searchParams(options);
159
+ const url = `https://api.mapbox.com/geocoding/v5/mapbox.places/${searchText}.json?${search.toString()}`;
160
+ return fetch(url).then(response => response.json());
161
+ }
package/src/types.ts ADDED
@@ -0,0 +1,149 @@
1
+ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
2
+ import { CircleMarkerOptions, TileLayerOptions } from "leaflet";
3
+ import { engineStore } from "@wwtelescope/engine-pinia";
4
+
5
+ export type WWTEngineStore = ReturnType<typeof engineStore>;
6
+
7
+ /* Funding acknowledgement */
8
+
9
+ export interface FundingAcknowledgementProps {
10
+ color?: string;
11
+ backgroundColor?: string;
12
+ }
13
+
14
+ /* Credit logos */
15
+
16
+ export interface CreditLogosProps {
17
+ visible?: boolean;
18
+ logoSize?: string;
19
+ }
20
+
21
+ /* Gallery */
22
+
23
+ export interface GalleryProps {
24
+ wtmlUrl: string;
25
+ columns?: number | string;
26
+ width?: string;
27
+ maxHeight?: string;
28
+ title?: string;
29
+ selectedColor?: string;
30
+ singleSelect?: boolean;
31
+ highlightLastOnly?: boolean;
32
+ previewIndex?: number;
33
+ closedText?: string;
34
+ }
35
+
36
+ /* Geolocation button */
37
+
38
+ export type GeolocationButtonDensity = null | "default" | "comfortable" | "compact";
39
+
40
+ export interface GeolocationButtonProps {
41
+ color?: string;
42
+ disabled?: boolean;
43
+ size?: string;
44
+ density?: GeolocationButtonDensity;
45
+ elevation?: string;
46
+ hideButton?: boolean;
47
+ showTextLabel?: boolean;
48
+ showCoords?: boolean;
49
+ showTextProgress?: boolean;
50
+ showProgressCircle?: boolean;
51
+ useTextButton?: boolean;
52
+ progressCircleSize?: number;
53
+ label?: string;
54
+ id?: string;
55
+ trueIcon?: string;
56
+ falseIcon?: string;
57
+ backgroundColor?: string;
58
+ showPermissions?: boolean;
59
+ }
60
+
61
+ /* Icon button */
62
+ // We need to do this because FontAwesome doesn't export the prop types
63
+ export type FontAwesomeIconProps = InstanceType<typeof FontAwesomeIcon>["$props"];
64
+ export type SizeType = Extract<FontAwesomeIconProps, 'size'>;
65
+
66
+ export interface IconButtonProps {
67
+ modelValue?: boolean;
68
+ faIcon?: string;
69
+ mdIcon?: string;
70
+ color?: string;
71
+ focusColor?: string;
72
+ backgroundColor?: string;
73
+ boxShadow?: boolean;
74
+ border?: boolean;
75
+ longPressTimeMs?: number;
76
+ tooltipText?: string;
77
+ tooltipLocation?: string;
78
+ tooltipOnClick?: boolean;
79
+ tooltipOnFocus?: boolean;
80
+ tooltipOnHover?: boolean;
81
+ tooltipOffset?: string | number;
82
+ showTooltip?: boolean;
83
+ faSize?: SizeType;
84
+ mdSize?: string;
85
+ }
86
+
87
+
88
+ /* Location selector */
89
+
90
+ export interface LocationDeg {
91
+ longitudeDeg: number;
92
+ latitudeDeg: number;
93
+ }
94
+
95
+ export interface LeafletMapOptions extends TileLayerOptions {
96
+ templateUrl: string;
97
+ initialLocation?: LocationDeg;
98
+ initialZoom?: number;
99
+ }
100
+
101
+ export interface GeoJSONProp {
102
+ url?: string;
103
+ geojson?: GeoJSON.FeatureCollection | GeoJSON.Feature | GeoJSON.GeometryCollection;
104
+ style: CircleMarkerOptions;
105
+ }
106
+
107
+ export interface PlaceDeg extends LocationDeg {
108
+ color?: string;
109
+ fillColor?: string;
110
+ fillOpacity?: number;
111
+ radius?: number;
112
+ name?: string;
113
+ }
114
+
115
+ export interface LocationSelectorProps {
116
+ activatorColor?: string;
117
+ detectLocation?: boolean;
118
+ modelValue?: LocationDeg;
119
+ mapOptions?: LeafletMapOptions;
120
+ initialPlace?: PlaceDeg;
121
+ places?: PlaceDeg[];
122
+ placeCircleOptions?: CircleMarkerOptions;
123
+ placeSelectable?: boolean;
124
+ selectable?: boolean;
125
+ selectedCircleOptions?: CircleMarkerOptions;
126
+ selectionEvent?: "click" | "dblclick";
127
+ worldRadii?: boolean;
128
+ geoJsonFiles?: GeoJSONProp[];
129
+ layers?: L.Layer[];
130
+ }
131
+
132
+ /* WWT HUD */
133
+
134
+ export interface HUDLocation {
135
+ top?: string | number,
136
+ left?: string | number,
137
+ bottom?: string | number,
138
+ right?: string | number,
139
+ }
140
+
141
+ export interface WwtHUDProps {
142
+ location?: HUDLocation;
143
+ offsetCenter?: { x: number; y: number };
144
+ otherVariables?: Object; // eslint-disable-line @typescript-eslint/ban-types
145
+ fontSize?: string;
146
+ backgroundColor?: string | null;
147
+ textShadow?: string | null;
148
+ store: ReturnType<typeof engineStore>;
149
+ }
package/src/utils.ts CHANGED
@@ -3,3 +3,36 @@ export const R2D = 180 / Math.PI;
3
3
 
4
4
  export const API_BASE_URL = "https://api.cosmicds.cfa.harvard.edu";
5
5
  export const MINIDS_BASE_URL = `${API_BASE_URL}/minids`;
6
+
7
+ export function supportsTouchscreen() {
8
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
9
+ // @ts-ignore
10
+ return ('ontouchstart' in window) || ('ontouchstart' in document.documentElement) || !!window.navigator.msPointerEnabled;
11
+ }
12
+
13
+ export function isMobile(userAgent: string): boolean {
14
+ return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent));
15
+ }
16
+
17
+ export function blurActiveElement() {
18
+ const active = document.activeElement;
19
+ if (active instanceof HTMLElement) {
20
+ active.blur();
21
+ }
22
+ }
23
+
24
+ // Modified from https://stackoverflow.com/a/37319954
25
+ export function filterInPlace<T>(array: T[], condition: (t: T) => boolean) {
26
+ let i = 0;
27
+ let j = 0;
28
+ while (i < array.length) {
29
+ const value = array[i];
30
+ if (condition(value)) {
31
+ array[j++] = value;
32
+ i++;
33
+ }
34
+ }
35
+
36
+ array.length = j;
37
+ return array;
38
+ }
@@ -0,0 +1,51 @@
1
+ import { engineStore } from "@wwtelescope/engine-pinia";
2
+ import { KeyboardControlSettings } from "./keyboard";
3
+ type WWTEngineStore = ReturnType<typeof engineStore>;
4
+
5
+ function doZoom(store: WWTEngineStore, zoomIn: boolean) {
6
+ const factor = zoomIn ? 1 / 1.3 : 1.3;
7
+ store.zoom(factor);
8
+ }
9
+
10
+ function doMove(store: WWTEngineStore, x: number, y: number) {
11
+ store.move({ x, y });
12
+ }
13
+
14
+ export function useWWTKeyboardControls(store: WWTEngineStore, element?: Window | HTMLElement): KeyboardControlSettings {
15
+ const root = element || window;
16
+ const kcs = new KeyboardControlSettings({});
17
+ root.addEventListener(
18
+ "keydown",
19
+ kcs.makeListener("zoomIn", () => doZoom(store, true)) as EventListener
20
+ );
21
+ root.addEventListener(
22
+ "keydown",
23
+ kcs.makeListener("zoomOut", () => doZoom(store, false)) as EventListener
24
+ );
25
+ root.addEventListener(
26
+ "keydown",
27
+ kcs.makeListener("moveUp", () =>
28
+ doMove(store, 0, kcs.moveAmount)
29
+ ) as EventListener
30
+ );
31
+ root.addEventListener(
32
+ "keydown",
33
+ kcs.makeListener("moveDown", () =>
34
+ doMove(store, 0, -kcs.moveAmount)
35
+ ) as EventListener
36
+ );
37
+ root.addEventListener(
38
+ "keydown",
39
+ kcs.makeListener("moveLeft", () =>
40
+ doMove(store, kcs.moveAmount, 0)
41
+ ) as EventListener
42
+ );
43
+ root.addEventListener(
44
+ "keydown",
45
+ kcs.makeListener("moveRight", () =>
46
+ doMove(store, -kcs.moveAmount, 0)
47
+ ) as EventListener
48
+ );
49
+
50
+ return kcs;
51
+ }