Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emit events on API call with silent: false #1264

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ All of the following options are optional.
- `modes`, Object: over ride the default modes with your own. `MapboxDraw.modes` can be used to see the default values. More information on custom modes [can be found here](https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/MODES.md).
- `defaultMode`, String (default: `'simple_select'`): the mode (from `modes`) that user will first land in.
- `userProperties`, boolean (default: `false`): properties of a feature will also be available for styling and prefixed with `user_`, e.g., `['==', 'user_custom_label', 'Example']`
- `silent`, boolean (default: `true`): Whether or not to emit events when calling Draw API methods. If `false`, events will be emitted.

## Modes

Expand Down Expand Up @@ -449,7 +450,7 @@ This event will *not* fire when a feature is created or deleted. To track those
The event data is an object with the following shape:

```js
{
{
features: Array<Feature>, // Array of features that were updated
action: string // Name of the action that triggered the update
}
Expand Down Expand Up @@ -493,7 +494,7 @@ This event is fired just after the current mode stops and just before the next m
The event data is an object with the following shape:

```js
{
{
mode: string // The next mode, i.e. the mode that Draw is changing to
}
```
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mapbox/mapbox-gl-draw",
"version": "1.4.3",
"version": "1.4.4-alpha.db018ed",
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
Expand Down
38 changes: 20 additions & 18 deletions src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,28 @@ const featureTypes = {
};

export default function(ctx, api) {

api.modes = Constants.modes;

// API doesn't emit events by default
const silent = ctx.options.silent !== undefined ? !!ctx.options.silent : true;

api.getFeatureIdsAt = function(point) {
const features = featuresAt.click({ point }, null, ctx);
return features.map(feature => feature.properties.id);
};

api.getSelectedIds = function () {
api.getSelectedIds = function() {
return ctx.store.getSelectedIds();
};

api.getSelected = function () {
api.getSelected = function() {
return {
type: Constants.geojsonTypes.FEATURE_COLLECTION,
features: ctx.store.getSelectedIds().map(id => ctx.store.get(id)).map(feature => feature.toGeoJSON())
};
};

api.getSelectedPoints = function () {
api.getSelectedPoints = function() {
return {
type: Constants.geojsonTypes.FEATURE_COLLECTION,
features: ctx.store.getSelectedCoordinates().map(coordinate => ({
Expand Down Expand Up @@ -72,7 +74,7 @@ export default function(ctx, api) {
return newIds;
};

api.add = function (geojson) {
api.add = function(geojson) {
const featureCollection = JSON.parse(JSON.stringify(normalize(geojson)));

const ids = featureCollection.features.map((feature) => {
Expand All @@ -89,14 +91,14 @@ export default function(ctx, api) {
throw new Error(`Invalid geometry type: ${feature.geometry.type}.`);
}
const internalFeature = new Model(ctx, feature);
ctx.store.add(internalFeature);
ctx.store.add(internalFeature, { silent });
} else {
// If a feature of that id has already been created, and we are swapping it out ...
const internalFeature = ctx.store.get(feature.id);
const originalProperties = internalFeature.properties;
internalFeature.properties = feature.properties;
if (!isEqual(originalProperties, feature.properties)) {
ctx.store.featureChanged(internalFeature.id);
ctx.store.featureChanged(internalFeature.id, { silent });
}
if (!isEqual(internalFeature.getCoordinates(), feature.geometry.coordinates)) {
internalFeature.incomingCoords(feature.geometry.coordinates);
Expand All @@ -110,7 +112,7 @@ export default function(ctx, api) {
};


api.get = function (id) {
api.get = function(id) {
const feature = ctx.store.get(id);
if (feature) {
return feature.toGeoJSON();
Expand All @@ -125,11 +127,11 @@ export default function(ctx, api) {
};

api.delete = function(featureIds) {
ctx.store.delete(featureIds, { silent: true });
ctx.store.delete(featureIds, { silent });
// If we were in direct select mode and our selected feature no longer exists
// (because it was deleted), we need to get out of that mode.
if (api.getMode() === Constants.modes.DIRECT_SELECT && !ctx.store.getSelectedIds().length) {
ctx.events.changeMode(Constants.modes.SIMPLE_SELECT, undefined, { silent: true });
ctx.events.changeMode(Constants.modes.SIMPLE_SELECT, undefined, { silent });
} else {
ctx.store.render();
}
Expand All @@ -138,11 +140,11 @@ export default function(ctx, api) {
};

api.deleteAll = function() {
ctx.store.delete(ctx.store.getAllIds(), { silent: true });
ctx.store.delete(ctx.store.getAllIds(), { silent });
// If we were in direct select mode, now our selected feature no longer exists,
// so escape that mode.
if (api.getMode() === Constants.modes.DIRECT_SELECT) {
ctx.events.changeMode(Constants.modes.SIMPLE_SELECT, undefined, { silent: true });
ctx.events.changeMode(Constants.modes.SIMPLE_SELECT, undefined, { silent });
} else {
ctx.store.render();
}
Expand All @@ -156,7 +158,7 @@ export default function(ctx, api) {
if (stringSetsAreEqual((modeOptions.featureIds || []), ctx.store.getSelectedIds())) return api;
// And if we are changing the selection within simple_select mode, just change the selection,
// instead of stopping and re-starting the mode
ctx.store.setSelected(modeOptions.featureIds, { silent: true });
ctx.store.setSelected(modeOptions.featureIds, { silent });
ctx.store.render();
return api;
}
Expand All @@ -166,7 +168,7 @@ export default function(ctx, api) {
return api;
}

ctx.events.changeMode(mode, modeOptions, { silent: true });
ctx.events.changeMode(mode, modeOptions, { silent });
return api;
};

Expand All @@ -175,22 +177,22 @@ export default function(ctx, api) {
};

api.trash = function() {
ctx.events.trash({ silent: true });
ctx.events.trash({ silent });
return api;
};

api.combineFeatures = function() {
ctx.events.combineFeatures({ silent: true });
ctx.events.combineFeatures({ silent });
return api;
};

api.uncombineFeatures = function() {
ctx.events.uncombineFeatures({ silent: true });
ctx.events.uncombineFeatures({ silent });
return api;
};

api.setFeatureProperty = function(featureId, property, value) {
ctx.store.setFeatureProperty(featureId, property, value);
ctx.store.setFeatureProperty(featureId, property, value, { silent });
return api;
};

Expand Down
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const events = {

export const updateActions = {
MOVE: 'move',
CHANGE_PROPERTIES: 'change_properties',
CHANGE_COORDINATES: 'change_coordinates'
};

Expand Down
4 changes: 2 additions & 2 deletions src/modes/mode_interface_accessors.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ ModeInterface.prototype.deleteFeature = function(id, opts = {}) {
* @name this.addFeature
* @param {DrawFeature} feature - the feature to add
*/
ModeInterface.prototype.addFeature = function(feature) {
return this._ctx.store.add(feature);
ModeInterface.prototype.addFeature = function(feature, opts = {}) {
return this._ctx.store.add(feature, opts);
};

/**
Expand Down
3 changes: 2 additions & 1 deletion src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const defaultOptions = {
styles,
modes,
controls: {},
userProperties: false
userProperties: false,
silent: true
};

const showControls = {
Expand Down
48 changes: 42 additions & 6 deletions src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,36 @@ Store.prototype.setDirty = function() {
* @param {string} featureId
* @return {Store} this
*/
Store.prototype.featureChanged = function(featureId) {
Store.prototype.featureCreated = function(featureId, options = {}) {
this._changedFeatureIds.add(featureId);

const silent = options.silent != null ? options.silent : this.ctx.options.silent;
if (silent !== true) {
const feature = this.get(featureId);
this.ctx.events.fire(Constants.events.CREATE, {
features: [feature.toGeoJSON()]
});
}

return this;
};

/**
* Sets a feature's state to changed.
* @param {string} featureId
* @return {Store} this
*/
Store.prototype.featureChanged = function(featureId, options = {}) {
this._changedFeatureIds.add(featureId);

const silent = options.silent != null ? options.silent : this.ctx.options.silent;
if (silent !== true) {
this.ctx.events.fire(Constants.events.UPDATE, {
action: options.action ? options.action : Constants.updateActions.CHANGE_COORDINATES,
features: [this.get(featureId).toGeoJSON()]
});
}

return this;
};

Expand Down Expand Up @@ -117,13 +145,15 @@ Store.prototype.getAllIds = function() {
/**
* Adds a feature to the store.
* @param {Object} feature
* @param {Object} [options]
* @param {Object} [options.silent] - If `true`, this invocation will not fire an event.
*
* @return {Store} this
*/
Store.prototype.add = function(feature) {
this.featureChanged(feature.id);
Store.prototype.add = function(feature, options = {}) {
this._features[feature.id] = feature;
this._featureIds.add(feature.id);
this.featureCreated(feature.id, {silent: options.silent});
return this;
};

Expand Down Expand Up @@ -312,13 +342,19 @@ Store.prototype.isSelected = function(featureId) {
* @param {string} featureId
* @param {string} property property
* @param {string} property value
* @param {Object} [options]
* @param {Object} [options.silent] - If `true`, this invocation will not fire an event.
*/
Store.prototype.setFeatureProperty = function(featureId, property, value) {
Store.prototype.setFeatureProperty = function(featureId, property, value, options = {}) {
this.get(featureId).setProperty(property, value);
this.featureChanged(featureId);

this.featureChanged(featureId, {
silent: options.silent,
action: Constants.updateActions.CHANGE_PROPERTIES
});
};

function refreshSelectedCoordinates(store, options) {
function refreshSelectedCoordinates(store, options = {}) {
const newSelectedCoordinates = store._selectedCoordinates.filter(point => store._selectedFeatureIds.has(point.feature_id));
if (store._selectedCoordinates.length !== newSelectedCoordinates.length && !options.silent) {
store._emitSelectionChange = true;
Expand Down
4 changes: 2 additions & 2 deletions test/api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,11 @@ test('Draw.set', () => {
'Draw.add called with new collection');
assert.equal(deleteSpy.callCount, 1,
'Draw.delete called');
assert.deepEqual(deleteSpy.getCall(0).args, [[
assert.deepEqual(deleteSpy.getCall(0).args[0], [
pointId,
lineId,
polygonId
]], 'Draw.delete called with old features');
], 'Draw.delete called with old features');

// Then set to another that contains a feature
// with an already-used id
Expand Down
4 changes: 1 addition & 3 deletions test/feature.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ test('Feature#changed', () => {
ctx.store.featureChanged.resetHistory();
feature.changed();
assert.equal(ctx.store.featureChanged.callCount, 1, 'called function on store');
assert.deepEqual(ctx.store.featureChanged.getCall(0).args, [featureGeoJson.id], 'with correct args');


assert.deepEqual(ctx.store.featureChanged.getCall(0).args[0], featureGeoJson.id, 'with correct args');
});

test('Feature#incomingCoords', () => {
Expand Down
Loading