@malloydata/malloy-explorer 0.0.278-dev250516210719 → 0.0.282-dev250527225235

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.
package/dist/esm/index.js CHANGED
@@ -17,7 +17,7 @@ import { jsx, Fragment, jsxs } from "react/jsx-runtime";
17
17
  import * as React from "react";
18
18
  import React__default, { useLayoutEffect, useEffect, useMemo, useState, useRef, createElement, useContext, useCallback, createContext, memo, useReducer } from "react";
19
19
  import * as QB from "@malloydata/malloy-query-builder";
20
- import { ASTArrowQueryDefinition, ASTSegmentViewDefinition, ASTRefinementViewDefinition, ASTOrderByViewOperation, ASTGroupByViewOperation, ASTAggregateViewOperation, ASTLimitViewOperation, ASTNestViewOperation, ASTTimeTruncationExpression, ASTWhereViewOperation, ASTHavingViewOperation, ASTArrowViewDefinition } from "@malloydata/malloy-query-builder";
20
+ import { ASTArrowQueryDefinition, ASTArrowViewDefinition, ASTSegmentViewDefinition, ASTNestViewOperation, ASTRefinementViewDefinition, ASTOrderByViewOperation, ASTGroupByViewOperation, ASTAggregateViewOperation, ASTLimitViewOperation, ASTTimeTruncationExpression, ASTFilterWithFilterString, ASTWhereViewOperation, ASTHavingViewOperation, ASTDrillViewOperation } from "@malloydata/malloy-query-builder";
21
21
  import { Tag } from "@malloydata/malloy-tag";
22
22
  import { TemporalFilterExpression, BooleanFilterExpression, NumberFilterExpression, StringFilterExpression } from "@malloydata/malloy-filter";
23
23
  import "@malloydata/render/webcomponent";
@@ -3324,29 +3324,105 @@ function useQueryBuilder(source, query) {
3324
3324
  }
3325
3325
  }, [source, query]);
3326
3326
  }
