From cfef242e9b76ba9caca2fc1871af74af6775538f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 12 Sep 2023 20:43:20 -0400
Subject: dropping link button on same collection makes a pushpin. fixed broken
 undo typing to crate doc in sidebar. fixed min/max scaling for cropped images
 and made annotationOverlays start to use it.   Fixed nested text boxes to
 stopPropagation on pointer down to enable editing of translations in sidebar.
 moved sidebar filters onto doc's filters.  Added metadata filters back to
 sidebar.  Added an -any- option to filtersPanel.  fixed schema view preview
 window,  added buttons and sliders.

---
 src/client/views/FilterPanel.tsx | 20 ++++++--------------
 1 file changed, 6 insertions(+), 14 deletions(-)

(limited to 'src/client/views/FilterPanel.tsx')

diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 0d4f4df5a..a601706ce 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -235,7 +235,7 @@ export class FilterPanel extends React.Component<filterProps> {
     facetValues = (facetHeader: string) => {
         const allCollectionDocs = new Set<Doc>();
         SearchUtil.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
-        const set = new Set<string>([String.fromCharCode(127) + '--undefined--']);
+        const set = new Set<string>([String.fromCharCode(127) + '--undefined--', Doc.FilterAny]);
         if (facetHeader === 'tags')
             allCollectionDocs.forEach(child =>
                 StrListCast(child[facetHeader])
@@ -257,24 +257,16 @@ export class FilterPanel extends React.Component<filterProps> {
     };
 
     render() {
-        // console.log('this is frist one today ' + this._allFacets);
-        this._allFacets.forEach(element => console.log(element));
-        // const options = Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => pair[1].filterable ).map(facet => value: facet, label: facet) //this._allFacets.filter(facet => this.activeFacetHeaders.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet }));
-        // console.log('HEELLLLLL ' + DocumentOptions);
-
-        let filteredOptions: string[] = ['author', 'tags', 'text', 'acl-Guest'];
+        let filteredOptions: string[] = ['author', 'tags', 'text', 'acl-Guest', ...this._allFacets.filter(facet => facet[0] === facet.charAt(0).toUpperCase())];
 
         Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => {
             if (pair[1].filterable) {
                 filteredOptions.push(pair[0]);
-                console.log('THIS IS FILTERABLE ALKDJFIIEII' + filteredOptions);
             }
         });
 
         let options = filteredOptions.map(facet => ({ value: facet, label: facet }));
 
-        //  Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => console.log('this is first piar ' + pair[0] + ' this is second piar ' + pair[1].filterable));
-
         return (
             <div className="filterBox-treeView">
                 <div className="filterBox-select">
@@ -398,13 +390,13 @@ export class FilterPanel extends React.Component<filterProps> {
                         <div>
                             <input
                                 style={{ width: 20, marginLeft: 20 }}
-                                checked={
+                                checked={['check', 'exists'].includes(
                                     StrListCast(this.targetDoc._childFilters)
                                         .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue)
-                                        ?.split(Doc.FilterSep)[2] === 'check'
-                                }
+                                        ?.split(Doc.FilterSep)[2] ?? ''
+                                )}
                                 type={type}
-                                onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')}
+                                onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? (fval === Doc.FilterAny ? 'exists' : 'check') : 'remove'), 'set filter')}
                             />
                             {facetValue}
                         </div>
-- 
cgit v1.2.3-70-g09d2


From 5b6c52fe703757a5d6ad1fccab935fae297189bb Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 13 Sep 2023 12:58:48 -0400
Subject: fixed text filters to be removable, have the right colors, and update
 when selected doc changes.  updated filter dropdown to only show parents with
 filters. fixed colors of link doc preview.

---
 src/client/views/FilterPanel.tsx          | 22 +++++++++++++++-------
 src/client/views/StyleProvider.tsx        |  4 +++-
 src/client/views/nodes/LinkDocPreview.tsx |  4 ++--
 3 files changed, 20 insertions(+), 10 deletions(-)

(limited to 'src/client/views/FilterPanel.tsx')

diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index a601706ce..4af29c70e 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -16,6 +16,7 @@ import './FilterPanel.scss';
 import { FieldView } from './nodes/FieldView';
 import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components';
 import { SettingsManager } from '../util/SettingsManager';
+import { Id } from '../../fields/FieldSymbols';
 
 interface filterProps {
     rootDoc: Doc;
@@ -341,9 +342,13 @@ export class FilterPanel extends React.Component<filterProps> {
                                     <div
                                         className="filterBox-facetHeader-remove"
                                         onClick={action(e => {
-                                            for (var key of this.facetValues(renderInfo.facetHeader)) {
-                                                if (this.mapActiveFiltersToFacets.get(key)) {
-                                                    Doc.setDocFilter(this.targetDoc, renderInfo.facetHeader, key, 'remove');
+                                            if (renderInfo.facetHeader === 'text') {
+                                                Doc.setDocFilter(this.targetDoc, renderInfo.facetHeader, 'match', 'remove');
+                                            } else {
+                                                for (var key of this.facetValues(renderInfo.facetHeader)) {
+                                                    if (this.mapActiveFiltersToFacets.get(key)) {
+                                                        Doc.setDocFilter(this.targetDoc, renderInfo.facetHeader, key, 'remove');
+                                                    }
                                                 }
                                             }
                                             this._selectedFacetHeaders.delete(renderInfo.facetHeader);
@@ -370,15 +375,18 @@ export class FilterPanel extends React.Component<filterProps> {
     }
 
     private displayFacetValueFilterUIs(type: string | undefined, facetHeader: string, renderInfoDomain?: number[] | undefined, renderInfoRange?: number[]): React.ReactNode {
-        switch (type /* renderInfo.type */) {
-            case 'text': // if (this.chosenFacets.get(facetHeader) === 'text')
+        switch (type) {
+            case 'text':
                 return (
                     <input
-                        placeholder={
+                        key={this.targetDoc[Id]}
+                        placeholder={'enter text to match'}
+                        defaultValue={
                             StrListCast(this.targetDoc._childFilters)
                                 .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader)
-                                ?.split(Doc.FilterSep)[1] ?? '-empty-'
+                                ?.split(Doc.FilterSep)[1]
                         }
+                        style={{ color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}
                         onBlur={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')}
                         onKeyDown={e => e.key === 'Enter' && undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')(e)}
                     />
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 775ccd067..96b2ddf70 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -318,7 +318,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                                 "this view inherits filters from one of its parents"}
                             color={SettingsManager.userColor}
                             background={showFilterIcon}
-                            items={[ ...(dashView ? [dashView]: []), ...(props?.docViewPath?.()??[]), ...(props?.DocumentView?[props?.DocumentView?.()]:[])].map(dv => ({
+                            items={[ ...(dashView ? [dashView]: []), ...(props?.docViewPath?.()??[]), ...(props?.DocumentView?[props?.DocumentView?.()]:[])]
+                                    .filter(dv => StrListCast(dv.rootDoc.childFilters).length || StrListCast(dv.rootDoc.childRangeFilters).length)
+                                    .map(dv => ({
                                 text: StrCast(dv.rootDoc.title),
                                 val: dv as any,
                                 style: {color:SettingsManager.userColor, background:SettingsManager.userBackgroundColor},
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 0ee83bc08..198cbe851 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -204,7 +204,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     };
     @computed get previewHeader() {
         return !this._linkDoc || !this._markerTargetDoc || !this._targetDoc || !this._linkSrc ? null : (
-            <div className="linkDocPreview-info">
+            <div className="linkDocPreview-info" style={{ background: SettingsManager.userBackgroundColor }}>
                 <div className="linkDocPreview-buttonBar" style={{ float: 'left' }}>
                     <Tooltip title={<div className="dash-tooltip">Edit Link</div>} placement="top">
                         <div className="linkDocPreview-button" onPointerDown={this.editLink}>
@@ -302,7 +302,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                 className="linkDocPreview"
                 ref={this._linkDocRef}
                 onPointerDown={this.followLinkPointerDown}
-                style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+                style={{ borderColor: SettingsManager.userColor, left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
                 {this.docPreview}
             </div>
         );
-- 
cgit v1.2.3-70-g09d2


From 2b96f355ea7f4aa0e1fcf0dbee8ce6bf6e8f09d4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 18 Sep 2023 14:27:05 -0400
Subject: revised how to do filtering by links.

---
 src/client/documents/Documents.ts        | 28 +++++++---------------------
 src/client/views/FilterPanel.tsx         | 15 +++++++--------
 src/client/views/InkTangentHandles.tsx   |  1 -
 src/client/views/nodes/MapBox/MapBox.tsx |  6 +++---
 src/fields/Doc.ts                        | 25 ++++++++++++++++++++-----
 5 files changed, 37 insertions(+), 38 deletions(-)

(limited to 'src/client/views/FilterPanel.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 055950e6f..254be67b9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -15,7 +15,7 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro
 import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField, YoutubeField } from '../../fields/URLField';
 import { inheritParentAcls, SharingPermissions } from '../../fields/util';
 import { Upload } from '../../server/SharedMediaTypes';
-import { aggregateBounds, OmitKeys, Utils } from '../../Utils';
+import { OmitKeys, Utils } from '../../Utils';
 import { YoutubeBox } from '../apis/youtube/YoutubeBox';
 import { DocServer } from '../DocServer';
 import { Networking } from '../Network';
@@ -1296,20 +1296,6 @@ export namespace DocUtils {
                   for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragsDocFilter.split(Doc.FilterSep)[0])) {
                       const facet = filterFacets[facetKey];
 
-                      let links = true;
-                      const linkedTo = filterFacets['-linkedTo'] && Array.from(Object.keys(filterFacets['-linkedTo']))?.[0];
-                      const linkedToField = filterFacets['-linkedTo']?.[linkedTo];
-                      const allLinks = linkedTo && linkedToField ? LinkManager.Instance.getAllRelatedLinks(d) : [];
-                      // prettier-ignore
-                      if (linkedTo) {
-                          if (allLinks.some(d => linkedTo === Field.toScriptString(DocCast(DocCast(d.link_anchor_1)?.[linkedToField]))) || //
-                           allLinks.some(d => linkedTo === Field.toScriptString(DocCast(DocCast(d.link_anchor_2)?.[linkedToField]))))
-                          {
-                              links = true;
-                          } 
-                          else links = false
-                      }
-
                       // facets that match some value in the field of the document (e.g. some text field)
                       const matches = Object.keys(facet).filter(value => value !== 'cookies' && facet[value] === 'match');
 
@@ -1319,16 +1305,16 @@ export namespace DocUtils {
                       // metadata facets that exist
                       const exists = Object.keys(facet).filter(value => facet[value] === 'exists');
 
-                      // metadata facets that exist
+                      // facets that unset metadata (a hack for making cookies work)
                       const unsets = Object.keys(facet).filter(value => facet[value] === 'unset');
 
-                      // facets that have an x next to them
+                      // facets that specify that a field must not match a specific value
                       const xs = Object.keys(facet).filter(value => facet[value] === 'x');
 
-                      if (!linkedTo && !unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true;
+                      if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true;
                       const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value));
                       const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value));
-                      const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined);
+                      const satisfiesExistsFacets = !exists.length ? true : exists.some(value => (facetKey !== '-linkedTo' ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length));
                       const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined);
                       const satisfiesMatchFacets = !matches.length
                           ? true
@@ -1344,11 +1330,11 @@ export namespace DocUtils {
                             });
                       // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria
                       if (parentCollection?.childFilters_boolean === 'OR') {
-                          if (links && satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
+                          if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
                       }
                       // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria
                       else {
-                          if (!links || !satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
+                          if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
                       }
                   }
                   return parentCollection?.childFilters_boolean === 'OR' ? false : true;
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 4af29c70e..312dc3a70 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -56,7 +56,7 @@ export class FilterPanel extends React.Component<filterProps> {
 
     @computed get _allFacets() {
         // trace();
-        const noviceReqFields = ['author', 'tags', 'text', 'type'];
+        const noviceReqFields = ['author', 'tags', 'text', 'type', '-linkedTo'];
         const noviceLayoutFields: string[] = []; //["_layout_curPage"];
         const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
 
@@ -118,7 +118,7 @@ export class FilterPanel extends React.Component<filterProps> {
     // }
 
     gatherFieldValues(childDocs: Doc[], facetKey: string) {
-        const valueSet = new Set<string>();
+        const valueSet = new Set<string>(StrListCast(this.props.rootDoc.childFilters).map(filter => filter.split(Doc.FilterSep)[1]));
         let rtFields = 0;
         let subDocs = childDocs;
         if (subDocs.length > 0) {
@@ -127,7 +127,6 @@ export class FilterPanel extends React.Component<filterProps> {
                 newarray = [];
                 subDocs.forEach(t => {
                     const facetVal = t[facetKey];
-                    // console.log("facetVal " + facetVal)
                     if (facetVal instanceof RichTextField || typeof facetVal === 'string') rtFields++;
                     facetVal !== undefined && valueSet.add(Field.toString(facetVal as Field));
                     (facetVal === true || facetVal == false) && valueSet.add(Field.toString(!facetVal));
@@ -182,14 +181,14 @@ export class FilterPanel extends React.Component<filterProps> {
                 });
 
                 if (facetHeader === 'text') {
-                    return { facetHeader: facetHeader, renderType: 'text' };
+                    return { facetHeader, renderType: 'text' };
                 } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) {
                     const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
                     const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05)));
                     const ranged = Doc.readDocRangeFilter(this.targetDoc, facetHeader); // not the filter range, but the zooomed in range on the filter
-                    return { facetHeader: facetHeader, renderType: 'range', domain: [extendedMinVal, extendedMaxVal], range: ranged ? ranged : [extendedMinVal, extendedMaxVal] };
+                    return { facetHeader, renderType: 'range', domain: [extendedMinVal, extendedMaxVal], range: ranged ? ranged : [extendedMinVal, extendedMaxVal] };
                 } else {
-                    return { facetHeader: facetHeader, renderType: 'checkbox' };
+                    return { facetHeader, renderType: 'checkbox' };
                 }
             })
         );
@@ -236,7 +235,7 @@ export class FilterPanel extends React.Component<filterProps> {
     facetValues = (facetHeader: string) => {
         const allCollectionDocs = new Set<Doc>();
         SearchUtil.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
-        const set = new Set<string>([String.fromCharCode(127) + '--undefined--', Doc.FilterAny]);
+        const set = new Set<string>([...StrListCast(this.props.rootDoc.childFilters).map(filter => filter.split(Doc.FilterSep)[1]), Doc.FilterNone, Doc.FilterAny]);
         if (facetHeader === 'tags')
             allCollectionDocs.forEach(child =>
                 StrListCast(child[facetHeader])
@@ -404,7 +403,7 @@ export class FilterPanel extends React.Component<filterProps> {
                                         ?.split(Doc.FilterSep)[2] ?? ''
                                 )}
                                 type={type}
-                                onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? (fval === Doc.FilterAny ? 'exists' : 'check') : 'remove'), 'set filter')}
+                                onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')}
                             />
                             {facetValue}
                         </div>
diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx
index 71e0ff63c..65f6a6dfa 100644
--- a/src/client/views/InkTangentHandles.tsx
+++ b/src/client/views/InkTangentHandles.tsx
@@ -12,7 +12,6 @@ import { UndoManager } from '../util/UndoManager';
 import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
 import { InkStrokeProperties } from './InkStrokeProperties';
-import { DocumentView } from './nodes/DocumentView';
 export interface InkHandlesProps {
     inkDoc: Doc;
     inkView: InkingStroke;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 3e22c0d0e..d7469e530 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -350,7 +350,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             // Removes filter
             Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
             Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
-            Doc.setDocFilter(this.rootDoc, '-linkedTo', Field.toString(DocCast(this.selectedPin.mapPin)), 'removeAll');
+            Doc.setDocFilter(this.rootDoc, '-linkedTo', `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
 
             const temp = this.selectedPin;
             if (!this._unmounting) {
@@ -382,7 +382,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
         // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
-        Doc.setDocFilter(this.rootDoc, '-linkedTo', Field.toScriptString(this.selectedPin), 'mapPin' as any);
+        Doc.setDocFilter(this.rootDoc, '-linkedTo', `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');
 
         this.recolorPin(this.selectedPin, 'green');
 
@@ -516,7 +516,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             // Removes filter
             Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
             Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
-            Doc.setDocFilter(this.rootDoc, '-linkedTo', Field.toString(DocCast(this.selectedPin.mapPin)), 'removeAll');
+            Doc.setDocFilter(this.rootDoc, '-linkedTo', `mapPin=${Field.toString(DocCast(this.selectedPin.mapPin))}`, 'remove');
 
             this.removePushpin(this.selectedPin);
         }
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 5af49629d..78a03d782 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1463,18 +1463,32 @@ export namespace Doc {
             const isTransparent = (color: string) => color !== '' && DashColor(color).alpha() !== 1;
             return isTransparent(StrCast(doc[key]));
         }
+        if (key === '-linkedTo') {
+            // links are not a field value, so handled here.  value is an expression of form ([field=]idToDoc("..."))
+            const allLinks = LinkManager.Instance.getAllRelatedLinks(doc);
+            const matchLink = (value: string, anchor: Doc) => {
+                const linkedToExp = value?.split('=');
+                if (linkedToExp.length === 1) return Field.toScriptString(anchor) === value;
+                return Field.toScriptString(DocCast(anchor[linkedToExp[0]])) === linkedToExp[1];
+            };
+            // prettier-ignore
+            return  (value === Doc.FilterNone && !allLinks.length) || 
+                    (value === Doc.FilterAny && !!allLinks.length) ||
+                    (allLinks.some(link => matchLink(value,DocCast(link.link_anchor_1)) || 
+                                           matchLink(value,DocCast(link.link_anchor_2)) ));
+        }
         if (typeof value === 'string') {
             value = value.replace(`,${Utils.noRecursionHack}`, '');
         }
         const fieldVal = doc[key];
         if (Cast(fieldVal, listSpec('string'), []).length) {
-            const vals = Cast(fieldVal, listSpec('string'), []);
+            const vals = StrListCast(fieldVal);
             const docs = vals.some(v => (v as any) instanceof Doc);
             if (docs) return value === Field.toString(fieldVal as Field);
             return vals.some(v => v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
         }
         const fieldStr = Field.toString(fieldVal as Field);
-        return fieldStr.includes(value) || (value === String.fromCharCode(127) + '--undefined--' && fieldVal === undefined); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
+        return fieldStr.includes(value) || (value === Doc.FilterAny && fieldVal !== undefined) || (value === Doc.FilterNone && fieldVal === undefined); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
     }
 
     export function deiconifyView(doc: Doc) {
@@ -1520,18 +1534,19 @@ export namespace Doc {
 
     export const FilterSep = '::';
     export const FilterAny = '--any--';
+    export const FilterNone = '--undefined--';
 
     // filters document in a container collection:
     // all documents with the specified value for the specified key are included/excluded
     // based on the modifiers :"check", "x", undefined
-    export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'removeAll' | 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
+    export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
         if (!container) return;
         const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'childFilters';
         const childFilters = StrListCast(container[filterField]);
         runInAction(() => {
             for (let i = 0; i < childFilters.length; i++) {
                 const fields = childFilters[i].split(FilterSep); // split key:value:modifier
-                if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || modifiers === 'removeAll' || (fields[2] === 'match' && modifiers === 'remove'))) {
+                if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
                     if (fields[2] === modifiers && modifiers && fields[1] === value.toString()) {
                         if (toggle) modifiers = 'remove';
                         else return;
@@ -1543,7 +1558,7 @@ export namespace Doc {
             }
             if (!childFilters.length && modifiers === 'match' && value === undefined) {
                 container[filterField] = undefined;
-            } else if (modifiers !== 'remove' && modifiers !== 'removeAll') {
+            } else if (modifiers !== 'remove') {
                 !append && (childFilters.length = 0);
                 childFilters.push(key + FilterSep + value + FilterSep + modifiers);
                 container[filterField] = new List<string>(childFilters);
-- 
cgit v1.2.3-70-g09d2


From 84aa8806a62e2e957e8281d7d492139e3d8225f2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 18 Sep 2023 14:41:53 -0400
Subject: removed filter checkboxes for list values since they don't work. 
 fixed any/undefined filtering for tags or other list fields.

---
 src/client/views/FilterPanel.tsx | 8 ++++++--
 src/fields/Doc.ts                | 8 ++++++--
 2 files changed, 12 insertions(+), 4 deletions(-)

(limited to 'src/client/views/FilterPanel.tsx')

diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 312dc3a70..69ceb0f65 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -17,6 +17,7 @@ import { FieldView } from './nodes/FieldView';
 import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components';
 import { SettingsManager } from '../util/SettingsManager';
 import { Id } from '../../fields/FieldSymbols';
+import { List } from '../../fields/List';
 
 interface filterProps {
     rootDoc: Doc;
@@ -245,8 +246,11 @@ export class FilterPanel extends React.Component<filterProps> {
         else
             allCollectionDocs.forEach(child => {
                 const fieldVal = child[facetHeader] as Field;
-                set.add(Field.toString(fieldVal));
-                (fieldVal === true || fieldVal === false) && set.add((!fieldVal).toString());
+                if (!(fieldVal instanceof List)) {
+                    // currently we have no good way of filtering based on a field that is a list
+                    set.add(Field.toString(fieldVal));
+                    (fieldVal === true || fieldVal === false) && set.add((!fieldVal).toString());
+                }
             });
         const facetValues = Array.from(set).filter(v => v);
 
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 78a03d782..56b97e42f 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1481,14 +1481,18 @@ export namespace Doc {
             value = value.replace(`,${Utils.noRecursionHack}`, '');
         }
         const fieldVal = doc[key];
+        // prettier-ignore
+        if ((value === Doc.FilterAny && fieldVal !== undefined) || 
+            (value === Doc.FilterNone && fieldVal === undefined)) {
+            return true;
+        }
         if (Cast(fieldVal, listSpec('string'), []).length) {
             const vals = StrListCast(fieldVal);
             const docs = vals.some(v => (v as any) instanceof Doc);
             if (docs) return value === Field.toString(fieldVal as Field);
             return vals.some(v => v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
         }
-        const fieldStr = Field.toString(fieldVal as Field);
-        return fieldStr.includes(value) || (value === Doc.FilterAny && fieldVal !== undefined) || (value === Doc.FilterNone && fieldVal === undefined); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
+        return Field.toString(fieldVal as Field).includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
     }
 
     export function deiconifyView(doc: Doc) {
-- 
cgit v1.2.3-70-g09d2