3327
+ const MalloyQueryFocusContext = /* @__PURE__ */ React.createContext({
3328
+ focusMainView: () => {
3329
+ },
3330
+ isMainViewFocused: true,
3331
+ focusNestView: () => {
3332
+ },
3333
+ isNestViewFocused: () => false,
3334
+ focusedNestView: null
3335
+ });
3336
+ function MalloyQueryFocusProvider({
3337
+ rootQuery,
3338
+ focusedNestViewPath,
3339
+ onFocusedNestViewPathChange,
3340
+ children
3341
+ }) {
3342
+ const focusedNestView = React.useMemo(() => {
3343
+ if (focusedNestViewPath.length === 0) {
3344
+ return null;
3345
+ }
3346
+ if (rootQuery) {
3347
+ const queryDef = rootQuery.definition;
3348
+ if (queryDef instanceof ASTArrowQueryDefinition) {
3349
+ return findNestView(queryDef.view, [...focusedNestViewPath].reverse());
3350
+ }
3351
+ }
3352
+ return null;
3353
+ }, [rootQuery, focusedNestViewPath]);
3354
+ const focusMainView = React.useCallback(() => {
3355
+ onFocusedNestViewPathChange([]);
3356
+ }, [onFocusedNestViewPathChange]);
3357
+ const focusNestView = React.useCallback((path) => {
3358
+ onFocusedNestViewPathChange([...path]);
3359
+ }, [onFocusedNestViewPathChange]);
3360
+ const isNestViewFocused = React.useCallback((path) => {
3361
+ return JSON.stringify(path) === JSON.stringify(focusedNestViewPath);
3362
+ }, [focusedNestViewPath]);
3363
+ const isMainViewFocused = focusedNestViewPath.length === 0;
3364
+ return /* @__PURE__ */ jsx(MalloyQueryFocusContext.Provider, {
3365
+ value: {
3366
+ focusMainView,
3367
+ isMainViewFocused,
3368
+ focusNestView,
3369
+ isNestViewFocused,
3370
+ focusedNestView
3371
+ },
3372
+ children
3373
+ });
3374
+ }
3375
+ function useQueryFocus() {
3376
+ return React.useContext(MalloyQueryFocusContext);
3377
+ }
3378
+ const findNestView = (currentView, remainingPath) => {
3379
+ if (remainingPath.length === 0) {
3380
+ return null;
3381
+ }
3382
+ if (currentView instanceof ASTArrowViewDefinition) {
3383
+ return findNestView(currentView.view, remainingPath);
3384
+ }
3385
+ if (currentView instanceof ASTSegmentViewDefinition) {
3386
+ const currentNestName = remainingPath.pop();
3387
+ const currentNestOperation = currentView.operations.items.find((operation) => operation instanceof ASTNestViewOperation && operation.name === currentNestName);
3388
+ if (currentNestOperation === void 0) {
3389
+ remainingPath.push(currentNestName);
3390
+ return null;
3391
+ } else if (remainingPath.length === 0) {
3392
+ return currentNestOperation.view;
3393
+ } else {
3394
+ return findNestView(currentNestOperation.view.definition, remainingPath);
3395
+ }
3396
+ }
3397
+ if (currentView instanceof ASTRefinementViewDefinition) {
3398
+ return findNestView(currentView.refinement, remainingPath);
3399
+ }
3400
+ return null;
3401
+ };
3327
3402
  function MalloyExplorerProvider({
3328
3403
  source,
3329
3404
  query,
3330
- setQuery,
3405
+ onQueryChange,
3406
+ focusedNestViewPath,
3407
+ onFocusedNestViewPathChange,
3331
3408
  children,
3332
3409
  topValues
3333
3410
  }) {
3334
3411
  const rootQuery = useQueryBuilder(source, query);
3335
- const [currentNestView, setCurrentNestView] = React.useState(null);
3336
- const [currentNestQueryPanel, setCurrentNestQueryPanel] = React.useState(null);
3337
3412
  return /* @__PURE__ */ jsx(TooltipProvider, {
3338
- children: /* @__PURE__ */ jsx(QueryEditorContext.Provider, {
3339
- value: {
3340
- source,
3341
- rootQuery,
3342
- setQuery,
3343
- topValues,
3344
- currentNestQueryPanel,
3345
- onCurrentNestQueryPanelChange: setCurrentNestQueryPanel,
3346
- currentNestView,
3347
- onCurrentNestViewChange: setCurrentNestView
3348
- },
3349
- children
3413
+ children: /* @__PURE__ */ jsx(MalloyQueryFocusProvider, {
3414
+ rootQuery,
3415
+ focusedNestViewPath,
3416
+ onFocusedNestViewPathChange,
3417
+ children: /* @__PURE__ */ jsx(QueryEditorContext.Provider, {
3418
+ value: {
3419
+ source,
3420
+ rootQuery,
3421
+ setQuery: onQueryChange,
3422
+ topValues
3423
+ },
3424
+ children
3425
+ })
3350
3426
  })
3351
3427
  });
3352
3428
  }
@@ -25863,13 +25939,14 @@ function QueryActionBar({
25863
25939
  const {
25864
25940
  rootQuery,
25865
25941
  setQuery,
25866
- source,
25867
- onCurrentNestQueryPanelChange,
25868
- onCurrentNestViewChange
25942
+ source
25869
25943
  } = useContext(QueryEditorContext);
25870
25944
  const {
25871
25945
  onCollapse
25872
25946
  } = useContext(ResizableCollapsiblePanelContext);
25947
+ const {
25948
+ focusMainView
25949
+ } = useQueryFocus();
25873
25950
  const isQueryEmpty = !rootQuery || rootQuery.isEmpty();
25874
25951
  const isRunEnabled = rootQuery == null ? void 0 : rootQuery.isRunnable();
25875
25952
  const onRunQuery = () => {
@@ -25877,10 +25954,6 @@ function QueryActionBar({
25877
25954
  runQuery(source, rootQuery.build());
25878
25955
  }
25879
25956
  };
25880
- const focusMainQueryPanel = () => {
25881
- onCurrentNestViewChange == null ? void 0 : onCurrentNestViewChange(null);
25882
- onCurrentNestQueryPanelChange == null ? void 0 : onCurrentNestQueryPanelChange(null);
25883
- };
25884
25957
  return /* @__PURE__ */ jsxs("div", {
25885
25958
  ...{
25886
25959
  className: "mly78zum5 mly1qughib mly6s0dn4 mlye8ttls"
@@ -25901,7 +25974,7 @@ function QueryActionBar({
25901
25974
  },
25902
25975
  children: [/* @__PURE__ */ jsx(Button, {
25903
25976
  onClick: () => {
25904
- focusMainQueryPanel();
25977
+ focusMainView();
25905
25978
  setQuery == null ? void 0 : setQuery(void 0);
25906
25979
  },
25907
25980
  isDisabled: !rootQuery || (rootQuery == null ? void 0 : rootQuery.isEmpty()),
@@ -32401,7 +32474,9 @@ function getOutputNameToInputNameMap(segment) {
32401
32474
  for (const operation of segment.operations.items) {
32402
32475
  if (operation instanceof ASTGroupByViewOperation || operation instanceof ASTAggregateViewOperation) {
32403
32476
  const reference = operation.field.getReference();
32404
- nameMap.set(operation.name, toFullName(reference.path, reference.name));
32477
+ if (reference) {
32478
+ nameMap.set(operation.name, toFullName(reference.path, reference.name));
32479
+ }
32405
32480
  }
32406
32481
  }
32407
32482
  return nameMap;
@@ -32423,8 +32498,11 @@ function segmentHasFieldInOutputSpace(segment, path, name) {
32423
32498
  const match = segment.operations.items.find((operation) => {
32424
32499
  if (operation instanceof ASTGroupByViewOperation || operation instanceof ASTAggregateViewOperation) {
32425
32500
  const reference = operation.field.getReference();
32426
- const isEqual = areReferencesEqual(path, name, reference.path, reference.name);
32427
- return isEqual;
32501
+ if (reference) {
32502
+ return areReferencesEqual(path, name, reference.path, reference.name);
32503
+ } else {
32504
+ return false;
32505
+ }
32428
32506
  }
32429
32507
  return false;
32430
32508
  });
@@ -33141,11 +33219,12 @@ function SortableOperation({
33141
33219
  operation,
33142
33220
  color
33143
33221
  }) {
33222
+ var _a2;
33144
33223
  const {
33145
33224
  setQuery
33146
33225
  } = useContext(QueryEditorContext);
33147
33226
  const fieldInfo = operation.getFieldInfo();
33148
- const path = operation.field.getReference().path ?? NULL_PATH;
33227
+ const path = ((_a2 = operation.field.getReference()) == null ? void 0 : _a2.path) ?? NULL_PATH;
33149
33228
  const {
33150
33229
  attributes,
33151
33230
  listeners,
@@ -33346,28 +33425,62 @@ const parsedToLabels = (parsed, filterString) => {
33346
33425
  value = stringClause.escaped_values.join(", ");
33347
33426
  break;
33348
33427
  case "=":
33349
- op = "is";
33350
- value = stringClause.values.join(", ");
33428
+ {
33429
+ const {
33430
+ not,
33431
+ values
33432
+ } = stringClause;
33433
+ op = not ? "is not" : "is";
33434
+ value = values.join(", ");
33435
+ }
33351
33436
  break;
33352
33437
  case "contains":
33353
- op = "contains";
33354
- value = stringClause.values.join(", ");
33438
+ {
33439
+ const {
33440
+ not,
33441
+ values
33442
+ } = stringClause;
33443
+ op = not ? "does not contain" : "contains";
33444
+ value = values.join(", ");
33445
+ }
33355
33446
  break;
33356
33447
  case "starts":
33357
- op = "starts with";
33358
- value = stringClause.values.join(", ");
33448
+ {
33449
+ const {
33450
+ not,
33451
+ values
33452
+ } = stringClause;
33453
+ op = not ? "does not start with" : "starts with";
33454
+ value = values.join(", ");
33455
+ }
33359
33456
  break;
33360
33457
  case "ends":
33361
- op = "is like";
33362
- value = stringClause.values.join(", ");
33458
+ {
33459
+ const {
33460
+ not,
33461
+ values
33462
+ } = stringClause;
33463
+ op = not ? "does not end with" : "ends with";
33464
+ value = values.join(", ");
33465
+ }
33363
33466
  break;
33364
33467
  case "empty":
33365
- op = "is empty";
33366
- value = "";
33468
+ {
33469
+ const {
33470
+ not
33471
+ } = stringClause;
33472
+ op = not ? "is not empty" : "is empty";
33473
+ value = "";
33474
+ }
33367
33475
  break;
33368
33476
  case "null":
33369
- op = stringClause.not ? "is not" : "is";
33370
- value = "null";
33477
+ {
33478
+ const {
33479
+ not
33480
+ } = stringClause;
33481
+ op = not ? "is not" : "is";
33482
+ value = "null";
33483
+ }
33371
33484
  break;
33372
33485
  }
33373
33486
  }
@@ -33531,22 +33644,27 @@ function SingleFilterOperation({
33531
33644
  rootQuery,
33532
33645
  filterOperation
33533
33646
  }) {
33647
+ const {
33648
+ setQuery
33649
+ } = useContext(QueryEditorContext);
33650
+ const setFilter = useCallback((filter2) => {
33651
+ if (filterOperation.filter instanceof ASTFilterWithFilterString) {
33652
+ filterOperation.filter.setFilter(filter2);
33653
+ }
33654
+ setQuery == null ? void 0 : setQuery(rootQuery.build());
33655
+ }, [filterOperation.filter, rootQuery, setQuery]);
33656
+ if (!(filterOperation.filter instanceof ASTFilterWithFilterString)) {
33657
+ return null;
33658
+ }
33534
33659
  const {
33535
33660
  fieldReference,
33536
33661
  filterString
33537
33662
  } = filterOperation.filter;
33538
33663
  const filter = filterOperation.filter.getFilter();
33539
33664
  const fieldInfo = fieldReference.getFieldInfo();
33540
- const {
33541
- setQuery
33542
- } = useContext(QueryEditorContext);
33543
33665
  if (fieldInfo.kind !== "dimension" && fieldInfo.kind !== "measure") {
33544
33666
  throw new Error(`Invalid filter field kind: ${fieldInfo.kind}`);
33545
33667
  }
33546
- const setFilter = useCallback((filter2) => {
33547
- filterOperation.filter.setFilter(filter2);
33548
- setQuery == null ? void 0 : setQuery(rootQuery.build());
33549
- }, [filterOperation.filter, rootQuery, setQuery]);
33550
33668
  const {
33551
33669
  op,
33552
33670
  value
@@ -34157,6 +34275,35 @@ const styles$d = {
34157
34275
  $$css: true
34158
34276
  }
34159
34277
  };
34278
+ const NestViewPathContext = /* @__PURE__ */ React__default.createContext([]);
34279
+ function FocusableView({
34280
+ children,
34281
+ nest
34282
+ }) {
34283
+ const {
34284
+ focusNestView,
34285
+ focusMainView
34286
+ } = useQueryFocus();
34287
+ const parentNestViewPath = useContext(NestViewPathContext);
34288
+ return /* @__PURE__ */ jsx("div", {
34289
+ onPointerDown: (e) => {
34290
+ e.stopPropagation();
34291
+ if (nest) {
34292
+ focusNestView([...parentNestViewPath, nest.name]);
34293
+ } else {
34294
+ focusMainView();
34295
+ }
34296
+ },
34297
+ children: nest ? /* @__PURE__ */ jsx(NestViewPathContext.Provider, {
34298
+ value: [...parentNestViewPath, nest.name],
34299
+ children: /* @__PURE__ */ jsx("div", {
34300
+ children
34301
+ })
34302
+ }) : /* @__PURE__ */ jsx("div", {
34303
+ children
34304
+ })
34305
+ });
34306
+ }
34160
34307
  function NestOperations({
34161
34308
  rootQuery,
34162
34309
  view,
@@ -34180,31 +34327,14 @@ function NestOperation({
34180
34327
  nest
34181
34328
  }) {
34182
34329
  const {
34183
- setQuery,
34184
- currentNestQueryPanel,
34185
- onCurrentNestQueryPanelChange,
34186
- onCurrentNestViewChange
34330
+ setQuery
34187
34331
  } = useContext(QueryEditorContext);
34188
34332
  const [renameOpen, setRenameOpen] = useState(false);
34189
- const panelRef = React.useRef(null);
34190
- const isCurrentNestQueryPanelFocused = currentNestQueryPanel !== null && panelRef.current == currentNestQueryPanel;
34191
- React.useEffect(() => {
34192
- if (isCurrentNestQueryPanelFocused) {
34193
- onCurrentNestViewChange == null ? void 0 : onCurrentNestViewChange(nest.view);
34194
- }
34195
- }, [nest, isCurrentNestQueryPanelFocused, onCurrentNestViewChange]);
34196
- const focusCurrentNestQueryPanel = () => {
34197
- onCurrentNestQueryPanelChange == null ? void 0 : onCurrentNestQueryPanelChange(panelRef.current);
34198
- onCurrentNestViewChange == null ? void 0 : onCurrentNestViewChange(nest.view);
34199
- };
34200
- const focusParentQueryPanel = () => {
34201
- const currentPanel = panelRef.current;
34202
- const parent = findParentNestQueryPanel(currentPanel);
34203
- onCurrentNestQueryPanelChange == null ? void 0 : onCurrentNestQueryPanelChange(parent);
34204
- if (parent === null) {
34205
- onCurrentNestViewChange == null ? void 0 : onCurrentNestViewChange(null);
34206
- }
34207
- };
34333
+ const parentNestViewPath = useContext(NestViewPathContext);
34334
+ const {
34335
+ focusNestView,
34336
+ isNestViewFocused
34337
+ } = useQueryFocus();
34208
34338
  const getControls = (nest2) => /* @__PURE__ */ jsxs(Fragment, {
34209
34339
  children: [/* @__PURE__ */ jsxs(DropdownMenu, {
34210
34340
  trigger: /* @__PURE__ */ jsx(Button, {
@@ -34217,7 +34347,7 @@ function NestOperation({
34217
34347
  icon: "clear",
34218
34348
  label: "Delete Query",
34219
34349
  onClick: () => {
34220
- focusParentQueryPanel();
34350
+ focusNestView([...parentNestViewPath]);
34221
34351
  nest2.delete();
34222
34352
  setQuery == null ? void 0 : setQuery(rootQuery.build());
34223
34353
  }
@@ -34232,21 +34362,19 @@ function NestOperation({
34232
34362
  view: nest2.view
34233
34363
  })]
34234
34364
  });
34235
- return /* @__PURE__ */ jsx("div", {
34236
- ...{
34237
- className: "mlyj3b58b mly1yf7rl7 mly1xmf6yo mlyh8yej3"
34238
- },
34365
+ return /* @__PURE__ */ jsx(FocusableView, {
34366
+ nest,
34239
34367
  children: /* @__PURE__ */ jsxs("div", {
34240
- ref: panelRef,
34241
- onPointerDownCapture: focusCurrentNestQueryPanel,
34242
- "data-nest-panel": true,
34368
+ ...{
34369
+ className: "mlyj3b58b mly1yf7rl7 mly1xmf6yo mlyh8yej3"
34370
+ },
34243
34371
  children: [/* @__PURE__ */ jsx(CollapsiblePanel, {
34244
34372
  title: nest.name,
34245
34373
  icon: viewToVisualizationIcon(nest.view),
34246
34374
  defaultOpen: true,
34247
34375
  controls: getControls(nest),
34248
34376
  collapsedControls: getControls(nest),
34249
- isFocused: isCurrentNestQueryPanelFocused,
34377
+ isFocused: isNestViewFocused([...parentNestViewPath, nest.name]),
34250
34378
  children: /* @__PURE__ */ jsx(View, {
34251
34379
  rootQuery,
34252
34380
  view: nest.view
@@ -34258,14 +34386,8 @@ function NestOperation({
34258
34386
  open: renameOpen,
34259
34387
  setOpen: setRenameOpen
34260
34388
  })]
34261
- })
34262
- }, nest.name);
34263
- }
34264
- function findParentNestQueryPanel(element2) {
34265
- if (!element2 || !element2.parentElement) return null;
34266
- const parentElement = element2.parentElement;
34267
- if (parentElement.dataset.nestPanel !== void 0) return parentElement;
34268
- return findParentNestQueryPanel(parentElement);
34389
+ }, nest.name)
34390
+ });
34269
34391
  }
34270
34392
  function Operations({
34271
34393
  rootQuery,
@@ -34295,7 +34417,8 @@ function Operations({
34295
34417
  orderBys.push(operation);
34296
34418
  } else if (operation instanceof ASTNestViewOperation) {
34297
34419
  nests.push(operation);
34298
- } else {
34420
+ } else if (operation instanceof ASTDrillViewOperation) ;
34421
+ else {
34299
34422
  limit = operation;
34300
34423
  }
34301
34424
  });
@@ -34434,19 +34557,13 @@ function Query({
34434
34557
  setQuery
34435
34558
  }) {
34436
34559
  const {
34437
- currentNestQueryPanel,
34438
- onCurrentNestQueryPanelChange,
34439
- onCurrentNestViewChange
34440
- } = React.useContext(QueryEditorContext);
34441
- const focusMainQueryPanel = () => {
34442
- onCurrentNestQueryPanelChange == null ? void 0 : onCurrentNestQueryPanelChange(null);
34443
- onCurrentNestViewChange == null ? void 0 : onCurrentNestViewChange(null);
34444
- };
34445
- return /* @__PURE__ */ jsx("div", {
34446
- onPointerDownCapture: focusMainQueryPanel,
34560
+ focusMainView,
34561
+ isMainViewFocused
34562
+ } = useQueryFocus();
34563
+ return /* @__PURE__ */ jsx(FocusableView, {
34447
34564
  children: /* @__PURE__ */ jsxs(CollapsiblePanel, {
34448
34565
  title: "Main query",
34449
- isFocused: !currentNestQueryPanel,
34566
+ isFocused: isMainViewFocused,
34450
34567
  controls: /* @__PURE__ */ jsxs(Fragment, {
34451
34568
  children: [/* @__PURE__ */ jsx(DropdownMenu, {
34452
34569
  trigger: /* @__PURE__ */ jsx(Button, {
@@ -34460,7 +34577,7 @@ function Query({
34460
34577
  icon: "clear",
34461
34578
  label: "Clear query",
34462
34579
  onClick: () => {
34463
- focusMainQueryPanel();
34580
+ focusMainView();
34464
34581
  setQuery == null ? void 0 : setQuery(void 0);
34465
34582
  },
34466
34583
  disabled: rootQuery.isEmpty()
@@ -36042,10 +36159,12 @@ function FieldTokenWithActions({
36042
36159
  }) {
36043
36160
  const {
36044
36161
  rootQuery,
36045
- setQuery,
36046
- currentNestView
36162
+ setQuery
36047
36163
  } = React__default.useContext(QueryEditorContext);
36048
- const view = currentNestView ?? viewDef;
36164
+ const {
36165
+ focusedNestView
36166
+ } = useQueryFocus();
36167
+ const view = focusedNestView ?? viewDef;
36049
36168
  const {
36050
36169
  groupByDisabledReason,
36051
36170
  aggregateDisabledReason,
@@ -36146,7 +36265,13 @@ function FieldTokenWithActions({
36146
36265
  onTooltipOpenChange: setIsTooltipOpen
36147
36266
  })]
36148
36267
  }) : null,
36149
- onClick: field.kind === "dimension" && !groupByDisabledReason ? () => handleAddOperationAction("groupBy") : field.kind === "measure" && !aggregateDisabledReason ? () => handleAddOperationAction("aggregate") : field.kind === "view" ? () => handleAddView() : void 0,
36268
+ onClick: field.kind === "dimension" && !groupByDisabledReason ? () => handleAddOperationAction("groupBy") : field.kind === "measure" && !aggregateDisabledReason ? () => handleAddOperationAction("aggregate") : field.kind === "view" ? () => {
36269
+ if (rootQuery == null ? void 0 : rootQuery.isEmpty()) {
36270
+ handleSetView();
36271
+ } else {
36272
+ handleAddView();
36273
+ }
36274
+ } : void 0,
36150
36275
  hoverActionsVisible: isFilterPopoverOpen || isTooltipOpen,
36151
36276
  tooltip: /* @__PURE__ */ jsx(FieldHoverCard, {
36152
36277
  field,